Fork me on GitHub

PHP ORM框架Doctrine使用介绍

doctrine 是什么?

PHP 开发者都知道PHP 如果想要连接到数据库,需要使用到 PDO扩展,PDO 的意义在于它提供了一个面向数据库连接的抽象层,你可以使用相同的函数来连接到不同的数据库。但是 PDO 只是提供了基本的增删改查方法,对于 web 开发来说效率比较低,开发过程繁琐。于是很多第三方框架在 PDO 之上进行了再次封装,这类框架通常被称为 ORM 框架,也即对象关系映射。一般每个 web 框架都会封装自己的 ORM 框架,比如 Laravel 中有 EloquentThinkPHP 也自己封装了一套。而doctrine 也是一个用PHP编写的 ORM 框架,Symfony 框架就直接整合了改组件作为内置的 ORM 框架。

doctrine 有哪些特点?

面向实体(Entry)而不是数据表

在进行数据映射时,大多数ORM 框架采取的都是数据表-Model类一一对应连接,采取的是Active Record模式,核心思想是“面向数据表”,其增删改查是面向数据表的,而 Doctrine 采取的是data mapping模式,核心思想是“面向实体),在数据表的基础之上抽象成实体,是面向实体的增删改查,这里说的实体概念是领域驱动设计中的实体,在这里不详细讨论。

使用DQL查询

由于 Dcotrine 提供的是面向实体的查询,因此它提供了一个类似SQL语法的操作,称之为DQLDoctrine查询语言)。这让我们可以将数据库的数据作为PHP的对象进行操作,而不用关心数据库的细节。这也使得我们可以方便的切换数据库后端引擎。

安装和配置Doctrine

安装

可以使用 Composer 来安装 doctrine,在你的composer.json文件中添加如下

1
2
3
4
5
{
"require": {
"doctrine/orm": "*"
}
}

使用composer install命令来下载安装。
如果你使用的是 Symfony 框架,由于 Symfony 内置 doctrine,无需进行额外的安装步骤。

配置(Symfony 中)

主要配置数据库连接信息,这部分信息通常配置在 app/config/parameters.yml

1
2
3
4
5
6
# app/config/parameters.yml
parameters:
database_host: localhost
database_name: test_project
database_user: root
database_password: password

通常在参数文件parameters.yml 来定义配置。

 app/config/config.yml
doctrine:
    dbal:
        driver:   pdo_mysql
        host:     "%database_host%"
        dbname:   "%database_name%"
        user:     "%database_user%"
        password: "%database_password%"

配置之后,Doctrine 就可以连接到数据库了。

Doctrine使用步骤

1. 定义一个实体(Entry) 类

比如我们需要一个Product类,以及它包含三个属性。

1
2
3
4
5
6
7
8
9
 
class Product
{
private $name;
private $price;
private $description;

//...getter...setter
}

2. 添加映射关系

与其他 ORM 框架不同的是,由于 Doctrine 在实现层面使用的是Data mapping的模式,因此需要一个映射文件,结构如下:

DoctrineMapper文件可以采用三种格式,分别是注解方式,yaml 格式,和 xml 格式,后面我们使用 xml 格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- src/AppBundle/Resources/config/doctrine/Product.orm.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">

<entity name="AppBundle\Entity\Product" table="product">
<id name="id" type="integer">
<generator strategy="AUTO" />
</id>
<field name="name" type="string" length="100" />
<field name="price" type="decimal" scale="2" />
<field name="description" type="text" />
</entity>
</doctrine-mapping>

以上定义了一个entry,包含了一个id字段和三个其他字段。

除此之外,doctrine 提供了一个命令来验证映射是否正确:

1
$  php bin/console doctrine:schema:validate

注意事项:

  1. 格式不能混用,单个实体只能使用单个格式来定义,不能混用。
  2. 数据表表明最好不要使用 SQL关键字,比如groupuser等,因为在数据库引擎中可能导致SQL错误。

3. 使用实体管理器

doctrine 使用EntityManager来统一对数据库进行访问

新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$product = new Product();
$product->setName('Keyboard');
$product->setPrice(19.99);
$product->setDescription('Ergonomic and stylish!');

$em = $this->getDoctrine()->getManager();

// tells Doctrine you want to (eventually) save the Product (no queries yet)
// 告诉Doctrine你希望(最终)存储Product对象(还没有语句执行)
$em->persist($product);

// actually executes the queries (i.e. the INSERT query)
// 真正执行语句(如,INSERT 查询)
$em->flush();

查找

1
2
3
$product = $this->getDoctrine()
->getRepository('AppBundle:Product')
->find($productId);

更新

1
2
3
4
5
6
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('AppBundle:Product')->find($productId);


$product->setName('New product name!');
$em->flush();

删除

1
2
$em->remove($product);
$em->flush();

通过以上实例可以看出,Doctrine通过entityManager管理着Entity,所有的查询,更新操作都是通过entityManager完成的,通过entityManager我们获取到某一特定EntityRepository,通过Repository提供的各种finders来查询Entity

配置关联关系

主要是在mapper文件中配置实体和子实体,值对象的关联关系。

多对一关系
比如多个评论对应同一篇文章:

1
2
3
4
5
6
7
<doctrine-mapping>
<entity name="Comment">
<many-to-one field="article" target-entity="Artical">
<join-column name="article_id" referenced-column-name="id" />
</many-to-one>
</entity>
</doctrine-mapping>

一对一关系
比如产品与物流:

1
2
3
4
5
6
7
8

<doctrine-mapping>
<entity class="Product">
<one-to-one field="shipment" target-entity="Shipment">
<join-column name="shipment_id" referenced-column-name="id" />
</one-to-one>
</entity>
</doctrine-mapping>

一对多关系
比如一个产品有多个 feature

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    <entity name="Product">
<one-to-many field="features" target-entity="Feature" mapped-by="product" />
</entity>
```

**多对多关系**
比如一个用户属于多个组,一个组内包含多个用户

```xml
<doctrine-mapping>
<entity name="User">
<many-to-many field="groups" target-entity="Group">
<join-table name="users_groups">
<join-columns>
<join-column name="user_id" referenced-column-name="id" />
</join-columns>
<inverse-join-columns>
<join-column name="group_id" referenced-column-name="id" />
</inverse-join-columns>
</join-table>
</many-to-many>
</entity>
</doctrine-mapping>

使用查询对象

Criteria 是一个类似于 Laravel 中的query,使用Criteria可以构建多个查询条件。

$criteria = Criteria::create();
$expr = Criteria::expr(); //表达式对象
$criteria->andWhere($expr->eq('id', 3)) //添加 id=3的条件
//获取查询结果
$results = $this->xxxRepository
    ->matching($criteria)
    ->getValues();