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已經將你能考慮到的操做都進行封裝好了,相信若是熟悉了以後,開發過程應該是會很是快的

相關文章
相關標籤/搜索