亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

Symfony2 Chinese manual / 數(shù)據(jù)庫(kù)和Doctrine ORM

數(shù)據(jù)庫(kù)和Doctrine ORM

對(duì)于任何應(yīng)用程序來(lái)說(shuō),一個(gè)最常見(jiàn)和最具挑戰(zhàn)的任務(wù),就是從數(shù)據(jù)庫(kù)中讀取和持久化數(shù)據(jù)信息。盡管symfony框架并未整合任何需要使用數(shù)據(jù)庫(kù)的組件,但是卻緊密集成了一個(gè)名為 Doctrine 的三方類(lèi)庫(kù)。Doctrine的主要目標(biāo)是為你提供一個(gè)強(qiáng)有力的工具,令數(shù)據(jù)庫(kù)互動(dòng)更加輕松和靈活。

在本章,你將學(xué)習(xí)如何在Symfony項(xiàng)目中利用doctrine來(lái)提供豐富的數(shù)據(jù)庫(kù)互動(dòng)。

Doctrine與symfony是完全解耦的,使用與否是可選的。本章講的全部是Doctrine ORM,目的是讓你把對(duì)象映射到關(guān)系型數(shù)據(jù)庫(kù)中(如 MySQL, PostgreSQL 和 Microsoft SQL)。如果你傾向于使用數(shù)據(jù)庫(kù)的原始查詢,這很簡(jiǎn)單,可參考 如何使用Doctrine DBAL 一文的講解。

你也可以使用Doctrine ODM類(lèi)庫(kù)將數(shù)據(jù)持久化到 MongoDB。參考 DoctrineMongoDBBundle 以了解更多信息。

簡(jiǎn)單例子:一件產(chǎn)品(Product) ?

要了解Doctrine是如何工作的,最簡(jiǎn)單的方式就是看一個(gè)實(shí)際應(yīng)用。在本節(jié),你需要配置你的數(shù)據(jù)庫(kù),創(chuàng)建一個(gè) Product 對(duì)象,把它持久化到數(shù)據(jù)庫(kù),再取回它。

配置數(shù)據(jù)庫(kù) ?

真正開(kāi)始之前,你需要配置你的數(shù)據(jù)庫(kù)連接信息。按照慣例,這部分信息通常配置在 app/config/parameters.yml 文件中:

# app/config/parameters.ymlparameters:
    database_host:      localhost
    database_name:      test_project
    database_user:      root
    database_password:  password
 # ...


通過(guò) parameters.yml 來(lái)定義配置,只是一個(gè)慣例。配置Doctrine時(shí),定義在那個(gè)文件中的參數(shù),將被主配置文件引用:

# app/config/config.ymldoctrine:
    dbal:
        driver:   pdo_mysql
        host:     "%database_host%"
        dbname:   "%database_name%"
        user:     "%database_user%"
       password: "%database_password%"
<!-- app/config/config.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services"           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xmlns:doctrine="http://symfony.com/schema/dic/doctrine"           xsi:schemaLocation="http://symfony.com/schema/dic/services        http://symfony.com/schema/dic/services/services-1.0.xsd        http://symfony.com/schema/dic/doctrine        http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">     <doctrine:config>
        <doctrine:dbal                driver="pdo_mysql"                host="%database_host%"                dbname="%database_name%"                user="%database_user%"                password="%database_password%" />
    </doctrine:config></container>
// app/config/config.php$configuration->loadFromExtension('doctrine', array(
    'dbal' => array(
        'driver'   => 'pdo_mysql',
        'host'     => '%database_host%',
        'dbname'   => '%database_name%',
        'user'     => '%database_user%',
        'password' => '%database_password%',
    ),));

通過(guò)把數(shù)據(jù)庫(kù)信息分離到一個(gè)單獨(dú)文件中,你可以很容易地為每個(gè)服務(wù)器保存不同的版本。你還可以在項(xiàng)目外輕松存儲(chǔ)數(shù)據(jù)庫(kù)配置(或任何敏感信息),舉例來(lái)說(shuō),就和apache中的配置信息一樣。參考 服務(wù)容器外部參數(shù)如何設(shè)置 以了解更多。

現(xiàn)在Doctrine可以連接你的數(shù)據(jù)庫(kù)了,下面的命令可以自動(dòng)生成一個(gè)空的 test_project 數(shù)據(jù)庫(kù):

$  php bin/console doctrine:database:create

設(shè)置數(shù)據(jù)庫(kù)為UTF8

Symfony項(xiàng)目開(kāi)始后,老練的程序員也會(huì)犯的一個(gè)錯(cuò)誤是,忘了設(shè)置他們的數(shù)據(jù)庫(kù)默認(rèn)字符集和校對(duì)規(guī)則(charset and collation),最終變成latin類(lèi)型,也就是多數(shù)數(shù)據(jù)庫(kù)默認(rèn)的。他們也許在第一次操作時(shí)會(huì)記得,但在后面的開(kāi)發(fā)中敲打兩行相關(guān)的常用命令之后就完全忘掉了:

$  php bin/console doctrine:database:drop --force
$  php bin/console doctrine:database:create

設(shè)置UTF8為MySQL的默認(rèn)字符集簡(jiǎn)單到只要在配置文件(一般是my.cnf文件)中加幾行代碼就可以了:

[mysqld]#Version 5.5.3 introduced "utf8mb4", which is recommendedcollation-server     = utf8mb4_general_ci # Replaces utf8_general_cicharacter-set-server = utf8mb4            # Replaces utf8

你還可以改變Doctrine的默認(rèn)字符集,以便生成的SQL使用設(shè)置正確的字符集。

# app/config/config.ymldoctrine:
    dbal:
        charset: utf8mb4
        default_table_options:
            charset: utf8mb4
            collate: utf8mb4_unicode_ci
<!-- app/config/config.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services"           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xmlns:doctrine="http://symfony.com/schema/dic/doctrine"           xsi:schemaLocation="http://symfony.com/schema/dic/services        http://symfony.com/schema/dic/services/services-1.0.xsd        http://symfony.com/schema/dic/doctrine        http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">     <doctrine:config>
        <doctrine:dbal                charset="utf8mb4">
            <doctrine:default-table-option name="charset">utf8mb4</doctrine:default-table-option>
            <doctrine:default-table-option name="collate">utf8mb4_unicode_ci</doctrine:default-table-option>
        </doctrine:dbal>
    </doctrine:config></container>
// app/config/config.php$configuration->loadFromExtension('doctrine', array(
    'dbal' => array(
        'charset' => 'utf8mb4',
        'default_table_options' => array(
            'charset' => 'utf8mb4'
            'collate' => 'utf8mb4_unicode_ci'
        )
    ),));

我們推薦避免使用Mysql的 uft8 字符集,因?yàn)樗⒉患嫒?-byte unicode字符,如果字符串中有這種字符會(huì)被清空。不過(guò)這種情況被修復(fù)了,參考 新型utf8mb4字符集。

如果你要用SQLite作為數(shù)據(jù)庫(kù),在path選項(xiàng)中設(shè)置你的數(shù)據(jù)庫(kù)路徑:

# app/config/config.ymldoctrine:
    dbal:
        driver: pdo_sqlite
        path: "%kernel.root_dir%/sqlite.db"
        charset: UTF8
<!-- app/config/config.xml --><?xml version="1.0" encoding="UTF-8" ?><container xmlns="http://symfony.com/schema/dic/services"           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xmlns:doctrine="http://symfony.com/schema/dic/doctrine"           xsi:schemaLocation="http://symfony.com/schema/dic/services        http://symfony.com/schema/dic/services/services-1.0.xsd        http://symfony.com/schema/dic/doctrine        http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">     <doctrine:config>
        <doctrine:dbal                driver="pdo_sqlite"                path="%kernel.root_dir%/sqlite.db"                charset="UTF-8" />
    </doctrine:config></container>
// app/config/config.php$container->loadFromExtension('doctrine', array(
    'dbal' => array(
        'driver'  => 'pdo_sqlite',
        'path'    => '%kernel.root_dir%/sqlite.db',
        'charset' => 'UTF-8',
    ),));

創(chuàng)建一個(gè)Entity類(lèi) ?

假設(shè)你正構(gòu)建一套程序,其中有些產(chǎn)品需要展示。即使不考慮Doctrine或者數(shù)據(jù)庫(kù),你也已經(jīng)知道你需要一個(gè) Product 對(duì)象來(lái)呈現(xiàn)這些產(chǎn)品。在你AppBundle的 Entity 目錄下創(chuàng)建這個(gè)類(lèi):

// src/AppBundle/Entity/Product.phpnamespace AppBundle\Entity; class Product{
    private $name;
    private $price;
    private $description;}

這個(gè)類(lèi)——常被稱(chēng)作一個(gè)“Entity”,表示 一個(gè)保存著數(shù)據(jù)的基本類(lèi) ——它很簡(jiǎn)單,可以滿足程序中所需產(chǎn)品的業(yè)務(wù)需求。這個(gè)類(lèi)還不能被保存到數(shù)據(jù)庫(kù)中——它只是個(gè)簡(jiǎn)單的PHP類(lèi)。

一旦你學(xué)習(xí)了Doctrine背后的概念,你可以讓Doctrine為你創(chuàng)建entity類(lèi)。它將問(wèn)你一些互動(dòng)問(wèn)題來(lái)幫你創(chuàng)建任意的entity:

$  php bin/console doctrine:generate:entity

添加映射信息 ?

Doctrine允許你以一種更加有趣的方式來(lái)使用數(shù)據(jù)庫(kù),而不只是把標(biāo)量數(shù)據(jù)的行(rows)取出到數(shù)組中。Doctrine允許你從數(shù)據(jù)庫(kù)中取出整個(gè) 對(duì)象,同時(shí)持久化整個(gè)對(duì)象到數(shù)據(jù)庫(kù)中。對(duì)Doctrine來(lái)說(shuō)要實(shí)現(xiàn)這些,你必須 映射 數(shù)據(jù)表到特定的PHP類(lèi)中,那些表的列(columns)必須被映射為相應(yīng)PHP類(lèi)的特定屬性。

1466153595_56497_19601_doctrine_image_1.png

你要以“元數(shù)據(jù)(meatdata)”形式來(lái)提供這些映射信息,有一組規(guī)則可以準(zhǔn)確告之Doctrine Product 類(lèi)及其屬性應(yīng)該如何 映射到 一個(gè)特定的數(shù)據(jù)表。這個(gè)metadata可以通過(guò)不同的格式來(lái)指定,包括YAML,XML或者通過(guò)DocBlock注釋?zhuān)ㄗg注:annotations)直接定義到 Product 類(lèi)中:

// src/AppBundle/Entity/Product.phpnamespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; /**
 * @ORM\Entity
 * @ORM\Table(name="product")
 */class Product{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;     /**
     * @ORM\Column(type="string", length=100)
     */
    private $name;     /**
     * @ORM\Column(type="decimal", scale=2)
     */
    private $price;     /**
     * @ORM\Column(type="text")
     */
    private $description;
# src/AppBundle/Resources/config/doctrine/Product.orm.ymlAppBundle\Entity\Product:
    type: entity
    table: product
    id:
        id:
            type: integer
            generator: { strategy: AUTO }
    fields:
        name:
            type: string
            length: 100
        price:
            type: decimal
            scale: 2
        description:
            type: text
<!-- 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>

一個(gè)bundle只可以接受一種metadata的定義格式。比如,不能把YAML的metadata定義和添加了注釋?zhuān)╝nnotation)的PHP entity類(lèi)混用。

表名是可選的,如果省略,將自動(dòng)取決于entity類(lèi)的名稱(chēng)。

Doctrine允許你選擇廣泛的字段類(lèi)型,每一種都有自己的配置??捎米侄晤?lèi)型的信息,參考 Doctrine字段類(lèi)型參考。

你也可以查看Doctrine官方文檔 Basic Mapping Documentation 以了解關(guān)于映射的所有細(xì)節(jié)信息。如果你使用annotation,你需要為所有annotation加掛 ORM\ (例如  ORM\Column(...) ),這在Doctrine文檔中并未寫(xiě)明。你還需要去包容 use Doctrine\ORM\Mapping as ORM; 聲明,它可以 import(導(dǎo)入) ORM annotation前綴。

小心Entity類(lèi)名(或者其屬性)同時(shí)也是一個(gè)SQL保留的關(guān)鍵字(如 groupuser )。例如,如果你的entity類(lèi)名稱(chēng)為 Group ,那么,默認(rèn)時(shí),你的表名將會(huì)是group,這在一些數(shù)據(jù)庫(kù)引擎中可能導(dǎo)致SQL錯(cuò)誤。參考 Reserved SQL keywords documentation 以了解如何正確規(guī)避這些名稱(chēng)。可選地,你可以任意選擇數(shù)據(jù)庫(kù)的schema,輕松映射成不同的表名或列名。參考  Creating Classes for the Database  和 Property Mapping文檔。

當(dāng)使用其他一些“使用了annotations”的類(lèi)庫(kù)或者程序(如Doxygen)時(shí),你應(yīng)該把 @IgnoreAnnotation 注釋添加到類(lèi)中,來(lái)指示Symfony應(yīng)該忽略哪個(gè)annotation。

例如,要避免 @fn annotation拋出異常,添加下列注釋?zhuān)?/p>

/**
 * @IgnoreAnnotation("fn")
 */class Product// ...

創(chuàng)建entity之后,你應(yīng)該使用以下命令來(lái)驗(yàn)證映射(mappings):

$  php bin/console doctrine:schema:validate

生成Getters和Setters ?

盡管Doctrine現(xiàn)在知道了如何持久化 Product 對(duì)象到數(shù)據(jù)庫(kù),但是類(lèi)本身還不具備真正用途。因?yàn)?Product 僅僅是一個(gè)帶有 private 屬性的常規(guī)PHP類(lèi),你需要?jiǎng)?chuàng)建 public 的getter和setter方法(比如 getName() , setName($name) )以便在程序其他部分來(lái)訪問(wèn)它的屬性(其屬性是protected)。幸運(yùn)的是,下面的命令可以自動(dòng)生成這些模板化的方法:

$  php bin/console doctrine:generate:entities AppBundle/Entity/Product

該命令可以確保 Product 類(lèi)所有的getter和setter都被生成。這是一個(gè)安全的命令行——你可以多次運(yùn)行它,它只會(huì)生成那些不存在的getters和setters(即,不會(huì)替換已有的方法)。

重要提示下面這句話極其深刻,乃是活用Doctrine的關(guān)鍵。大家一定照做。

記得,doctrine entity generator生成的是簡(jiǎn)單的getters/setters。你應(yīng)該復(fù)審那些已生成的方法,在必要時(shí),添加邏輯進(jìn)去,以滿足你的程序之需求。

關(guān)于 doctrine:generate:entities 的更多內(nèi)容

使用 doctrine:generate:entities 命令你可以:

  • 在entity類(lèi)中生成getters和setters;

  • 在entity類(lèi)配置了 @ORM\Entity(repositoryClass=”…”) annotation的情況下生成所對(duì)應(yīng)的repository類(lèi);

  • 為 1:n 或 n:m 生成合適的構(gòu)造器。

doctrine:generate:entities 命令會(huì)保存原始 Product.php 文件的備份并命名為 Product.php~ 。 有些時(shí)候這個(gè)文件可能會(huì)引發(fā)“Cannot redeclare class”錯(cuò)誤。它可以被安全刪除。你還可以使用 –no-backup 選項(xiàng),來(lái)防止生成這些備份文件。

注意,你并不 需要 (依賴(lài)于)此命令。你也可以手寫(xiě)getters和setters。這個(gè)選項(xiàng)的存在只是為了節(jié)省你的時(shí)間,因?yàn)樵陂_(kāi)發(fā)過(guò)程中創(chuàng)建這些方法是一個(gè)常見(jiàn)任務(wù)。

你也可以為一個(gè)bundle或者一個(gè)entity命名空間內(nèi)的所有已知實(shí)體(任何包含Doctrine映射信息的PHP類(lèi))來(lái)生成getter和setter:

# generates all entities in the AppBundle# 生成AppBundle下的全部entities$  php bin/console doctrine:generate:entities AppBundle
# generates all entities of bundles in the Acme namespace
# 生成Acme命名空間下的bundles的全部entities
$  php bin/console doctrine:generate:entities Acme

創(chuàng)建數(shù)據(jù)表/Schema ?

現(xiàn)在你有了一個(gè)包含映射信息的可用 Product 類(lèi),因此Doctrine確切地知道如何持久化它。當(dāng)然,你還沒(méi)有相應(yīng)的 product 數(shù)據(jù)表在庫(kù)中。幸運(yùn)的是,Doctrine可以自動(dòng)創(chuàng)建所有的數(shù)據(jù)表。要這么做,運(yùn)行以下命令:

$  php bin/console doctrine:schema:update --force

說(shuō)真的,這條命令出奇的強(qiáng)大。它會(huì)比較你的數(shù)據(jù)庫(kù) 理論上應(yīng)該是 什么樣子的(基于你的entities的映射信息)以及 實(shí)際上 它應(yīng)該是什么樣,然后執(zhí)行所需的SQl語(yǔ)句來(lái)將數(shù)據(jù)庫(kù)的schema 更新到 它所應(yīng)有的樣子。換句話說(shuō),如果你添加了一個(gè)包含“映射元數(shù)據(jù)”(mapping metadata)的新屬性到 Product 并運(yùn)行此任務(wù),它將執(zhí)行所需的 "ALTER TABLE" 語(yǔ)句,向已經(jīng)存在的 product 表添加那個(gè)新列。

一個(gè)利用此功能之優(yōu)勢(shì)的更佳方式是通過(guò) migrations,它允許你生成這些SQL語(yǔ)句,并把它們并存儲(chǔ)到migration類(lèi)中,這些類(lèi)能夠有序運(yùn)行在你的生產(chǎn)環(huán)境中,進(jìn)而安全可靠地更新和追蹤數(shù)據(jù)庫(kù)的schema改變。

不管你是否利用了數(shù)據(jù)庫(kù)遷移,doctrine:schema:update 命令只適合在開(kāi)發(fā)環(huán)境中使用。它不應(yīng)該被用于生產(chǎn)環(huán)境。

現(xiàn)在你的數(shù)據(jù)庫(kù)中有了一個(gè)全功能的product表,它的列與你指定的元數(shù)據(jù)相匹配。

持久化對(duì)象到數(shù)據(jù)庫(kù) ?

現(xiàn)在你有了一個(gè)Product實(shí)體和與之映射的product數(shù)據(jù)庫(kù)表。你可以把數(shù)據(jù)持久化到數(shù)據(jù)庫(kù)里。在Controller內(nèi),它非常簡(jiǎn)單。添加下面的方法到bundle的DefaultController中。

現(xiàn)在你已經(jīng)把 Product entity 映射到與之對(duì)應(yīng)的 product 表中,你已經(jīng)準(zhǔn)備好把 Product 對(duì)象持久化到數(shù)據(jù)庫(kù)中。在控制器里面,這極其簡(jiǎn)單。向bundle的 DefaultController 添加以下方法:

// src/AppBundle/Controller/DefaultController.php // ...use AppBundle\Entity\Product;use Symfony\Component\HttpFoundation\Response; // ...public function createAction(){
    $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你希望(最終)存儲(chǔ)Product對(duì)象(還沒(méi)有語(yǔ)句執(zhí)行)
    $em->persist($product);     // actually executes the queries (i.e. the INSERT query)
    // 真正執(zhí)行語(yǔ)句(如,INSERT 查詢)
    $em->flush();     return new Response('Saved new product with id '.$product->getId());}

如果你正在跟進(jìn)本例程,需要?jiǎng)?chuàng)建一個(gè)路由,并指向這個(gè)action,才能看到它運(yùn)行。

本例展示了在控制器中使用Doctrine的 getDoctrine() 方法。這是取出 doctrine 服務(wù)的快捷方法。若你在服務(wù)中注入此服務(wù),即可在任意地方使用doctrine。參考 服務(wù)容器 以了解更多創(chuàng)建服務(wù)之內(nèi)容。

深入分析一下前面的例子:

  • 10-13行 在此處實(shí)例化,并且像其他常規(guī)PHP對(duì)象一樣去使用 $product 對(duì)象。
  • 15行 這一行取出了Doctrine的 entity manager 對(duì)象,它負(fù)責(zé)處理數(shù)據(jù)庫(kù)的持久化(譯注:寫(xiě)入)和取出對(duì)象的過(guò)程。
  • 18行 persist($product) 調(diào)用,告訴Doctrine去 "管理" $product 對(duì)象。它 沒(méi)有 引發(fā)對(duì)數(shù)據(jù)庫(kù)的請(qǐng)求。
  • 21行 當(dāng) flush() 方法被調(diào)用時(shí),Doctrine會(huì)遍歷它管理的所有對(duì)象以確定是否需要被持久化到數(shù)據(jù)庫(kù)。本例中, $product 對(duì)象的數(shù)據(jù)在庫(kù)中并不存在,因此entity manager要執(zhí)行 INSERT 請(qǐng)求,在 product 表中創(chuàng)建一個(gè)新行。

事實(shí)上,由于Doctrine了解你的全部被管理的實(shí)體,當(dāng)你調(diào)用 flush() 方法時(shí),它會(huì)計(jì)算出所有的變更集合(changeset),并按正確順序執(zhí)行語(yǔ)句。它利用準(zhǔn)備好的緩存語(yǔ)句以略微提高性能。比如,你要持久化總數(shù)為100的 Product 對(duì)象,然后調(diào)用 flush() 方法,Doctrine將用一個(gè)單一的prepare語(yǔ)法對(duì)象,來(lái)執(zhí)行100次 INSERT 請(qǐng)求。

如果 flush() 調(diào)用失敗,一個(gè) Doctrine\ORM\ORMException 異常會(huì)被拋出。參考 Transactions and Concurrency(處理和并發(fā))。

在創(chuàng)建和更新對(duì)象時(shí),工作流是相同的。在下一小節(jié)你將看到,如果記錄已經(jīng)存在于數(shù)據(jù)庫(kù)中,Doctrine是如何聰明地自動(dòng)發(fā)出一個(gè) Update 語(yǔ)句的。

Doctrine提供了一個(gè)類(lèi)庫(kù),允許你程序化地加載測(cè)試數(shù)據(jù)到你的項(xiàng)目中(即,"fixture data",固定的數(shù)據(jù))。參考 DoctrineFixturesBundle 以了解更多。

從數(shù)據(jù)庫(kù)中獲取對(duì)象 ?

從數(shù)據(jù)庫(kù)中取回對(duì)象就更簡(jiǎn)單了,舉個(gè)例子,假如你配置了一個(gè)路由,基于產(chǎn)品的 id 來(lái)顯示特定的 Product 對(duì)象:

public function showAction($productId){
    $product = $this->getDoctrine()
        ->getRepository('AppBundle:Product')
        ->find($productId);     if (!$product) {
        throw $this->createNotFoundException(
            'No product found for id '.$productId
        );
    }     // ... do something, like pass the $product object into a template
    // ... 做一些事,比如把 $product 對(duì)象傳入模板}

你可以使用 @ParamConverter 快捷注釋?zhuān)沩毦帉?xiě)任何代碼即可實(shí)現(xiàn)同樣的功能。參考 FrameworkExtraBundle 以了解更多。

當(dāng)你要查詢某個(gè)特定類(lèi)型的對(duì)象時(shí),你總是要使用它的”respository”(寶庫(kù))。你可以認(rèn)為Respository是一個(gè)PHP類(lèi),它的唯一工作就是幫助你從那個(gè)特定的類(lèi)中取出entity。對(duì)于一個(gè)entity類(lèi),要訪問(wèn)其寶庫(kù),通過(guò):

$repository = $this->getDoctrine()
    ->getRepository('AppBundle:Product');

appBundle:Product 是快捷寫(xiě)法,你可以在Doctrine里隨處使用,以替代entity類(lèi)的FQCN類(lèi)名(如 AppBundle\Entity\Product )。只要你的entity存放在bundle的 Entity 命名空間下,它就會(huì)工作。

一旦有了Repository對(duì)象,你就可以訪問(wèn)它的全部有用的方法了。

$repository = $this->getDoctrine()->getRepository('AppBundle:Product');
// query for a single product by its primary key (usually "id")// 通過(guò)主鍵(通常是id)查詢一件產(chǎn)品
$product = $repository->find($productId); // dynamic method names to find a single product based on a column value// 動(dòng)態(tài)方法名稱(chēng),基于字段的值來(lái)找到一件產(chǎn)品$product = $repository->findOneById($productId);$product = $repository->findOneByName('Keyboard');
// dynamic method names to find a group of products based on a column value
// 動(dòng)態(tài)方法名稱(chēng),基于字段值來(lái)找出一組產(chǎn)品$products = $repository->findByPrice(19.99); 
// find *all* products / 查出 *全部* 產(chǎn)品$products = $repository->findAll();

當(dāng)然,你也可以使用復(fù)雜的查詢,參考 對(duì)象查詢 小節(jié) 。

你也可以有效利用 findByfindOneBy 方法,基于多個(gè)條件來(lái)輕松獲取對(duì)象:

$repository = $this->getDoctrine()->getRepository('AppBundle:Product'); // query for a single product matching the given name and price// 查詢一件產(chǎn)品,要匹配給定的名稱(chēng)和價(jià)格$product = $repository->findOneBy(
    array('name' => 'Keyboard', 'price' => 19.99)); // query for multiple products matching the given name, ordered by price// 查詢多件產(chǎn)品,要匹配給定的名稱(chēng)和價(jià)格$products = $repository->findBy(
    array('name' => 'Keyboard'),
    array('price' => 'ASC'));

渲染任何頁(yè)面時(shí),你可以在除錯(cuò)工具條(web debug toolbar)的右下角看到許多查詢。

1466160351_18163_67861_doctrine_web_debug_toolbar (1).png

如果你點(diǎn)擊圖標(biāo),分析器(profiler)將會(huì)打開(kāi),顯示出所產(chǎn)生的精確查詢。

如果你的頁(yè)面查詢超過(guò)了50個(gè),圖標(biāo)會(huì)變成黃色。這表明某些地方不大對(duì)勁。

對(duì)象更新 ?

一旦從Doctrine中獲取了一個(gè)對(duì)象,更新它就很容易了。假設(shè)你有一個(gè)路由,把一個(gè)產(chǎn)品id映射到controller的updateaction:

public function updateAction($productId){
    $em = $this->getDoctrine()->getManager();
    $product = $em->getRepository('AppBundle:Product')->find($productId);     if (!$product) {
        throw $this->createNotFoundException(
            'No product found for id '.$productId
        );
    }     $product->setName('New product name!');
    $em->flush();     return $this->redirectToRoute('homepage');}

更新一個(gè)對(duì)象包括三步:

  1. 從Doctrine中取出對(duì)象;
  2. 修改對(duì)象;
  3. 調(diào)用entity manager的 flush() 方法。

注意調(diào)用 $em->persist($product) 是不必要的。回想一下,這個(gè)方法只是告訴Doctrine去管理或者“觀察” $product 對(duì)象。此處,因?yàn)槟阋呀?jīng)取到了 $product 對(duì)象了,它已經(jīng)被管理了。

刪除對(duì)象 ?

刪除一個(gè)對(duì)象十分類(lèi)似,但需要從entity manager調(diào)用 remove() 方法:

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

你可能已經(jīng)預(yù)期,remove() 方法通知Doctrine你想從數(shù)據(jù)庫(kù)中刪除指定的entity。真正的 DELETE 查詢不會(huì)被真正執(zhí)行,直到 flush() 方法被調(diào)用。

對(duì)象查詢 ?

你已經(jīng)看到repository對(duì)象是如何讓你執(zhí)行一些基本查詢而毋須做任何工作了:

$repository = $this->getDoctrine()->getRepository('AppBundle:Product'); $product = $repository->find($productId);$product = $repository->findOneByName('Keyboard');

當(dāng)然,Doctrine 也允許你使用Doctrine Query Language(DQL)來(lái)寫(xiě)一些復(fù)雜的查詢,DQL類(lèi)似于SQL,只是它用于查詢一個(gè)或者多個(gè)entity類(lèi)的對(duì)象(如 product),而SQL則是查詢一個(gè)數(shù)據(jù)表中的行(如 product )。

在Doctrine中查詢時(shí),你有兩個(gè)主要選擇:編寫(xiě)純正的Doctrine查詢(DQL) 或者 使用Doctrine的Query Builder。

使用DQL進(jìn)行對(duì)象查詢 ?

假設(shè)你要查詢價(jià)格高于 19.99 的產(chǎn)品,并且按價(jià)格從低到高排列。你可以使用DQL,Doctrine中類(lèi)似原生SQL的語(yǔ)法,來(lái)構(gòu)造一個(gè)用于此場(chǎng)景的查詢:

$em = $this->getDoctrine()->getManager();$query = $em->createQuery(
    'SELECT p
    FROM AppBundle:Product p
    WHERE p.price > :price
    ORDER BY p.price ASC')->setParameter('price', 19.99); $products = $query->getResult();

如果你習(xí)慣了寫(xiě)SQL,那么對(duì)于DQL也會(huì)非常自然。它們之間最大的不同就是你需要就“select PHP對(duì)象”來(lái)進(jìn)行思考,而不是數(shù)據(jù)表的行。正因?yàn)槿绱?,你? AppBundle:Product 這個(gè) entity (可選的一個(gè)AppBundle\Entity\Product 類(lèi)的快捷寫(xiě)法)來(lái)select,然后給entity一個(gè) p 的別名。

注意 setParameter() 方法。當(dāng)使用Doctrine時(shí),通過(guò)“占位符”來(lái)設(shè)置任意的外部值(上面例子的 :price),是一個(gè)好辦法,因?yàn)樗梢苑乐筍QL注入攻擊。

getResult() 方法返回一個(gè)結(jié)果數(shù)組。要得到一個(gè)結(jié)果,可以使用getSingleResult()(這個(gè)方法在沒(méi)有結(jié)果時(shí)會(huì)拋出一個(gè)異常)或者 getOneOrNullResult()

$product = $query->setMaxResults(1)->getOneOrNullResult();

DQL語(yǔ)法強(qiáng)大到令人難以置信,允許輕松地在entity之間進(jìn)行join(稍后會(huì)覆蓋relations)和group等。參考 Doctrine Query Language 文檔以了解更多。

使用Doctrine's Query Builder進(jìn)行對(duì)象查詢 ?

不去寫(xiě)DQL的大字符串,你可以使用一個(gè)非常有用的QueryBuilder對(duì)象,來(lái)構(gòu)建那個(gè)字符串。當(dāng)你的查詢?nèi)Q于動(dòng)態(tài)條件時(shí),這很有用,因?yàn)殡S著你的連接字符串不斷增加,DQL代碼會(huì)越來(lái)越難以閱讀:

$repository = $this->getDoctrine()
    ->getRepository('AppBundle:Product'); // createQueryBuilder() automatically selects FROM AppBundle:Product// and aliases it to "p"// createQueryBuilder() 自動(dòng)從 AppBundle:Product 進(jìn)行 select 并賦予 p 假名$query = $repository->createQueryBuilder('p')
    ->where('p.price > :price')
    ->setParameter('price', '19.99')
    ->orderBy('p.price', 'ASC')
    ->getQuery(); $products = $query->getResult();// to get just one result: / 要得到一個(gè)結(jié)果:// $product = $query->setMaxResults(1)->getOneOrNullResult();

QueryBuilder對(duì)象包含了創(chuàng)建查詢時(shí)的所有必要方法。通過(guò)調(diào)用getQuery()方法,query builder將返回一個(gè)標(biāo)準(zhǔn)的Query對(duì)象,可用于取得請(qǐng)求的結(jié)果集。

Query Builder更多信息,參考Doctrine的 Query Builder 文檔。

把自定義查詢組織到Repository類(lèi)中 ?

前面所有的查詢是直接寫(xiě)在你的控制器中的。但對(duì)于程序的組織來(lái)說(shuō),Doctrine提供了一個(gè)專(zhuān)門(mén)的repository類(lèi),它允許你保存所有查詢邏輯到一個(gè)中心位置。

參考 如何創(chuàng)建自定義Repository類(lèi) 以了解更多。

配置 ?

Doctrine是高度可配置的,雖然你可能永遠(yuǎn)不會(huì)去關(guān)心那些選項(xiàng)。要了解Doctrine的配置信息,參考 config reference。

Doctrine字段類(lèi)型參考 ?

Doctrine配備了大量可用的字段類(lèi)型。每一個(gè)都能把PHP數(shù)據(jù)類(lèi)型映射到特定的字段類(lèi)型中,無(wú)論你使用什么數(shù)據(jù)庫(kù)。對(duì)于每一個(gè)字段類(lèi)型, Column 都可以被進(jìn)一步配置,可以設(shè)置 length、nullable 行為,name 或者其他選項(xiàng)??捎米侄晤?lèi)型的列表,參考 Mapping Types documentation。

Associations(關(guān)系) 和 Relations(關(guān)聯(lián)) ?

Doctrine 提供了你所需要的管理數(shù)據(jù)庫(kù)關(guān)系(也被稱(chēng)為關(guān)聯(lián)-associations)的所有的功能。更多信息,參考 如何使用Doctrine Associations / Relations。

總結(jié) ?

有了Doctrine,你可以集中精力到你的 對(duì)象 以及 如何把它應(yīng)用到程序中,而數(shù)據(jù)庫(kù)持久化則是第二位。這是因?yàn)镈octrine允許你使用任何的PHP對(duì)象來(lái)保存你的數(shù)據(jù),并且依靠“元數(shù)據(jù)映射”信息來(lái)把一個(gè)對(duì)象的數(shù)據(jù)映射到一個(gè)特定的數(shù)據(jù)表之中。

Doctrine有很多強(qiáng)大的功能等著你去學(xué)習(xí),像是relationships(關(guān)聯(lián)),復(fù)雜查詢和事件監(jiān)聽(tīng)。