doctrine2究竟是个什么玩意

本文非原创,转载自:http://www.cnblogs.com/yjf512/p/3375614.htmlphp

感谢原做者分享!html

 

以前和最近一个项目用到了Doctrine,因为是别人搭建的,本身没有很了解,最近又开始作的时候发现拙荆见肘,因而看了一下doctrine教程,本文就是加上本身理解的doctrine教程文档笔记了。mysql

Doctrine2 配置需求

须要php5.3.3及以上git

可使用composer安装github

什么是Doctrine?

Doctrine是一个ORM(Object-relational mapper),提供php数据库和PHP对象的映射。他和其余的ORM同样都是为了保证持久层和逻辑层的分类而存在的。redis

什么是Entity

Entity是PHP的一个对象sql

Entity对应的表须要有主键mongodb

Entity中不能含有final属性或者final方法数据库

教程:

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.htmlbootstrap

教程代码的github地址在:

https://github.com/doctrine/doctrine2-orm-tutorial

使用composer安装

Image(17)[4]

Image(18)[4]

doctrine是能够根据Entity代码来生成数据表的

在src文件夹(config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src"), // config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/src"), // isDevMode);)

中写出Entity代码

而后使用

?
1
php vendor/bin/doctrine orm:schema-tool:create

工具来生成数据表

用下面的命令更新数据表

?
1
$ php vendor/bin/doctrine orm:schema-tool:update --force --dump-sql

固然在bootstrap中须要设置链接mysql数据库

?
1
2
3
4
5
6
7
8
9
10
11
12
// database configuration parameters
$conn = array (
     'driver' => 'pdo_mysql' ,
     'host' => 'localhost' ,
     'user' => 'yjf' ,
     'password' => 'yjf' ,
     'dbname' => 'yjf' ,
     'path' => __DIR__ . '/db.sql' ,
);
 
// obtaining the entity manager
$entityManager = EntityManager::create( $conn , $config );

其中Entity除了可使用代码来进行设置外,也可使用xml和yml文件进行设置。

它的命令行读取的是cli-config.php这个配置数据,因此在使用doctrine的命令行以前,须要先编写这个配置文件。

使用EntityManager能对Entity进行增删改查的操做

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
增长:
$product = new Product();
$product ->setName( $newProductName );
 
$entityManager ->persist( $product );
$entityManager -> flush ();
 
查询:
$entityManager ->find( 'Product' , $id )
 
更新:
$product = $entityManager ->find( 'Product' , $id );
$product ->setName( $newName );
$entityManager -> flush ();
 
删除:
$product = $entityManager ->find( 'Product' , $id );
$product ->remove();
$entityManager -> flush ();

如何调试

doctrine因为EntityManager结构复杂,因此使用var_dump()返回的数据及其庞大,而且可读性差。应该使用

Doctrine\Common\Util\Debug::dump()来打印信息。

表之间的关联如何体如今Entity上

首先明确表和表的关联有几种:

一对一

一对多

多对一

多对多

好比教程中举的例子,bug系统

bug表和user表分别存储bug信息和user信息

每一个bug有个工程师engineer的属性,表明这个bug是由哪一个工程师开发的,那么就有可能有多个bug是由一个工程师开发的。因此bug表对于user表就是多对一的关系。user表对于bug表就是一对多的关系。

bug表和product表分别存储bug信息和出bug的产品信息

一个bug可能有多个产品一块儿出现问题致使的,而一个产品也有可能有多个bug,因此bug表和product表就是多对多的关系。

一对一的关系就比较简单了。

一对多如何设置

这里主要说下一对多和多对一的时候,在bug的Entity中和user的Entity中应该对应这样设置:

?
1
2
3
4
5
6
7
8
9
10
11
12
#bug.php
     /**
      * @ManyToOne(targetEntity="User", inversedBy="assignedBugs")
      **/
     protected $engineer ;
 
#user.php
     /**
      * @OneToMany(targetEntity="Bug", mappedBy="engineer")
      * @var Bug[]
      **/
     protected $assignedBugs = null;

这里ManyToOne和OneToMany是互相对应的,inversedBy和mappedBy也是互相对应的。

这样若是使用doctrine自动生成表结构:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mysql> show create table users;
 
| users | CREATE TABLE `users` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
 
 
mysql> show create table bugs;
 
| bugs  | CREATE TABLE `bugs` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `engineer_id` int(11) DEFAULT NULL,
   `reporter_id` int(11) DEFAULT NULL,
   `description` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
   `created` datetime NOT NULL,
   `status` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
   PRIMARY KEY (`id`),
   KEY `IDX_1E197C9F8D8CDF1` (`engineer_id`),
   KEY `IDX_1E197C9E1CFE6F5` (`reporter_id`),
   CONSTRAINT `FK_1E197C9E1CFE6F5` FOREIGN KEY (`reporter_id`) REFERENCES `users` (`id`),
   CONSTRAINT `FK_1E197C9F8D8CDF1` FOREIGN KEY (`engineer_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

能够看出的是bugs生成了engineer_id属性,而后自动生成外键的索引。

更多的entity和mysql的对应关系是:

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html

如何使用DQL进行查询

对,你没有看错,这里是DQL而不是SQL。DQL是毛语言呢?Document Query Language,意思就是文档化的sql语句,为何sql语句须要文档化呢?sql语句更倾向于表结构实现,因此在写sql语句的时候头脑中须要具现化的是表结构,而ORM的目的就是不须要开发者关注表结构,因此须要一个不基于表结构的查询语句,又能直接翻译成为SQL语句,这就是DQL。DQL能够直接对Entity进行增删改查,而不须要直接对表进行操做。

好比下面的一个例子:

$dql = "SELECT b, e, r FROM Bug b JOIN b.engineer e JOIN b.reporter r ORDER BY b.created DESC";

$query = $entityManager->createQuery($dql);
$query->setMaxResults(30); 
$bugs=$query->getResult();

看这里的sql中From的就是“Bug”,这个是Entity的类,其实熟悉了sql,dql的查询语法也是如出一辙的。

考虑使用dql而不是sql除了和ORM目标一致外,还有一个好处,就是存储层和逻辑层的耦合分开了。好比个人存储层要从mysql换成mongodb,那么逻辑层是什么都不须要动的。

使用Repository

到这里就嘀咕,我常常进行的查询是根据字段查询列表啥的,难道每次须要我都拼接dql么?固然不用,doctrine为咱们准备了repository的概念,就是查询库,里面封装了各类查询经常使用的方法。

使用以下:

<?php
$product = $entityManager->getRepository('Product')
                         ->findOneBy(array('name' => $productName));

Repository对应的操做有:

?
1
2
3
4
5
find()
findAll()
findBy()
findOneBy()
findOneByXXX()

示例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php // $em instanceof EntityManager
$user = $em ->getRepository( 'MyProject\Domain\User' )->find( $id );
 
<?php // $em instanceof EntityManager
 
// All users that are 20 years old
$users = $em ->getRepository( 'MyProject\Domain\User' )->findBy( array ( 'age' => 20));
 
// All users that are 20 years old and have a surname of 'Miller'
$users = $em ->getRepository( 'MyProject\Domain\User' )->findBy( array ( 'age' => 20, 'surname' => 'Miller' ));
 
// A single user by its nickname
$user = $em ->getRepository( 'MyProject\Domain\User' )->findOneBy( array ( 'nickname' => 'romanb' ));
 
 
<?php // A single user by its nickname
$user = $em ->getRepository( 'MyProject\Domain\User' )->findOneBy( array ( 'nickname' => 'romanb' ));
 
// A single user by its nickname (__call magic)
$user = $em ->getRepository( 'MyProject\Domain\User' )->findOneByNickname( 'romanb' );

 

doctrine还容许本身建立Repository,而后只须要在Entity中说明下repositoryClass就能够了。

?
1
2
3
4
5
6
7
class UserRepository extends EntityRepository
{
     public function getAllAdminUsers()
     {
         return $this ->_em->createQuery( 'SELECT u FROM MyDomain\Model\User u WHERE u.status = "admin"' )
                          ->getResult();
     }}

如何使用原生的sql语句来作查询?

除了dql以外,doctrine也容许使用原生的sql语句来作查询。这篇教程有最详细的说明http://docs.doctrine-project.org/en/latest/reference/native-sql.html

主要是提供了createNativeQuery的方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// Equivalent DQL query: "select u from User u where u.name=?1"
// User owns an association to an Address but the Address is not loaded in the query.
$rsm = new ResultSetMapping;
$rsm ->addEntityResult( 'User' , 'u' );
$rsm ->addFieldResult( 'u' , 'id' , 'id' );
$rsm ->addFieldResult( 'u' , 'name' , 'name' );
$rsm ->addMetaResult( 'u' , 'address_id' , 'address_id' );
 
$query = $this ->_em->createNativeQuery( 'SELECT id, name, address_id FROM users WHERE name = ?' , $rsm );
$query ->setParameter(1, 'romanb' );
 
$users = $query ->getResult();

 

这里惟一让人不解的就是ResultSetMapping了,ResultSetMapping也是理解原生sql查询的关键。

其实也没什么不解的了,ORM是不容许数据库操做返回的不是Object的,因此ResultSetMapping就是数据库数据和Object的结构映射。

这个Mapping也能够在Entity中进行设置。

如何使用QueryBuilder

QueryBuilder是doctrine提供的一种在DQL之上的一层查询操做,它封装了一些api,提供给用户进行组装DQL的。

QueryBuilder的好处就是看起来不用本身字符串拼装查询语句了。

关于QueryBuilder的详细说明能够看这篇文章:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html

?
1
2
3
4
5
6
7
8
<?php
// $qb instanceof QueryBuilder
$qb = $em ->createQueryBuilder();
 
$qb ->select( 'u' )
    ->from( 'User' , 'u' )
    ->where( 'u.id = ?1' )
    ->orderBy( 'u.name' , 'ASC' );
 

QueryBuilder的接口有:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?phpclass QueryBuilder{
     // Example - $qb->select('u')
     // Example - $qb->select(array('u', 'p'))
     // Example - $qb->select($qb->expr()->select('u', 'p'))
     public function select( $select = null);
 
     // Example - $qb->delete('User', 'u')
     public function delete ( $delete = null, $alias = null);
 
     // Example - $qb->update('Group', 'g')
     public function update( $update = null, $alias = null);
 
     // Example - $qb->set('u.firstName', $qb->expr()->literal('Arnold'))
     // Example - $qb->set('u.numChilds', 'u.numChilds + ?1')
     // Example - $qb->set('u.numChilds', $qb->expr()->sum('u.numChilds', '?1'))
     public function set( $key , $value );
 
     // Example - $qb->from('Phonenumber', 'p')
     public function from( $from , $alias = null);
 
     // Example - $qb->innerJoin('u.Group', 'g', Expr\Join::WITH, $qb->expr()->eq('u.status_id', '?1'))
     // Example - $qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1')
     public function innerJoin( $join , $alias = null, $conditionType = null, $condition = null);
 
     // Example - $qb->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, $qb->expr()->eq('p.area_code', 55))
     // Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55')
     public function leftJoin( $join , $alias = null, $conditionType = null, $condition = null);
 
     // NOTE: ->where() overrides all previously set conditions
     //
     // Example - $qb->where('u.firstName = ?1', $qb->expr()->eq('u.surname', '?2'))
     // Example - $qb->where($qb->expr()->andX($qb->expr()->eq('u.firstName', '?1'), $qb->expr()->eq('u.surname', '?2')))
     // Example - $qb->where('u.firstName = ?1 AND u.surname = ?2')
     public function where( $where );
 
     // Example - $qb->andWhere($qb->expr()->orX($qb->expr()->lte('u.age', 40), 'u.numChild = 0'))
     public function andWhere( $where );
 
     // Example - $qb->orWhere($qb->expr()->between('u.id', 1, 10));
     public function orWhere( $where );
 
     // NOTE: -> groupBy() overrides all previously set grouping conditions
     //
     // Example - $qb->groupBy('u.id')
     public function groupBy( $groupBy );
 
     // Example - $qb->addGroupBy('g.name')
     public function addGroupBy( $groupBy );
 
     // NOTE: -> having() overrides all previously set having conditions
     //
     // Example - $qb->having('u.salary >= ?1')
     // Example - $qb->having($qb->expr()->gte('u.salary', '?1'))
     public function having( $having );
 
     // Example - $qb->andHaving($qb->expr()->gt($qb->expr()->count('u.numChild'), 0))
     public function andHaving( $having );
 
     // Example - $qb->orHaving($qb->expr()->lte('g.managerLevel', '100'))
     public function orHaving( $having );
 
     // NOTE: -> orderBy() overrides all previously set ordering conditions
     //
     // Example - $qb->orderBy('u.surname', 'DESC')
     public function orderBy( $sort , $order = null);
 
     // Example - $qb->addOrderBy('u.firstName')
     public function addOrderBy( $sort , $order = null); // Default $order = 'ASC'}

更多更复杂的查询能够查看上文的连接。

查询结果是只能返回对象吗?

固然不仅,当你执行query的时候能够试试使用:

?
1
2
3
4
5
$result = $query ->getResult();
$single = $query ->getSingleResult();
$array = $query ->getArrayResult();
$scalar = $query ->getScalarResult();
$singleScalar = $query ->getSingleScalarResult();

doctrine查询操做总结

如今总结下,doctrine2 作查询操做有下面几种方法

1 使用EntityManager直接进行find查询

2 使用DQL进行createQuery($dql)进行查询

3 使用QueryBuilder进行拼装dql查询

4 使用Repository进行查询

5 使用原生的sql进行createNativeQuery($sql)进行查询

doctrine2的增长,删除,更新操做都须要使用Entity进行操做

一个项目有几个实现路径:

1 Code First:先用代码写好Object,而后根据Object生成数据库

2 Model First:先用工具写好UML,而后根据UML生成数据库和PHP代码

3 Database First:先写好数据库的schema表,而后生成PHP代码

如何作分页操做

分页操做是常用到的,doctrine使用了Paginator类来作这个操做

好比:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
// list_bugs_array.php
use Doctrine\ORM\Tools\Pagination\Paginator;
require_once "bootstrap.php" ;
 
$dql = "SELECT b, e, r, p FROM Bug b JOIN b.engineer e " .
        "JOIN b.reporter r JOIN b.products p ORDER BY b.created DESC" ;
$query = $entityManager ->createQuery( $dql )
                ->setFirstResult(0)
                ->setMaxResults(1);
 
$paginator = new Paginator( $query , $fetchJoinCollection = true);
 
$c = count ( $paginator );
echo "count: $c" . "\r\n" ;
 
$bugs = $query ->getArrayResult();
 
foreach ( $bugs as $item ) {
      print_r( $item );
}
exit ;

Image(16)[4]

返回了总条数2,也返回了查询的结果。赞~!

如何进行sql和dql的调试

咱们难免调试的时候要取出sql和dql语句。咱们可使用

?
1
2
$query ->getDQL()
$query ->getSQL()

来获取出实际进行查询的sql语句

为何在增删更新的时候有个flush操做

doctrine在增长,删除,更新的时候并非直接进行操做,而是将操做存放在每一个EntityManager的UnitOfWork。

你可使用

?
1
2
3
4
$entityManager ->getUnitOfWork()
$entityManager ->getUnitOfWork()->size()
$entityManager ->getEntityState( $entity )
来控制UnitOfWork
?
1
  

如何注入Entity增长,删除,更新操做

doctrine提供了监听Event的功能,好比你要在Persist以前作一个日志处理,你就能够实现一个Listener,其中实现了prePersist方法
而后把Listener挂载到Entity上

?
1
2
3
4
5
6
7
8
<?php
namespace MyProject\Entity;
 
/** @Entity @EntityListeners({"UserListener"}) */
class User
{
     // ....
}

如何实现事务?

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
// $em instanceof EntityManager
$em ->getConnection()->beginTransaction(); // suspend auto-commit
try {
     //... do some work
     $user = new User;
     $user ->setName( 'George' );
     $em ->persist( $user );
     $em -> flush ();
     $em ->getConnection()->commit();
} catch (Exception $e ) {
     $em ->getConnection()->rollback();
     $em ->close();
     throw $e ;
}

使用DQL只能进行查询操做吗?

固然不仅,咱们可使用execute()来对增删改查的DQL语句进行操做

?
1
2
3
<?php
$q = $em ->createQuery( 'delete from MyProject\Model\Manager m where m.salary > 100000' );
$numDeleted = $q ->execute();

Entity能够设置哪些属性:

参考文章:

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html

有哪些Cache机制

doctrine能够支持APC,Memcache,Xcache,Redis这几种缓存机制

全部这些缓存机制都是基于一个抽象方法,这个抽象方法中有的接口有:

?
1
2
3
4
fetch( $id )
contains( $id )
save( $id , $data , $lifeTime =false)
delete ( $id )

各自对应的初始化代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
APC:
<?php
$cacheDriver = new \Doctrine\Common\Cache\ApcCache();
$cacheDriver ->save( 'cache_id' , 'my_data' );
 
MemCache:
<?php
$memcache = new Memcache();
$memcache ->connect( 'memcache_host' , 11211);
 
$cacheDriver = new \Doctrine\Common\Cache\MemcacheCache();
$cacheDriver ->setMemcache( $memcache );
$cacheDriver ->save( 'cache_id' , 'my_data' );
 
Xcache:
<?php
$cacheDriver = new \Doctrine\Common\Cache\XcacheCache();
$cacheDriver ->save( 'cache_id' , 'my_data' );
 
Redis:
<?php
$redis = new Redis();
$redis ->connect( 'redis_host' , 6379);
 
$cacheDriver = new \Doctrine\Common\Cache\RedisCache();
$cacheDriver ->setRedis( $redis );
$cacheDriver ->save( 'cache_id' , 'my_data' );

还有一个命令clear-cache能够用来进行缓存的增删改查

具体参考文章:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/caching.html

doctrine提供的工具备哪些

参考文章:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/tools.html

也可使用list来进行查看命令

Image(19)[4]

看看这些命令,大体能够完成的功能是:

数据库schema生成php的Entity代码

php的Entity代码生成数据库schema

缓存相关操做

数据库schema相关操做

but对于这些命令:

可是本身试过才知道,有些仍是有一些限制的的,好比它必需要求表必须有一个自增主键,并且而且是库中的全部表都有这个要求。。。才能生成ORM的Entity等数据。

还有生成entity也必需要先建立一个基本的模板之类的。

可是这些工具总的来讲仍是颇有用的,聊胜于无。

总结

doctrine就是一个很庞大的ORM系统,它能够嵌入到其余框架中,好比symfony,好比Yii等。

ORM的最终目的就是将逻辑层和持久层分离,在这个层面来讲,doctrine很好地完成了这个任务。

doctrine已经将你能考虑到的操做都进行封装好了,相信若是熟悉了以后,开发过程应该是会很是快的

相关文章
相关标签/搜索