# 建立数据库实体
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`这个字段本身的操作。
在本教程中,我们不再对此进行进一步的展开。
- 引言
- 1 LAMP
- 1.1 安装虚拟机
- 1.2 安装Vagrant
- 1.3 安装Ubuntu
- 1.4 安装Apache 2
- 1.5 安装PHP
- 1.6 安装MySQL服务器
- 1.7 最后的微调
- 1.8 设置一个虚拟主机
- 1.9 一个趁手的IDE
- 2 Symfony 3和重要构件
- 2.1 Symfony 3
- 2.2 Doctrine
- 2.3 Twig
- 2.4 Composer
- 3 Symfony重要概念
- 3.1 MVC
- 3.2 Bundle/包
- 3.3 Route/路由
- 3.4 Controller/控制器
- 3.5 Entity/实体
- 3.6 Repository/仓库
- 3.7 Template/模板
- 3.8 Test/测试
- 4 藏书管理程序的结构
- 5 创建应用
- 5.1 建立版本管理
- 5.2 建立数据库
- 5.3 应用结构
- 5.4 建立数据库实体
- 5.5 样本数据
- 5.6 路由
- 5.7 模板
- 5.8 开始编写首页
- 5.9 书籍详情页面
- 5.10 书籍列表页面
- 5.11 书籍搜索
- 6 用户和后台
- 7 结语