建立数据库实体

ORM的本质在于将一个数据库(更确切的说是其中的表格)“转换”到一个PHP对象。于是我们不用类似“select * from …”或者“insert into …”这样的SQL语句来操作表格中的数据,而是改用更直观、也更不容易出错的方式。比如下面这段代码的最终运行效果是在rsywx数据库的book_place中插入了一个新的纪录。

  1. $place = new BookPlace();
  2. $place->setName('Common');
  3. $manager->persist($place);
  4. $manager->flush();

这样的过程也许比mysqli_query($connection, 'insert into …')多了几行代码的输入,但是出错机会少,而且也更安全

导入MySQL数据库

我们的应用开发里程中,数据库已经建立完成(见建立数据库一节)。所以,我们需要将数据库转换到ORM中可以操作的类。

在严格的MVC框架下,这样形成的类是M(odel)层。但由于在本应用中,我们将提供数据这一任务全部放置到API中完成,所以我们导入MySQL数据库形成M层并不是必须的。我们在本教程中还是这么做是为了后面一节样本数据的需要。

进入虚拟机中项目的根目录(/vagrant/symfony),输入如下命令:

php bin/console doctrine:mapping:import AppBundle yml

其中:

  • bin/console是SF的命令行接口,对SF框架应用的操作都要通过这个接口。
  • doctrine:mapping:import表明我们要导入一个数据库。
  • AppBundle表明导入的数据库要为AppBundle这个包所用。
  • yml表明我们要用YAML格式保存导出的数据库信息。
    命令顺利执行后,在symfony\src\AppBundle\Resources\config\doctrine\目录下会多出几个文件,这几个文件一一与数据库中的表格对应。比如BookPlace.orm.yml对应的就是book_place数据库。而它的内容也是如此:
  1. AppBundle\Entity\BookPlace:
  2. type: entity
  3. table: book_place
  4. id:
  5. id:
  6. type: integer
  7. nullable: false
  8. options:
  9. unsigned: false
  10. id: true
  11. generator:
  12. strategy: IDENTITY
  13. fields:
  14. name:
  15. type: string
  16. nullable: false
  17. length: 255
  18. options:
  19. fixed: false
  20. lifecycleCallbacks: { }

如果我们回忆一下book_place的结构,会看到这个yml文件对表格的描述是与该表格的定义完全一致的。

我们暂时不会深入讨论各个字段定义中各个选项的意义。而且在一般情况下,我更喜欢从数据库到类的映射方式。

生成实体类

导入了数据库后,我们执行如下命令来生成实体类:

php bin/console doctrine:generate:entities AppBundle

该命令会在AppBundle目录下生成一个新目录Entity,其中会有若干个PHP文件。这些文件一一与上一步生成的YML文件对应,也因此一一与数据库中的表格对应。比如BookPlace.php文件对应的是BookPlace.orm.yml文件,并进而对应的是book_place这个表格。它的内容如下:

  1. <?php
  2. namespace AppBundle\Entity;
  3. /**
  4. * BookPlace
  5. */
  6. class BookPlace
  7. {
  8. /**
  9. * @var integer
  10. */
  11. private $id;
  12. /**
  13. * @var string
  14. */
  15. private $name;
  16. /**
  17. * Get id
  18. *
  19. * @return integer
  20. */
  21. public function getId()
  22. {
  23. return $this->id;
  24. }
  25. /**
  26. * Set name
  27. *
  28. * @param string $name
  29. *
  30. * @return BookPlace
  31. */
  32. public function setName($name)
  33. {
  34. $this->name = $name;
  35. return $this;
  36. }
  37. /**
  38. * Get name
  39. *
  40. * @return string
  41. */
  42. public function getName()
  43. {
  44. return $this->name;
  45. }
  46. }

这个文件中包含一个命名空间(AppBundle\Entity)的声明和一个类(BookPlace)的声明。而在类的声明中,包括了两部分:

  • 第一部分是成员声明。在BookPlace类中,只有两个成员:一个是$id,一个是$name。它们分别于book_place表格中的两个字段idname对应。
  • 第二部分是方法声明。一般情况下,对于每个成员,都有两个方法,一个是setter,一个是getter。对于只读字段或者应该由数据库引擎自动生成的字段(如本类中表明记录唯一性的id)就只有一个getter。
    从数据库(表格)到ORM映射,再到PHP类声明,我们完成了将数据库加以对象化的步骤。

注意:上面产生的PHP文件是自动生成的。我们对这个文件和其中的类声明不应该做任何的改动。

我们只能修改YML文件然后用doctrine:generate:entities来生成PHP文件,用doctrine:schema:update命令更新数据库;或者直接操作数据库,并在此通过上面讲到的两个步骤来更新ORM和Entity。

ORM表述和Entity类中对表间关系的描述

在结束本小节之前,我们有必要看看ORM表述和Entity类中是怎样描述表之间的关系的。

我们的book_book表格与若干表格有“1对多”的关系。比如一本书的出版社和book_publisher,它的购买地点和book_place都有1对多的关系。

BookBook.orm.yml中,我们可以找到这样一段:

  1. AppBundle\Entity\BookBook:
  2. ... ...
  3. manyToOne:
  4. place:
  5. targetEntity: BookPlace
  6. cascade: { }
  7. fetch: LAZY
  8. mappedBy: null
  9. inversedBy: null
  10. joinColumns:
  11. place:
  12. referencedColumnName: id
  13. orphanRemoval: false
  14. publisher:
  15. targetEntity: BookPublisher
  16. cascade: { }
  17. fetch: LAZY
  18. mappedBy: null
  19. inversedBy: null
  20. joinColumns:
  21. publisher:
  22. referencedColumnName: id
  23. orphanRemoval: false
  24. lifecycleCallbacks: { }

在这里我们可以清楚看到,BookBook是多端,它与两个1端对应。

而在BookBook.php中,我们可以找到这样的代码:

  1. <?php
  2. namespace AppBundle\Entity;
  3. /**
  4. * BookBook
  5. */
  6. class BookBook
  7. {
  8. ... ...
  9. /**
  10. * @var \AppBundle\Entity\BookPlace
  11. */
  12. private $place;
  13. /**
  14. * @var \AppBundle\Entity\BookPublisher
  15. */
  16. private $publisher;
  17. ... ...
  18. /**
  19. * Set place
  20. *
  21. * @param \AppBundle\Entity\BookPlace $place
  22. *
  23. * @return BookBook
  24. */
  25. public function setPlace(\AppBundle\Entity\BookPlace $place = null)
  26. {
  27. $this->place = $place;
  28. return $this;
  29. }
  30. /**
  31. * Get place
  32. *
  33. * @return \AppBundle\Entity\BookPlace
  34. */
  35. public function getPlace()
  36. {
  37. return $this->place;
  38. }
  39. /**
  40. * Set publisher
  41. *
  42. * @param \AppBundle\Entity\BookPublisher $publisher
  43. *
  44. * @return BookBook
  45. */
  46. public function setPublisher(\AppBundle\Entity\BookPublisher $publisher = null)
  47. {
  48. $this->publisher = $publisher;
  49. return $this;
  50. }
  51. /**
  52. * Get publisher
  53. *
  54. * @return \AppBundle\Entity\BookPublisher
  55. */
  56. public function getPublisher()
  57. {
  58. return $this->publisher;
  59. }
  60. }

针对placepublisher这两个字段,在book_book中存放的只是一个ID(一个整数),但是在根据ORM生成的PHP类中,它们以各自PHP类出现(\AppBundle\Entity\BookPlace\AppBundle\Entity\BookPublisher)。对应的,它们的setter和getter也是对这两个类的操作,而不是对id这个字段本身的操作。

在本教程中,我们不再对此进行进一步的展开。