OTOBANK Engineering Blog

オトバンクはコンテンツが大好きなエンジニアを募集しています!

Symfony2で利用されているDoctrineに入門する(後編)

こんにちは!@mrtryです。
最近、つくりおきした鍋を冷蔵庫に入れ忘れて、おかずを腐らせる失態を2度ほど犯しています...。
みなさん...。最近暖かくなってきていますので、食中毒には気を付けましょう...。

さて、「Symfony2入門」の8回目の記事です。
前回のSymfony2で利用されているDoctrineに入門する(中編)に引き続き、今日は後編をお送りします。

前回は、DoctrineとDBを連携するための設定 エンティティの作成 エンティティを元したテーブル作成 を紹介しました。
今回は、 Doctrineを介したCRUD操作 について紹介したいと思います。

Create

Createする際の手順は以下のようになります。

  1. EntityManagerを準備する
  2. エンティティを新しく生成する
  3. EntityManagerを介して、生成したエンティティをDoctrineの管理下におく
  4. DBに永続化する

以下は、実際のコードの例です。

use AppBundle\Entity\Product;

public function createAction()
{
    // EntityManagerを準備する
    $em = $this->getDoctrine()->getManager();

    // 新しいエンティティを作成し、プロパティを設定する
    $product = new Product();
    $product->setName('Apple');
    $product->setPrice(100);
    $product->setDescription('From Aomori');

    // $productを永続化するエンティティとして管理する
    $em->persist($product);
    // DBへ永続化する
    $em->flush();

    ...
    
}

順に見ていきましょう。

    // EntityManagerを取得する  
    $em = $this->getDoctrine()->getManager();

EntityManagerというものが出てきました。 EntityManagerとは、エンティティの情報をDBへ永続化してくれるものです。 エンティティの情報をDBへ永続化する際は、このEntityManegerを介して行うことになります。

なお、今回はRDBとしてmysqlを用いているので、EntityManagerが返ってきますが、 MongoDBなどのドキュメント指向データベースを用いた際は、DocumentManagerが返ってきます。

    // 新しいエンティティを作成し、プロパティを設定する  
    $product = new Product();  
    $product->setName('Apple');  
    $product->setPrice(100);  
    $product->setDescription('From Aomori');  

前回生成したProductエンティティを新しく生成し、各セッターを用いてプロパティを設定しています。

    // $productを永続化するエンティティとして管理する  
    $em->persist($product);  
    // DBへ永続化する  
    $em->flush();

EntityManegerを介して、DBにエンティティの内容を反映します。 まず、

    $em->persist($product);

で、引数で渡されたエンティティを永続化するエンティティとしてDoctrineで管理します。 persistという名前を見て「これで永続化されるのかな?」と勘違いしてしまうかもしれませんが、 あくまで管理しているだけで、まだDBへの永続化処理は行われていません。

その後、

    $em->flush();

を行うと、それまでにpersist()されていたエンティティがDBへ永続化されます。

Read

Readする際の手順は、以下のようになります。

  1. 読み取りたいエンティティのリポジトリを準備する
  2. リポジトリのfind系メソッドを用いて、エンティティを取得する

以下は、実際のコードの例です。

public function readAction()
{
    // ProductRepositoryを取得
    $productRepository = $this->getDoctrine()->getRepository('AppBundle:Product');
  
    // productテーブルにあるカラム`name`について、`Apple`という文字列に一致するエンティティを取得する
    $product = $productRepository->findByName('Apple');
      
    ...
    
}

順に見ていきましょう。

    // ProductRepositoryを取得  
    $productRepository = $this->getDoctrine()->getRepository('AppBundle:Product');

今度は、ProductRepositoryというものが出てきました。 このリポジトリとは、DBのテーブルをオブジェクトとしたものです。 エンティティの永続化はEntityManagerを介して行いますが、 エンティティを取得するときは、このリポジトリを介して取得します。

    // productテーブルにあるカラム`name`について、`Apple`という文字列に一致するエンティティを取得する  
    $product = $productRepository->findByName('Apple');

$productRepositoryfindByName()を用いて、 productテーブルにあるカラムnameについて、Appleという文字列に一致するエンティティを取得します。

このように、エンティティを取得するは、Repositoryに実装されているfindXXXと名前についたメソッドを用いて取得します。

以下で、その他のfind系メソッドの例を紹介します

参考:Fetching Objects from the Database

// 主キーで検索し、一致するエンティティを取得する (普通はID)
$product = $repository->find($productId);

// 任意のカラム名に基づき、任意の値で検索し、該当するうちの最初のエンティティを取得
$product = $repository->findOneById($productId);
$product = $repository->findOneByName('Keyboard');

// 任意のカラム名に基づき、任意の値を検索し、一致するエンティティをすべて取得する
$products = $repository->findByPrice(19.99);

// すべてのエンティティを取得
$products = $repository->findAll();

Update

Updateする際の手順は、以下のようになります。

  1. リポジトリを介して、更新対象とするエンティティを取得する
  2. 取得したエンティティのプロパティを更新する
  3. DBへ永続化する

以下は、実際のコードの例です。

use AppBundle\Entity\Product;

public function updateAction()
{
    // EntityManagerを取得する
    $em = $this->getDoctrine()->getManager();

    $productId = XXX;

    // productテーブルからIDを指定してエンティティを取得する
    $product = $this->getDoctrine()
      ->getRepository('AppBundle:Product')
      ->find($productId);

    // priceに`100`をセットする
    $product->setPrice(100);

    // DBへ永続化する
    $em->flush();

    ...
    
}

CreateとReadで紹介したメソッドの組み合わせで実現できますが、1箇所だけ違うところがあります。 DBへの永続化の過程で、persist()が省略されています。 persist()はDoctrine側でこのエンティティを管理するよという意味で実行するものでした。 ですが、上記のコードのように、Repositoryを介して取得したエンティティについては、既にDoctrine側の管理対象となっているので、書かなくても良いです。

Notice that calling $em->persist($product) isn't necessary. Recall that this method simply tells Doctrine to manage or "watch" the $product object. In this case, since you fetched the $product object from Doctrine, it's already managed.

Databases and the Doctrine ORM (2.7) | updating-an-object

Delete

Updateする際の手順は、以下のようになります。

  1. リポジトリを介して削除対象とするエンティティを取得する。
  2. EntityManager->remove()を行い、削除するエンティティとして管理する
  3. DBへ永続化する

以下は、実際のコードの例です。

use AppBundle\Entity\Product;

public function deleteAction()
{
    // EntityManagerを取得する
    $em = $this->getDoctrine()->getManager();

    // productテーブルからIDを指定してエンティティを取得する
    $product = $this->getDoctrine()
      ->getRepository('AppBundle:Product')
      ->find($productId);

    //$productを削除するエンティティとして管理する
    $em->remove($product);
    // DBへ永続化する
    $em->flush();
}

エンティティを削除する際は、EntityManagerで用意されているremove()というメソッドを利用します。 remove()の引数となるエンティティは、実行したタイミングでDoctrineで管理されるので、flush()を行うと、DBへ永続化されます。

Doctrine Query Language(DQL)

複数のテーブルを組み合わせたクエリを発行する時、上記の手法だとなかなか難しいこともあります。 今回は詳しく紹介しませんが、DoctrineにはObject Query Language(OQL)として、Doctrine Query Language(DQL)というものも実装されています。

おわりに

今回は、DoctrineでCRUDをする例を紹介しました。
次回は、サービスコンテナについて紹介したいと思います。

また、この記事は、@mrtryの勉強の一環で書いていますので、 お気づきの点などがありましたら、コメント等でご指摘いただければ幸いです!

参考文献