OTOBANK Engineering Blog

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

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

こんにちわ!@mrtryです。 もう3月ですね。新卒入社して、もうすぐ1年経ちます。 新卒と言えなくなってしまうのが、ちょっと寂しい今日このごろです。

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

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

プロジェクトをつくる

まず、準備として、Symfonyのプロジェクトをつくります。 今回はtry-doctrineという名前で作成します。

$ symfony new try-doctrine 2.8

 Downloading Symfony...

    5.29 MB/5.29 MB ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  100%

 Preparing project...

 ✔  Symfony 2.8.15 was successfully installed. Now you can:

    * Change your current directory to /Users/symmt/Program/Study/try-doctrine

    * Configure your application in app/config/parameters.yml file.

    * Run your application:
        1. Execute the php app/console server:run command.
        2. Browse to the http://localhost:8000 URL.

    * Read the documentation at http://symfony.com/doc

RDBの設定

エンティティを生成する前に、DBの設定を行います。 今回は、RDBとしてmysqlを用いて実例を紹介します。

まず、app/config/parameters.ymlにて、設定を行います。 このファイルは、Symfonyのインフラに関係する設定をするためのファイルです。 各Bundleの設定や、APIのURLなどの定数の管理などに利用されます。

プロジェクトを立ち上げた直後のparameters.ymlにはDBとメールサーバーの設定が記述されています。 今回はdatabase_nameの箇所をtry-doctrineと書き換えたものを利用します。

$ cat -n app/config/parameters.yml
  1 # This file is auto-generated during the composer install
  2 parameters:
  3     database_host: 127.0.0.1
  4     database_port: null
  5     database_name: try-doctrine
  6     database_user: root
  7     database_password: null
  8     mailer_transport: smtp
  9     mailer_host: 127.0.0.1
 10     mailer_user: null
 11     mailer_password: null
 12     secret: 328b4f9d66009015aa9e8770eb73163d0b74684f

設定が終わったら、doctrine:database:createコマンドを実行します。 実行すると、database_nameで設定した名前でDBが新しく作成されます。

$ app/console doctrine:database:create
Created database `try-doctrine` for connection named default

実際に一覧を見てみると、DBが作成されていることが確認できます。

mysql> show databases;
+---------------------------+
| Database                  |
+---------------------------+
| ...                       |
| try-doctrine              |
+---------------------------+
11 rows in set (0.01 sec)

エンティティを生成する

次に、エンティティを生成します。 doctrine:generate:entityというコマンドを利用します。 実行すると対話形式となり、一通り入力が完了すると、エンティティが生成されます。

今回は例として、Productというエンティティを生成したいと思います。 フィールド名等については、以下の表をご確認ください。

フィールド名 null許容 主キー
id id false true
name string(255) false false
price integer false false
description text false false

実際に生成する

上記の表に沿って、エンティティを生成した際の様子です。

$ app/console doctrine:generate:entity


  Welcome to the Doctrine2 entity generator



This command helps you generate Doctrine2 entities.

First, you need to give the entity name you want to generate.
You must use the shortcut notation like AcmeBlogBundle:Post.

The Entity shortcut name: AppBundle:Product

Determine the format to use for the mapping information.

Configuration format (yml, xml, php, or annotation) [annotation]:

Instead of starting with a blank entity, you can add some fields now.
Note that the primary key will be added automatically (named id).

Available types: array, simple_array, json_array, object,
boolean, integer, smallint, bigint, string, text, datetime, datetimetz,
date, time, decimal, float, binary, blob, guid.

New field name (press <return> to stop adding fields): name
Field type [string]:
Field length [255]:
Is nullable [false]:
Unique [false]:

New field name (press <return> to stop adding fields): price
Field type [string]: integer
Is nullable [false]:
Unique [false]:

New field name (press <return> to stop adding fields): description
Field type [string]: text
Is nullable [false]:
Unique [false]:

New field name (press <return> to stop adding fields):


  Entity generation


  created ./src/AppBundle/Entity/Product.php
> Generating entity class src/AppBundle/Entity/Product.php: OK!
> Generating repository class src/AppBundle/Repository/ProductRepository.php: OK!


  Everything is OK! Now get to work :).

各項目について

First, you need to give the entity name you want to generate.
You must use the shortcut notation like AcmeBlogBundle:Post.

The Entity shortcut name: AppBundle:Product

生成するエンティティ名とそれを置くBundle先をBundle名:エンティティ名という書き方で指定します。 今回はAppBundleProductというエンティティを生成するので、AppBundle:Productと書きました。

Determine the format to use for the mapping information.

Configuration format (yml, xml, php, or annotation) [annotation]:

マッピング定義のフォーマットを指定します。 マッピング定義とはアプリケーション内のクラス、プロパティと、DBのテーブル、カラムの対応関係を定義しているものです。 ざっくりなイメージとしては、 ER図の内容を設定ファイルとしたものといった感じです。 デフォルトでは、annotationで設定するようになっています。 今回はannotationでよかったので、そのままReturnで進めました。

Instead of starting with a blank entity, you can add some fields now.
Note that the primary key will be added automatically (named id).

IDの項目はDoctrine側で自動的に生成されます。 今回で言うと、idがそれにあたります。

Available types: array, simple_array, json_array, object,
boolean, integer, smallint, bigint, string, text, datetime, datetimetz,
date, time, decimal, float, binary, blob, guid.

New field name (press <return> to stop adding fields): name
Field type [string]:
Field length [255]:
Is nullable [false]:
Unique [false]:

New field name (press <return> to stop adding fields): price
Field type [string]: integer
Is nullable [false]:
Unique [false]:

New field name (press <return> to stop adding fields): description
Field type [string]: text
Is nullable [false]:
Unique [false]:

フィールド名とその型を設定していきます。 型は、Available typesにて列挙されているものを指定できます。

フィールド名 null許容 ユニークキー制約
name string false false
price integer false false
description text false false

入力が終わったら、New field name (press <return> to stop adding fields)と書いてあるとおり、フィールド名を入力せずReturnを押すと、次に進みます。

  Entity generation


  created ./src/AppBundle/Entity/Product.php
> Generating entity class src/AppBundle/Entity/Product.php: OK!
> Generating repository class src/AppBundle/Repository/ProductRepository.php: OK!


  Everything is OK! Now get to work :).

ここまで終えると、エンティティとリポジトリが生成されます。 生成されたエンティティは以下のようになります。 先程設定していたフィールドがプロパティとして書かれ、プロパティの上部には設定した型などの情報がannotationで書かれていることが確認できます。 (突然出てきたリポジトリですが、今回は置いときます)

$ cat -n src/AppBundle/Entity/Product.php
  1  <?php
  2
  3  namespace AppBundle\Entity;
  4
  5  use Doctrine\ORM\Mapping as ORM;
  6
  7  /**
  8   * Product
  9   *
 10   * @ORM\Table(name="product")
 11   * @ORM\Entity(repositoryClass="AppBundle\Repository\ProductRepository")
 12   */
 13  class Product
 14  {
 15      /**
 16       * @var int
 17       *
 18       * @ORM\Column(name="id", type="integer")
 19       * @ORM\Id
 20       * @ORM\GeneratedValue(strategy="AUTO")
 21       */
 22      private $id;
 23
 24      /**
 25       * @var string
 26       *
 27       * @ORM\Column(name="name", type="string", length=255)
 28       */
 29      private $name;
 30
 31      /**
 32       * @var int
 33       *
 34       * @ORM\Column(name="price", type="integer")
 35       */
 36      private $price;
 37
 38      /**
 39       * @var string
 40       *
 41       * @ORM\Column(name="description", type="text")
 42       */
 43      private $description;
 44
 45
 46      /**
 47       * Get id
 48       *
 49       * @return integer
 50       */
 51      public function getId()
 52      {
 53          return $this->id;
 54      }
 55
 56      /**
 57       * Set name
 58       *
 59       * @param string $name
 60       * @return Product
 61       */
 62      public function setName($name)
 63      {
 64          $this->name = $name;
 65
 66          return $this;
 67      }
 68
 69      /**
 70       * Get name
 71       *
 72       * @return string
 73       */
 74      public function getName()
 75      {
 76          return $this->name;
 77      }
 78
 79      /**
 80       * Set price
 81       *
 82       * @param integer $price
 83       * @return Product
 84       */
 85      public function setPrice($price)
 86      {
 87          $this->price = $price;
 88
 89          return $this;
 90      }
 91
 92      /**
 93       * Get price
 94       *
 95       * @return integer
 96       */
 97      public function getPrice()
 98      {
 99          return $this->price;
100      }
101
102      /**
103       * Set description
104       *
105       * @param string $description
106       * @return Product
107       */
108      public function setDescription($description)
109      {
110          $this->description = $description;
111
112          return $this;
113      }
114
115      /**
116       * Get description
117       *
118       * @return string
119       */
120      public function getDescription()
121      {
122          return $this->description;
123      }
124  }

生成したエンティティをDBに反映する

Productエンティティを作成しましたが、それに対応するproductテーブルはまだDBに存在していません。 Productエンティティに対応するproductテーブルを作成する必要があります。

既に作成されているエンティティを元にして、DBのスキーマを更新するというdoctrine:schema:updateコマンドがあります。 これを実行して、Productエンティティに対応するproductテーブルを生成します。

app/console doctrine:schema:update
ATTENTION: This operation should not be executed in a production environment.
           Use the incremental update to detect changes during development and use
           the SQL DDL provided to manually update your database in production.

The Schema-Tool would execute "1" queries to update the database.
Please run the operation by passing one - or both - of the following options:
    doctrine:schema:update --force to execute the command
    doctrine:schema:update --dump-sql to dump the SQL statements to the screen

doctrine:schema:update --dump-sql を実行すると、実際に実行されるSQLを確認することができます。 doctrine:schema:update --force を実行すると、--dump-sqlで確認したSQLが実行されます。

実際にやっていきます。 try-doctrineに存在するテーブルを確認します。 まだ何も作成していないので、Emptyになっています。

mysql> show tables;
Empty set (0.00 sec)

doctrine:schema:update --dump-sqlを実行します。 先程生成したエンティティと同等の内容のテーブルを生成するSQLが発行されています。 doctrine:schema:update --forceを実行すると、このSQLが実行されることになります。

$ app/console doctrine:schema:update --dump-sql
CREATE TABLE product (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, price INT NOT NULL, description LONGTEXT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;

実際に実行されるSQLを確認したところで、doctrine:schema:update --forceを実行して、テーブルを作成します。

app/console doctrine:schema:update --force
Updating database schema...
Database schema updated successfully! "1" queries were executed

try-doctrineに存在するテーブルを確認します。 productテーブルが新規に作成され、フィールド名等の設定もエンティティの設定と同等なものになっています。

mysql> show tables;
+------------------------+
| Tables_in_try-doctrine |
+------------------------+
| product                |
+------------------------+
1 row in set (0.00 sec)

mysql> show columns from product;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| name        | varchar(255) | NO   |     | NULL    |                |
| price       | int(11)      | NO   |     | NULL    |                |
| description | longtext     | NO   |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

これで、PHPでDoctrineを介してProductエンティティを操作することで、DBを操作する準備が整いました。

おわりに

今回は DoctrineとDBを連携するための設定 エンティティの作成 エンティティを元したテーブル作成 を紹介しました。 次回は、DoctrineでCRUDをする具体例を紹介したいと思います。

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

参考