本文非原創,轉載自:http://www.cnblogs.com/yjf512/p/3375614.htmlphp
感謝原做者分享!html
以前和最近一個項目用到了Doctrine,因爲是別人搭建的,本身沒有很瞭解,最近又開始作的時候發現拙荊見肘,因而看了一下doctrine教程,本文就是加上本身理解的doctrine教程文檔筆記了。mysql
須要php5.3.3及以上git
可使用composer安裝github
Doctrine是一個ORM(Object-relational mapper),提供php數據庫和PHP對象的映射。他和其餘的ORM同樣都是爲了保證持久層和邏輯層的分類而存在的。redis
Entity是PHP的一個對象sql
Entity對應的表須要有主鍵mongodb
Entity中不能含有final屬性或者final方法數據庫
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.htmlbootstrap
https://github.com/doctrine/doctrine2-orm-tutorial
在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的命令行以前,須要先編寫這個配置文件。
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()來打印信息。
首先明確表和表的關聯有幾種:
好比教程中舉的例子,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而不是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,那麼邏輯層是什麼都不須要動的。
到這裏就嘀咕,我常常進行的查詢是根據字段查詢列表啥的,難道每次須要我都拼接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();
}}
|
除了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是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();
|
如今總結下,doctrine2 作查詢操做有下面幾種方法
doctrine2的增長,刪除,更新操做都須要使用Entity進行操做
分頁操做是常用到的,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
;
|
返回了總條數2,也返回了查詢的結果。贊~!
咱們難免調試的時候要取出sql和dql語句。咱們可使用
1
2
|
$query
->getDQL()
$query
->getSQL()
|
來獲取出實際進行查詢的sql語句
doctrine在增長,刪除,更新的時候並非直接進行操做,而是將操做存放在每一個EntityManager的UnitOfWork。
你可使用
1
2
3
4
|
$entityManager
->getUnitOfWork()
$entityManager
->getUnitOfWork()->size()
$entityManager
->getEntityState(
$entity
)
來控制UnitOfWork
|
1
|
|
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
;
}
|
固然不僅,咱們可使用execute()來對增刪改查的DQL語句進行操做
1
2
3
|
<?php
$q
=
$em
->createQuery(
'delete from MyProject\Model\Manager m where m.salary > 100000'
);
$numDeleted
=
$q
->execute();
|
參考文章:
全部這些緩存機制都是基於一個抽象方法,這個抽象方法中有的接口有:
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
參考文章:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/tools.html
也可使用list來進行查看命令
看看這些命令,大體能夠完成的功能是:
可是本身試過才知道,有些仍是有一些限制的的,好比它必需要求表必須有一個自增主鍵,並且而且是庫中的全部表都有這個要求。。。才能生成ORM的Entity等數據。
還有生成entity也必需要先建立一個基本的模板之類的。
可是這些工具總的來講仍是頗有用的,聊勝於無。
doctrine就是一個很龐大的ORM系統,它能夠嵌入到其餘框架中,好比symfony,好比Yii等。
ORM的最終目的就是將邏輯層和持久層分離,在這個層面來講,doctrine很好地完成了這個任務。
doctrine已經將你能考慮到的操做都進行封裝好了,相信若是熟悉了以後,開發過程應該是會很是快的