シムノート

PHPフレームワークSymfonyの学習帳

ユーザ用ツール

サイト用ツール


サイドバー

メニュー



このエントリーをはてなブックマークに追加

blog:2015-12-27:doctrine入門_starting_with_the_product

Doctrine入門 : (2) Product から始める

エンティティ

以下の様に、Productエンティティを作成します。普通のPOPO(Plain Old PHP Object)としてクラスを定義します。

<?php

namespace AppBundle\Entity;


class Product
{
    /**
     * @var int
     */
    protected $id;

    /**
     * @var string
     */
    protected $name;

    public function getId()
    {
        return $this->id;
    }

    public function getName()
    {
        return $this->name;
    }

    public function setName($name)
    {
        $this->name = $name;
    }
}

エンティティにはオブジェクトを一意に識別する為のidを持たせます。idは値を自動採番させる項目なので、Setterは作成しません。

Doctrineメタ言語

次にProductにDoctrineのメタ言語を使ってDBとのマッピング情報を定義します。

// ...

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="product")
 */
class Product
{
    /**
     * @ORM\Id
     * @ORM\Column(name="id", type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(name="name", type="string")
     */
    protected $name;

    // ...
}

Doctrineのメタ言語はアノテーションで記述します。
classのアノテーションで、ProductクラスがEntityであること、DBのproductテーブルにマッピングすることを定義しています。 クラスの各プロパティ(id, name)のアノテーションではDBテーブルの対応する項目とデータ型を定義しています。 idはエンティティを一意に識別する為のID(プライマリーキー)であること、自動で値を割り当てることを定義しています。

最初のエンティティが定義が完了しました。consoleコマンドを使って、DBにテーブルを作成します。

$ bin/console doctrine:schema:create

ATTENTION: This operation should not be executed in a production environment.

Creating database schema...
Database schema created successfully!
 

DBの中身を確認してみます。

$ sqlite3 var/data.db3

sqlite> .table
product
sqlite> .schema product
CREATE TABLE product (id INTEGER NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id));
sqlite> 

productテーブルが作成されています。

CRUD

condoleコマンドを使って、ProductのCRUD処理を生成します。

$ bin/console doctrine:generate:crud

The Entity shortcut name: AppBundle:Product [ENTER]
Do you want to generate the "write" actions [no]? yes [ENTER]
Configuration format (yml, xml, php, or annotation) [annotation]: [ENTER]
Routes prefix [/product]: [ENTER]
Do you confirm generation [yes]? [ENTER]

メニューを以下のように修正します。

<nav class="navbar navbar-default">
  <div class="container-fluid">
    <!-- Brand and toggle get grouped for better mobile display -->
    <div class="navbar-header">
      {# ... #}
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li><a href="{{ path('product_index') }}">Product</a></li>
        <li><a href="#">Link</a></li>
      </ul>
      <ul class="nav navbar-nav navbar-right">
        {# ... #}
      </ul>
    </div><!-- /.navbar-collapse -->
  </div><!-- /.container-fluid -->
</nav>

Create Product

ProductController.php内のProductの新規作成箇所を見てみます。

// ...
class ProductController extends Controller
{
    // ...

    /**
     * Creates a new Product entity.
     *
     * @Route("/new", name="product_new")
     * @Method({"GET", "POST"})
     */
    public function newAction(Request $request)
    {
        $product = new Product();                     // Productオブジェクトを作成
        $form = $this->createForm('AppBundle\Form\ProductType', $product);
        $form->handleRequest($request);
        // handleRequest()の中ではProductオブジェクトにPOSTパラメータがセットされ
        // 以下のコードと同様の処理が行われています。
        //
        // $params = $request->request->get('product');
        // $product->setName($params['name']);        // Productオブジェクトに値をセット

        if ($form->isSubmitted() && $form->isValid()) {
            $em = $this->getDoctrine()->getManager(); // DoctrineからEntityManagerを取得
            $em->persist($product);                   // EntityManagerの管理下に新規Productオブジェクトを追加
            $em->flush();                             // DBトランザクション実行

            return $this->redirectToRoute('product_show', array('id' => $product->getId()));
        }

        return $this->render('product/new.html.twig', array(
            'product' => $product,
            'form' => $form->createView(),
        ));
    }
    // ...
}

上記のハイライト箇所ががDoctrine関連の処理です。
新規のProductオブジェクトを作成し、値をセットします。
DBへの保存対象とするにはpersist()を使ってオブジェクトをEntityManagerの管理下に置きます。
flush()でEntityManager管理下のオブジェクトの変更を1つのトランザクションでDBに更新します。
上記の場合、新規のオブジェクト(idが採番されていない)があるので、INSERT文が発行されます。

List Products

ProductController.php内のProduct一覧の表示箇所を見てみます。

// ...
class ProductController extends Controller
{
    // ...

    /**
     * Lists all Product entities.
     *
     * @Route("/", name="product_index")
     * @Method("GET")
     */
    public function indexAction()
    {
        $em = $this->getDoctrine()->getManager();
        $products = $em->getRepository('AppBundle:Product')->findAll();

        return $this->render('product/index.html.twig', array(
            'products' => $products,
        ));
    }
    // ...
}

EntityManagerのgetRepository()はエンティティ毎のfinderオブジェクト(レポジトリ)を作成します。レポジトリはfindAll()等の検索用メソッドを提供します。

以下はfindAll()で検索された$productsを表示しているビューです。

{% extends 'base.html.twig' %}

{% block body %}
    <h1>Product list</h1>

    <table class="table table-striped table-hover table-bordered">
    
        {# ... #}
        
        <tbody>
        {% for product in products %}
            <tr>
                <td><a href="{{ path('product_show', { 'id': product.id }) }}">{{ product.id }}</a></td>
                <td>{{ product.name }}</td>
                <td>
                    {# ... #}
                </td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
    {# ... #}
{% endblock %}

Show Product

ProductController.php内のProductの表示箇所を見てみます。

// ...
class ProductController extends Controller
{
    // ...
    
    /**
     * Finds and displays a Product entity.
     *
     * @Route("/{id}", name="product_show")
     * @Method("GET")
     * 
     * // ここに以下の行が省略されています。
     * // @ParamConverter("product", class="AppBundl:Product")
     */
    public function showAction(Product $product)
    {
        // @ParamConverterの機能を使って、以下と同様の処理が行われています。
        // 
        // $repository = $this->getDoctrine()->getRepository('AppBundle:Product');
        // $product = $repository->find($id);        
    
        $deleteForm = $this->createDeleteForm($product);

        return $this->render('product/show.html.twig', array(
            'product' => $product,
            'delete_form' => $deleteForm->createView(),
        ));
    }    
    // ...
}

consoleコマンドのCURDジェネレータで作成したshowAction()ではSensioFrameworkExtraBundleの@ParamConverterの機能を使って、Productオブジェクトの検索処理が省略されています。

実際にはDoctrineからレポジトリを取得し、find($id)を使ってProductオブジェクトを取得しています。

なお、以下のレポジトリを取得する2つの文は同じ結果となります。

$productRepository = $this->getDoctrine()->getManager()->getRepository('AppBundle:Product');
$productRepository = $this->getDoctrine()->getRepository('AppBundle:Product');

Update Product

ProductController.php内のProductの更新箇所を見てみます。

// ...
class ProductController extends Controller
{
    // ...
    
    /**
     * Displays a form to edit an existing Product entity.
     *
     * @Route("/{id}/edit", name="product_edit")
     * @Method({"GET", "POST"})
     * 
     * // ここに以下の行が省略されています。
     * // @ParamConverter("product", class="AppBundl:Product")
     */
    public function editAction(Request $request, Product $product)
    {
        // @ParamConverterの機能を使って、以下と同様の処理が行われています。
        // 
        // $repository = $this->getDoctrine()->getRepository('AppBundle:Product');
        // $product = $repository->find($id);        

        $deleteForm = $this->createDeleteForm($product);
        $editForm = $this->createForm('AppBundle\Form\ProductType', $product);
        $editForm->handleRequest($request);
        // handleRequest()の中ではProductオブジェクトにPOSTパラメータがセットされ
        // 以下のコードと同様の処理が行われています。
        //
        // $params = $request->request->get('product');
        // $product->setName($params['name']);        // Productオブジェクトに値をセット

        if ($editForm->isSubmitted() && $editForm->isValid()) {
            $em = $this->getDoctrine()->getManager(); // DoctrineからEntityManagerを取得
            $em->persist($product);                   // EntityManagerの管理下にProductオブジェクトを追加
            $em->flush();                             // DBトランザクションを実行

            return $this->redirectToRoute('product_edit', array('id' => $product->getId()));
        }

        return $this->render('product/edit.html.twig', array(
            'product' => $product,
            'edit_form' => $editForm->createView(),
            'delete_form' => $deleteForm->createView(),
        ));
    }
    // ...
}

editAction()showAction()と同様に@ParamConverterの機能を使って、Productオブジェクトの検索処理が省略されています。 レポジトリからfind($id)を使って取得したProductオブジェクトに対して変更を加えて、EntitiyManager経由でDBに更新しています。flush()を実行した際にはDBにUPDATE文が発行されます。

33行目の$em->persist($product);は実は不要です。Doctrineから検索したオブジェクトは既に、EntityManagerの管理下にある為、persist()する必要は在りません。

Delete Product

ProductController.php内のProductの削除箇所を見てみます。

// ...
class ProductController extends Controller
{
    // ...
    
    /**
     * Deletes a Product entity.
     *
     * @Route("/{id}", name="product_delete")
     * @Method("DELETE")
     * 
     * // ここに以下の行が省略されています。
     * // @ParamConverter("product", class="AppBundl:Product")
     */
    public function deleteAction(Request $request, Product $product)
    {
        // @ParamConverterの機能を使って、以下と同様の処理が行われています。
        // 
        // $repository = $this->getDoctrine()->getRepository('AppBundle:Product');
        // $product = $repository->find($id);        

        $form = $this->createDeleteForm($product);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->remove($product);
            $em->flush();
        }

        return $this->redirectToRoute('product_index');
    }
    // ...
}

オブジェクトの削除処理は更新処理とほぼ同じです。違いはEntityManagerに削除対象のオブジェクトを知らせる為に、remove()を使ってます。flash()した時にはDBにDELETE文が発行されます。

動作確認

http://localhost:8000/product にアクセスして、ProductのCRUDを試してみてください。


Comments



98 +12 = ?
blog/2015-12-27/doctrine入門_starting_with_the_product.txt · 最終更新: 2016/01/20 06:49 by tsubo