Doctrine是一個基于PHP的對象關系映射(ORM),它構建在強大的數據庫抽象層(DBAL)之上,透明地為PHP對象提供持久化。
你可以從官方文檔中讀到更多關于Doctrine ORM 的內容。
首先,先激活和配置Doctrine DBAL,然后激活ORM。如果你遵循本章的說明和約定,你只需要告訴Doctrine自動映射你的實體:
#?app/config/config.yml?doctrine:?????orm:?????????auto_mapping:?true? 因為Doctrine透明地提供PHP對象的持久化,所以它可以與任何PHP類一起工作:
?namespace?Acme\HelloBundle\Entity;??class?User?{?????protected?$id;?????protected?$name;??????public?function?getId()?????{?????????return?$this->id;?????}??????public?function?setName($name)?????{?????????$this->name?=?$name;?????}??????public?function?getName()?????{?????????return?$this->name;?????}?}
當你的實體被定義之后,當你使用doctrine:generate:entities命令時,你可以忽略getter/setter方法而讓Doctrine為你創建它們。這只在你創建了映射信息之后才能正常工作(參見下面)
為了讓Doctrine管理你的類(Doctrine2中叫實體),你需要使用注釋、XML或YAML來編寫映射信息:
?namespace?Acme\HelloBundle\Entity;??use?Doctrine\ORM\Mapping?as?ORM;?????class?User?{??????????????protected?$id;?????????????protected?$name;?}? 當你在Symfony2項目中使用注釋時,你不得不將所有Doctrine ORM的名字空間注釋都使用rom:前綴。
如果你使用YAML或XML去描述你的實體時,你可以忽略實體類的創建,并讓doctrine:generate:entities命令來為你自動生成。
為了不用為每個實體創建一個文件,你也可以在doctrine.orm.yml文件中定義你的映射信息。
用下列命令創建與你元數據信息相關的數據庫和模式
$?php?app/console?doctrine:database:create?$?php?app/console?doctrine:schema:create? 最后,使用你的實體,并且用Doctrine來管理它的持久化狀態:
//?src/Acme/HelloBundle/Controller/UserController.php?namespace?Acme\HelloBundle\Controller;??use?Acme\HelloBundle\Entity\User;??class?UserController?extends?Controller?{?????public?function?createAction()?????{?????????$user?=?new?User();?????????$user->setName('Jonathan?H.?Wage');??????????$em?=?$this->get('doctrine')->getEntityManager();?????????$em->persist($user);?????????$em->flush();??????????//?...?????}??????public?function?editAction($id)?????{?????????$em?=?$this->get('doctrine')->getEntityManager();?????????$user?=?$em->find('AcmeHelloBundle:User',?$id);?????????$user->setBody('new?body');?????????$em->persist($user);?????????$em->flush();??????????//?...?????}??????public?function?deleteAction($id)?????{?????????$em?=?$this->get('doctrine')->getEntityManager();?????????$user?=?$em->find('AcmeHelloBundle:User',?$id);?????????$em->remove($user);?????????$em->flush();??????????//?...?????}?}? 現在問題出現了,如果你想改變你的映射信息并更新你的開發數據庫模式,而不想丟掉設置并失去已有數據的話,那么首先我們應該在我們的User實體中添加一個new屬性:
?namespace?Acme\HelloBundle\Entity;??use?Doctrine\ORM\Mapping?as?ORM;???class?User?{??????????protected?$new;???????}? 當你需要那樣做,將你的數據庫模式更新到new字段時,你只需要去運行下列命令:
$?php?app/console?doctrine:schema:update? 現在你的數據庫被更新且new字段被添加到數據庫的數據表中。
查詢
如你所見,使用實體管理器操作一個對象是簡單容易的。但你如何查詢對象集呢?如同所有Doctrine操作一樣,這也是可以通過實體管理器做到的。在上個例子中可以使用查詢語句來改變刪除先引導對象再將其刪除的刪除方法:
public?function?deleteAction($id)?{?????$query?=?$this->get('doctrine')->getEntityManager()????????????????->createQuery('DELETE?FROM?Acme\HelloBundle\Entity\User?u?????????????????????????WHERE?u.id?=?:id');?????$query->setParameters(array(?????????????????'id'?=>?$id?????));??????$query->execute();???????}? 當然,你也可以使用SELECT和UPDATE查詢。Doctrine帶來了它自己的查詢語言DQL(Doctrine Query Language)。DQL跟SQL有些相似,但查詢語言有它自己的語法。
你可以在官方網站查看更多關于Doctrine查詢語言 。DQL的優點在于它是數據庫未知的,也就是說使用DQL所寫的同一查詢可以在任何支持的數據庫引擎上工作。
使用DQL的DELETE或UPDATE語句是有缺陷的。具體來說,因為是直接對數據庫操作,Doctrine并不知道內部細節。舉個例子,如果你對一個對象查詢,然后通過UPDATE語句直接在數據庫中對該對象進行更新,該對象本身不會反映這個更新。因此,小心使用這些語句,在單個請求里,你的對象可能會和你數據庫不同步。
Repositories
在Symfony2的控制器中使用查詢是很不好的做法,查詢應該在你Bundle的模型層中使用,以便能夠測試它們并在你的應用程序中重用。幸運地是,Doctrine允許你使用名為Repositories的特殊類來封裝查詢。
Doctrine為你的repository類提供了一個缺省實現,因此你可以使用它們的通用方法去查詢你的實體數據。其中之一就是findAll方法。
$em?=?$this->get('doctrine')->getEntityManager();?$users?=?$em->getRepository('AcmeHelloBundle:User')->findAll();? 如果你想創建你自己的函數去查詢和維護你的數據,你需要為一個實體創建一個自定義的Repository類。要做到這一點,你需要在你的映射定義中添加repository類的名稱。
//?src/Acme/HelloBundle/Entity/User.php?namespace?Acme\HelloBundle\Entity;??use?Doctrine\ORM\Mapping?as?ORM;??/**??*?@ORM\Entity(repositoryClass="Acme\HelloBundle\Repository\UserRepository")??*/?class?User?{?????//...?}? 如果你運行以下命令生成你的實體,那么repository類會自動創建。
$?php?app/console?doctrine:generate:entities? 如果你在添加repositoryClass映射之前就已經生成了你的實體,那么你就不得不自行創建這個類了。幸運地是,它非常容易,簡單地在你Bundle目錄下的Repository/目錄創建一個類,并確保它繼承Doctrine\ORM\EntityRepository類。一旦你創建了這個類,你就可以為你的實體添加任意方法了。
下列代碼示范了一個repository類的示例。
?namespace?Acme\HelloBundle\Repository;??use?Doctrine\ORM\EntityRepository;??class?UserRepository?extends?EntityRepository?{?????public?function?findAllOrderedByName()?????{?????????return?$this->getEntityManager()?????????????????????->createQuery('SELECT?u?FROM?Acme\HelloBundle\Entity\User?u?????????????????????????????????????ORDER?BY?u.name?ASC')?????????????????????->getResult();?????}?}? 實體管理器在repositories函數中可能通過$this->getEntityManager()來訪問。
新方法的用法和缺省查找函數一樣。
$em?=?$this->get('doctrine')->getEntityManager();?$users?=?$em->getRepository('AcmeHelloBundle:User')?????????????->findAllOrderedByName();? 配置
我們在Symfony2中只是知道了讓Doctrine ORM運行的必要配置選項,其它的配置選項都使用缺省值。
下面的配置示例顯示了ORM的所有缺省配置:
doctrine:?????orm:?????????auto_mapping:?true?????????auto_generate_proxy_classes:?true?????????proxy_namespace:?Proxies?????????proxy_dir:?%kernel.cache_dir%/doctrine/orm/Proxies?????????default_entity_manager:?default?????????metadata_cache_driver:?array?????????query_cache_driver:?array?????????result_cache_driver:?array? 這有許多其它的配置選項,你可以用來覆寫某些類,但那些僅用于非常高級的用例。你可以看看配置指南 ,以便對所有支持的選項有個了解。
你可以指定緩存驅動的值,象"array"、"apc"、"memcache"或"xcache"。
下面的例子展示了緩存配置的概況:
doctrine:?????orm:?????????auto_mapping:?true?????????metadata_cache_driver:?apc?????????query_cache_driver:?xcache?????????result_cache_driver:?????????????type:?memcache?????????????host:?localhost?????????????port:?11211?????????????instance_class:?Memcache? 映射配置
顯式定義所有被映射的實體是ORM唯一必要的配置,你可以控制一些配置選項,下面是一些映射配置選項:
- type :在注釋、xml、yml、php或staticphp中,它指定你映射所用的元數據類型;
- dir :指向映射或實體文件路徑(依賴驅動),如果這是個相對路徑,那么它假定Bundle的根目錄是它的根。它只在你映射的名稱是Bundle名時工作。如果你想使用這個選項指向一個絕對路徑,那么你應該使用在DIC中的內核參數來做它的前綴(如%kernel.root_dir%);
- prifix :在這個映射的所有實體中共享的通用名稱空間前綴這個前綴應該永遠不能與其它定義的映射前綴沖突,否則你的一些實體將不能被Doctrine發現。這個選項缺省指向Bundle名稱空間+Entity。如應用程序Bundle叫AcmeHelloBundle,那么prefixy就應該是Acme\HelloBundle\Entity
- alias :Doctrine為實體的名稱空間提供了別名方式(使用簡短的名稱),可以在DQL查詢或Repository訪問中使用。當使用一個Bundle時別名會默認為該Bundle的名稱。
- is_bundle :該選項是從dir派生出來的值,缺省為真。當dir使用相對路徑,用file_exists()檢查驗證返回false時該值為真,反之為假。
為了避免為你的映射配置大量信息,你應該遵循以下約定:
- 將你所有的實體放置在Bundle目錄下的Entity/目錄。如Acme/HelloBundle/Entity/;
- 如果你使用xml、yml或php映射,那么將你所有的配置文件都放在Resources/config/doctrine/目錄中,后綴分別是orm.xml、orm.yml或orm.php;
- 如果只有一個Entity/目錄,而Resources/config/doctrine/目錄未找到時假定使用注釋方式
以下配置展示了多個映射的例子:
doctrine:?????orm:?????????auto_mapping:?false?????????mappings:?????????????MyBundle1:?~?????????????MyBundle2:?yml?????????????MyBundle3:?{?type:?annotation,?dir:?Entity/?}?????????????MyBundle4:?{?type:?xml,?dir:?Resources/config/doctrine/mapping?}?????????????MyBundle5:?????????????????type:?yml?????????????????dir:?my-bundle-mappings-dir?????????????????alias:?BundleAlias?????????????doctrine_extensions:?????????????????type:?xml?????????????????dir:?%kernel.root_dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entity?????????????????prefix:?DoctrineExtensions\Entity\?????????????????alias:?DExt? 多實體管理
你可以在Symfony2應用程序中使用多實體管理。如果你使用不同的數據庫甚至供應商使用完全不同的實體集。
下列配置代碼展示了如何定義兩個實體管理器
doctrine:?????orm:?????????default_entity_manager:???default?????????cache_driver:?????????????apc???????????#?array,?apc,?memcache,?xcache?????????entity_managers:?????????????default:?????????????????connection:???????default?????????????????mappings:?????????????????????MyBundle1:?~?????????????????????MyBundle2:?~?????????????customer:?????????????????connection:???????customer?????????????????mappings:?????????????????????MyBundle3:?~? 與DBAL一樣,如果你已經配置了多個實體管理器實例,并且想得到指定的那個,你可以從Doctrine注冊中用它的名稱檢索到它:
class?UserController?extends?Controller?{?????public?function?indexAction()?????{?????????$em?=??$this->get('doctrine')->getEntityManager();?????????$customerEm?=??$this->get('doctrine')->getEntityManager('customer');?????}?}? 注冊事件監聽和訂閱
Doctrine 使用輕量級的Doctrine\Common\EventManager類來跟蹤你想要“鉤進”的大量不同的事件。你可以通過doctrine.event_listener或doctrine.event_subscriber使用服務容器來標識各自的服務,從而注冊事件監聽器或訂閱。
要注冊事件監聽器或訂閱(監聽器來源于此)服務,你必須要使用適當的名稱來標識它們。依賴你的用戶,你可以將監聽器“鉤進”每個DBAL連接和ORM實體管理器中,或者只是一個指定的DBAL連接和使用該連接的所有實體管理器。
doctrine:?????dbal:?????????default_connection:?default?????????connections:?????????????default:?????????????????driver:?pdo_sqlite?????????????????memory:?true??services:?????my.listener:?????????class:?MyEventListener?????????tags:?????????????-?{?name:?doctrine.event_listener?}?????my.listener2:?????????class:?MyEventListener2?????????tags:?????????????-?{?name:?doctrine.event_listener,?connection:?default?}?????my.subscriber:?????????class:?MyEventSubscriber?????????tags:?????????????-?{?name:?doctrine.event_subscriber,?connection:?default?}
注冊自定義的DQL函數
你可以通過配置注冊自定義的DQL函數。
#?app/config/config.yml?doctrine:?????orm:?????????#?...?????????entity_managers:?????????????default:?????????????????#?...?????????????????dql:?????????????????????string_functions:?????????????????????????test_string:?Acme\HelloBundle\DQL\StringFunction?????????????????????????second_string:?Acme\HelloBundle\DQL\SecondStringFunction?????????????????????numeric_functions:?????????????????????????test_numeric:?Acme\HelloBundle\DQL\NumericFunction?????????????????????datetime_functions:?????????????????????????test_datetime:?Acme\HelloBundle\DQL\DatetimeFunction? 控制行命令
Doctrine2 ORM 在doctrine名稱空間下整合提供了一些控制行命令,為了看到你可以在控制行運行的命令列表,不要加任何參數或選項:
$?php?app/console?...??doctrine???:ensure-production-settings??Verify?that?Doctrine?is?properly?configured?for?a?production?environment.???:schema-tool?????????????????Processes?the?schema?and?either?apply?it?directly?on?EntityManager?or?generate?the?SQL?output.?doctrine:cache???:clear-metadata??????????????Clear?all?metadata?cache?for?a?entity?manager.???:clear-query?????????????????Clear?all?query?cache?for?a?entity?manager.???:clear-result????????????????Clear?result?cache?for?a?entity?manager.?doctrine:fixtures???:load????????????????????????Load?data?fixtures?to?your?database.?doctrine:database???:create??????????????????????Create?the?configured?databases.???:drop????????????????????????Drop?the?configured?databases.?doctrine:generate???:entities????????????????????Generate?entity?classes?and?method?stubs?from?your?mapping?information.???:entity??????????????????????Generate?a?new?Doctrine?entity?inside?a?bundle.???:proxies?????????????????????Generates?proxy?classes?for???entity?classes.???:repositories????????????????Generate?repository?classes?from?your?mapping?information.?doctrine:mapping???:convert?????????????????????Convert?mapping?information?between?supported?formats.???:convert-d1-schema???????????Convert?a?Doctrine1?schema?to?Doctrine2?mapping?files.???:import??????????????????????Import?mapping?information?from?an?existing?database.?doctrine:query???:dql?????????????????????????Executes?arbitrary?DQL?directly?from?the?command?line.???:sql?????????????????????????Executes?arbitrary?SQL?directly?from?the?command?line.?doctrine:schema???:create??????????????????????Processes?the?schema?and?either?create?it?directly?on?EntityManager?Storage?Connection?or?generate?the?SQL?output.???:drop????????????????????????Processes?the?schema?and?either?drop?the?database?schema?of?EntityManager?Storage?Connection?or?generate?the?SQL?output.???:update??????????????????????Processes?the?schema?and?either?update?the?database?schema?of?EntityManager?Storage?Connection?or?generate?the?SQL?output.??...? 為了能引導數據夾到你的數據庫中,你需要安裝DoctrineFixturesBundle,如何安裝請參見食譜條目如何在Symfony2中創建數據夾 。
表單集成
在Doctrine ORM和Symfony2 Form組件之間的集成是緊密的。因為Doctrine實體是POPO(plain old php objects),在缺省情況下可以很好地集成到Form組件中,至少原始的數據類型如字符串、整數和字段沒有問題。你也可以使用associations來很好地集成它們。
使用專用類型EntityType是有幫助的,它提供了來自被選實體的選擇列表:
use?Symfony\Bridge\Doctrine\Form\Type\EntityType;??$builder->add('users','entity',??????array('class'?=>?'Acme\\HelloBundle\\Entity\\User',?));? 必填的class選項需要一個實體類名作為參數,可選的property選項允許你選擇用于展示的實體屬性(如果沒有設置則使用__toString)。可選的query_builder選項需要一個QueryBuilder實例或封裝的檢索repository作為參數,并返回QueryBuilder用來得到選擇。如果沒有設置則將使用所有實體。
總結
以上是生活随笔為你收集整理的Symfony2Book04:Doctrine03-对象关系映射(ORM)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。