🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 建立数据库实体 ORM的本质在于将一个数据库(更确切的说是其中的表格)“转换”到一个PHP对象。于是我们不用类似“`select * from ...`”或者“`insert into ...`”这样的SQL语句来操作表格中的数据,而是改用更直观、也更不容易出错的方式。比如下面这段代码的最终运行效果是在`rsywx`数据库的`book_place`中插入了一个新的纪录。 ~~~ $place = new BookPlace(); $place->setName('Common'); $manager->persist($place); $manager->flush(); ~~~ 这样的过程也许比`mysqli_query($connection, 'insert into ...')`多了几行代码的输入,但是出错机会少,而且也更安全。 ## 导入MySQL数据库 我们的应用开发里程中,数据库已经建立完成(见[建立数据库](https://taylorr.gitbooks.io/building-a-web-site-with-symfony/content/05.02%20database.html)一节)。所以,我们需要将数据库转换到ORM中可以操作的类。 在严格的MVC框架下,这样形成的类是M(odel)层。但由于在本应用中,我们将提供数据这一任务全部放置到API中完成,所以我们导入MySQL数据库形成M层并不是必须的。我们在本教程中还是这么做是为了后面一节[样本数据](https://taylorr.gitbooks.io/building-a-web-site-with-symfony/content/05.05%20fixture.html)的需要。 进入虚拟机中项目的根目录(`/vagrant/symfony`),输入如下命令: `php bin/console doctrine:mapping:import AppBundle yml` 其中: * `bin/console`是SF的命令行接口,对SF框架应用的操作都要通过这个接口。 * `doctrine:mapping:import`表明我们要导入一个数据库。 * `AppBundle`表明导入的数据库要为`AppBundle`这个包所用。 * `yml`表明我们要用[YAML](http://yaml.org/)格式保存导出的数据库信息。 命令顺利执行后,在`symfony\src\AppBundle\Resources\config\doctrine\`目录下会多出几个文件,这几个文件一一与数据库中的表格对应。比如`BookPlace.orm.yml`对应的就是`book_place`数据库。而它的内容也是如此: ~~~ AppBundle\Entity\BookPlace: type: entity table: book_place id: id: type: integer nullable: false options: unsigned: false id: true generator: strategy: IDENTITY fields: name: type: string nullable: false length: 255 options: fixed: false lifecycleCallbacks: { } ~~~ 如果我们回忆一下`book_place`的结构,会看到这个yml文件对表格的描述是与该表格的定义完全一致的。 我们暂时不会深入讨论各个字段定义中各个选项的意义。而且在一般情况下,我更喜欢从数据库到类的映射方式。 ## 生成实体类 导入了数据库后,我们执行如下命令来生成实体类: `php bin/console doctrine:generate:entities AppBundle` 该命令会在`AppBundle`目录下生成一个新目录`Entity`,其中会有若干个PHP文件。这些文件一一与上一步生成的YML文件对应,也因此一一与数据库中的表格对应。比如`BookPlace.php`文件对应的是`BookPlace.orm.yml`文件,并进而对应的是`book_place`这个表格。它的内容如下: ~~~ <?php namespace AppBundle\Entity; /** * BookPlace */ class BookPlace { /** * @var integer */ private $id; /** * @var string */ private $name; /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * Set name * * @param string $name * * @return BookPlace */ public function setName($name) { $this->name = $name; return $this; } /** * Get name * * @return string */ public function getName() { return $this->name; } } ~~~ 这个文件中包含一个命名空间(`AppBundle\Entity`)的声明和一个类(`BookPlace`)的声明。而在类的声明中,包括了两部分: * 第一部分是成员声明。在`BookPlace`类中,只有两个成员:一个是`$id`,一个是`$name`。它们分别于`book_place`表格中的两个字段`id`和`name`对应。 * 第二部分是方法声明。一般情况下,对于每个成员,都有两个方法,一个是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`中,我们可以找到这样一段: ~~~ AppBundle\Entity\BookBook: ... ... manyToOne: place: targetEntity: BookPlace cascade: { } fetch: LAZY mappedBy: null inversedBy: null joinColumns: place: referencedColumnName: id orphanRemoval: false publisher: targetEntity: BookPublisher cascade: { } fetch: LAZY mappedBy: null inversedBy: null joinColumns: publisher: referencedColumnName: id orphanRemoval: false lifecycleCallbacks: { } ~~~ 在这里我们可以清楚看到,`BookBook`是多端,它与两个1端对应。 而在`BookBook.php`中,我们可以找到这样的代码: ~~~ <?php namespace AppBundle\Entity; /** * BookBook */ class BookBook { ... ... /** * @var \AppBundle\Entity\BookPlace */ private $place; /** * @var \AppBundle\Entity\BookPublisher */ private $publisher; ... ... /** * Set place * * @param \AppBundle\Entity\BookPlace $place * * @return BookBook */ public function setPlace(\AppBundle\Entity\BookPlace $place = null) { $this->place = $place; return $this; } /** * Get place * * @return \AppBundle\Entity\BookPlace */ public function getPlace() { return $this->place; } /** * Set publisher * * @param \AppBundle\Entity\BookPublisher $publisher * * @return BookBook */ public function setPublisher(\AppBundle\Entity\BookPublisher $publisher = null) { $this->publisher = $publisher; return $this; } /** * Get publisher * * @return \AppBundle\Entity\BookPublisher */ public function getPublisher() { return $this->publisher; } } ~~~ 针对`place`和`publisher`这两个字段,在`book_book`中存放的只是一个ID(一个整数),但是在根据ORM生成的PHP类中,它们以各自PHP类出现(`\AppBundle\Entity\BookPlace`和`\AppBundle\Entity\BookPublisher`)。对应的,它们的setter和getter也是对这两个类的操作,而不是对`id`这个字段本身的操作。 在本教程中,我们不再对此进行进一步的展开。