Pangkalan Data dan ORM Doktrin


Salah satu tugas yang paling biasa dan mencabar untuk mana-mana aplikasi ialah membaca dan mengekalkan maklumat data daripada pangkalan data. Walaupun rangka kerja symfony tidak menyepadukan mana-mana komponen yang memerlukan penggunaan pangkalan data, ia disepadukan rapat dengan perpustakaan kelas pihak ketiga yang dipanggil Doctrine. Matlamat utama Doctrine adalah untuk menyediakan anda alat berkuasa yang menjadikan interaksi pangkalan data lebih mudah dan lebih fleksibel.

Dalam bab ini, anda akan belajar cara menggunakan doktrin untuk menyediakan interaksi pangkalan data yang kaya dalam projek Symfony.

Doktrin dan simfoni dipisahkan sepenuhnya, dan sama ada untuk menggunakannya atau tidak adalah pilihan. Bab ini adalah mengenai Doktrin ORM, yang bertujuan untuk membolehkan anda memetakan objek ke pangkalan data hubungan (seperti MySQL, PostgreSQL dan Microsoft SQL). Jika anda lebih suka menggunakan pertanyaan mentah pangkalan data, ini sangat mudah, anda boleh rujuk artikel Cara menggunakan Doktrin DBAL.

Anda juga boleh menggunakan perpustakaan kelas Doctrine ODM untuk mengekalkan data ke MongoDB. Rujuk DoctrineMongoDBBundle untuk maklumat lanjut.

Contoh mudah: produk

Untuk memahami cara Doktrin berfungsi, cara paling mudah ialah melihat aplikasi praktikal. Dalam bahagian ini, anda perlu mengkonfigurasi pangkalan data anda, mencipta objek Product, mengekalkannya ke pangkalan data, dan kemudian mendapatkannya. Product 对象,把它持久化到数据库,再取回它。

配置数据库 

真正开始之前,你需要配置你的数据库连接信息。按照惯例,这部分信息通常配置在 app/config/parameters.yml 文件中:

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


🎜 Tentukan konfigurasi melalui parameters.yml< /code> , hanya konvensyen. Apabila mengkonfigurasi Doktrin, parameter yang ditakrifkan dalam fail itu akan dirujuk oleh fail konfigurasi utama: 🎜
<!-- 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%',
    ),));
$  php bin/console doctrine:database:create
🎜 Dengan mengasingkan maklumat pangkalan data ke dalam fail berasingan, anda boleh menyimpan versi berbeza untuk setiap pelayan dengan mudah. Anda juga boleh menyimpan konfigurasi pangkalan data (atau sebarang maklumat sensitif) dengan mudah di luar projek, contohnya, sama seperti maklumat konfigurasi dalam Apache. Rujuk 🎜Cara menetapkan parameter luaran bekas perkhidmatan🎜 untuk mengetahui lebih lanjut. 🎜🎜

Kini Doktrin boleh menyambung ke pangkalan data anda, arahan berikut boleh menjana pangkalan data projek_ujian kosong: test_project 数据库:

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

设置数据库为UTF8

Symfony项目开始后,老练的程序员也会犯的一个错误是,忘了设置他们的数据库默认字符集和校对规则(charset and collation),最终变成latin类型,也就是多数数据库默认的。他们也许在第一次操作时会记得,但在后面的开发中敲打两行相关的常用命令之后就完全忘掉了:

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

设置UTF8为MySQL的默认字符集简单到只要在配置文件(一般是my.cnf文件)中加几行代码就可以了:

# app/config/config.ymldoctrine:
    dbal:
        charset: utf8mb4
        default_table_options:
            charset: utf8mb4
            collate: utf8mb4_unicode_ci

你还可以改变Doctrine的默认字符集,以便生成的SQL使用设置正确的字符集。

<!-- 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'
        )
    ),));
# app/config/config.ymldoctrine:
    dbal:
        driver: pdo_sqlite
        path: "%kernel.root_dir%/sqlite.db"
        charset: UTF8

我们推荐避免使用Mysql的 uft8 字符集,因为它并不兼容4-byte unicode字符,如果字符串中有这种字符会被清空。不过这种情况被修复了,参考 新型utf8mb4字符集

如果你要用SQLite作为数据库,在path选项中设置你的数据库路径:

<!-- 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',
    ),));
// src/AppBundle/Entity/Product.phpnamespace AppBundle\Entity; class Product{
    private $name;
    private $price;
    private $description;}

创建一个Entity类 

假设你正构建一套程序,其中有些产品需要展示。即使不考虑Doctrine或者数据库,你也已经知道你需要一个 Product 对象来呈现这些产品。在你AppBundle的 Entity

$  php bin/console doctrine:generate:entity

# 🎜🎜#Tetapkan pangkalan data kepada UTF8

Selepas memulakan projek Symfony, kesilapan pengaturcara yang berpengalaman akan terlupa untuk menetapkan set aksara lalai pangkalan data dan peraturan pengumpulan (charset dan collation), akhirnya menjadi jenis latin, yang merupakan lalai kebanyakan pangkalan data. Mereka mungkin mengingatinya pada kali pertama mereka mengendalikannya, tetapi mereka lupa sepenuhnya selepas menaip dua baris arahan biasa yang berkaitan dalam pembangunan seterusnya:

// 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;
Menetapkan UTF8 sebagai set aksara lalai MySQL adalah semudah seperti Cuma tambahkan beberapa baris kod pada fail konfigurasi (biasanya fail my.cnf):
# 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

Anda juga boleh menukar set aksara lalai Doctrine supaya SQL yang dihasilkan menggunakan set aksara yang betul.
<!-- 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>
/**
 * @IgnoreAnnotation("fn")
 */class Product// ...
$  php bin/console doctrine:schema:validate
Kami mengesyorkan agar anda mengelakkan menggunakan set aksara uft8 Mysql kerana ia tidak serasi dengan aksara unicode 4-bait dan akan dikosongkan jika terdapat aksara sedemikian dalam rentetan. Walau bagaimanapun, keadaan ini telah dibetulkan, rujuk Aksara utf8mb4 baharu tetapkan .

Jika anda ingin menggunakan SQLite sebagai pangkalan data, tetapkan laluan pangkalan data anda dalam pilihan laluan: #🎜🎜#
$  php bin/console doctrine:generate:entities AppBundle/Entity/Product
# 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
$  php bin/console doctrine:schema:update --force
#🎜🎜##🎜🎜#

Buat kelas Entiti

#🎜🎜# Andaikan anda adalah Bina program di mana beberapa produk perlu dipamerkan. Walaupun tanpa memikirkan Doktrin atau pangkalan data, anda sudah tahu bahawa anda memerlukan objek Product untuk mewakili produk ini. Cipta kelas ini dalam direktori Entity AppBundle anda: #🎜🎜#
// 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你希望(最终)存储Product对象(还没有语句执行)
    $em->persist($product);     // actually executes the queries (i.e. the INSERT query)
    // 真正执行语句(如,INSERT 查询)
    $em->flush();     return new Response('Saved new product with id '.$product->getId());}
#🎜🎜#Kelas ini - selalunya dipanggil "Entiti", bermaksud #🎜🎜# yang menyimpan data Kelas asas #🎜 🎜# - Ia mudah dan memenuhi keperluan perniagaan produk yang diperlukan dalam program. Kelas ini tidak boleh disimpan ke pangkalan data lagi - ia hanyalah kelas PHP yang mudah. #🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜#Setelah anda mempelajari konsep di sebalik Doktrin, anda boleh membenarkan Doktrin mencipta kelas entiti untuk anda. Ia akan bertanya kepada anda beberapa soalan interaktif untuk membantu anda mencipta sebarang entiti: #🎜🎜#
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 对象传入模板}
#🎜🎜##🎜🎜#

Tambah maklumat pemetaan

Doktrin membolehkan anda menggunakan pangkalan data dengan cara yang lebih menarik dan bukannya hanya mengeluarkan baris data skalar ke dalam tatasusunan. Doktrin membolehkan anda mendapatkan semula keseluruhan objek daripada pangkalan data dan mengekalkan keseluruhan objek ke pangkalan data pada masa yang sama. Untuk Doktrin mencapai ini, anda mesti peta jadual data ke kelas PHP tertentu dan lajur jadual tersebut mesti dipetakan kepada sifat khusus kelas PHP yang sepadan.

1466153595_56497_19601_doctrine_image_1.png

Anda perlu menyediakan maklumat pemetaan ini dalam bentuk "data daging", dan terdapat satu set peraturan yang boleh memberitahu Doktrin dengan tepat Produk< /code> Bagaimana kelas dan sifatnya harus Product 类及其属性应该如何 映射到 一个特定的数据表。这个metadata可以通过不同的格式来指定,包括YAML,XML或者通过DocBlock注释(译注:annotations)直接定义到 Product 类中:

$repository = $this->getDoctrine()
    ->getRepository('AppBundle:Product');
$repository = $this->getDoctrine()->getRepository('AppBundle:Product');
// query for a single product by its primary key (usually "id")// 通过主键(通常是id)查询一件产品
$product = $repository->find($productId); // dynamic method names to find a single product based on a column value// 动态方法名称,基于字段的值来找到一件产品$product = $repository->findOneById($productId);$product = $repository->findOneByName('Keyboard');
// dynamic method names to find a group of products based on a column value
// 动态方法名称,基于字段值来找出一组产品$products = $repository->findByPrice(19.99); 
// find *all* products / 查出 *全部* 产品$products = $repository->findAll();
$repository = $this->getDoctrine()->getRepository('AppBundle:Product'); // query for a single product matching the given name and price// 查询一件产品,要匹配给定的名称和价格$product = $repository->findOneBy(
    array('name' => 'Keyboard', 'price' => 19.99)); // query for multiple products matching the given name, ordered by price// 查询多件产品,要匹配给定的名称和价格$products = $repository->findBy(
    array('name' => 'Keyboard'),
    array('price' => 'ASC'));

一个bundle只可以接受一种metadata的定义格式。比如,不能把YAML的metadata定义和添加了注释(annotation)的PHP entity类混用。

表名是可选的,如果省略,将自动取决于entity类的名称。

Doctrine允许你选择广泛的字段类型,每一种都有自己的配置。可用字段类型的信息,参考 Doctrine字段类型参考。

你也可以查看Doctrine官方文档 Basic Mapping Documentation 以了解关于映射的所有细节信息。如果你使用annotation,你需要为所有annotation加挂 ORM (例如  ORMColumn(...) ),这在Doctrine文档中并未写明。你还需要去包容 use DoctrineORMMapping as ORM; 声明,它可以 import(导入) ORM annotation前缀。

小心Entity类名(或者其属性)同时也是一个SQL保留的关键字(如 groupuser )。例如,如果你的entity类名称为 Group dipetakan ke

jadual data tertentu. Metadata ini boleh ditentukan dalam format yang berbeza, termasuk YAML, XML atau ditakrifkan secara langsung dalam kelas Product melalui anotasi DocBlock (Anotasi: anotasi):
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');}
$em->remove($product);$em->flush();
$repository = $this->getDoctrine()->getRepository('AppBundle:Product'); $product = $repository->find($productId);$product = $repository->findOneByName('Keyboard');
#🎜🎜#Sebuah himpunan hanya boleh menerima satu format definisi metadata. Sebagai contoh, anda tidak boleh mencampurkan definisi metadata YAML dan kelas entiti PHP dengan anotasi ditambahkan. #🎜🎜##🎜🎜##🎜🎜#
#🎜🎜#Nama jadual adalah pilihan, jika ditinggalkan, akan secara automatik bergantung pada nama kelas entiti. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#Doktrin membolehkan anda memilih daripada pelbagai jenis medan, masing-masing dengan konfigurasinya sendiri. Untuk maklumat tentang jenis medan yang tersedia, lihat Rujukan Jenis Medan Doktrin. #🎜🎜#
#🎜🎜#Anda juga boleh menyemak dokumen rasmi Doktrin Dokumentasi Pemetaan Asas untuk mengetahui semua butiran tentang maklumat pemetaan. Jika anda menggunakan anotasi, anda perlu melampirkan ORM (seperti ORMColumn(...)) pada semua anotasi, yang tidak dinyatakan dalam dokumentasi Doktrin. Anda juga perlu memasukkan pernyataan use DoctrineORMMapping sebagai ORM;, yang boleh #🎜🎜#import (import) #🎜🎜# ORM awalan anotasi. #🎜🎜##🎜🎜##🎜🎜#
#🎜🎜#Berhati-hati dengan nama kelas Entiti (atau atributnya) Ia juga merupakan kata kunci simpanan SQL (seperti group dan user). Contohnya, jika nama kelas entiti anda ialah Kumpulan, maka secara lalai, nama jadual anda akan menjadi kumpulan, yang boleh menyebabkan ralat SQL dalam sesetengah enjin pangkalan data. Rujuk dokumentasi kata kunci SQL Terpelihara untuk mengetahui cara memintas nama ini dengan betul. Secara pilihan, anda boleh sewenang-wenangnya memilih skema pangkalan data dan dengan mudah memetakannya kepada nama jadual atau nama lajur yang berbeza. Lihat Mencipta Kelas untuk dokumentasi Pangkalan Data dan Pemetaan Harta. #🎜🎜##🎜🎜##🎜🎜#

Apabila menggunakan beberapa perpustakaan kelas atau program lain yang "menggunakan anotasi" (seperti Doxygen), anda harus menambahkan anotasi @IgnoreAnnotation pada kelas untuk mengarahkan Symfony anotasi yang harus diabaikan. @IgnoreAnnotation 注释添加到类中,来指示Symfony应该忽略哪个annotation。

例如,要避免 @fn annotation抛出异常,添加下列注释:

$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();

创建entity之后,你应该使用以下命令来验证映射(mappings):

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

生成Getters和Setters 

尽管Doctrine现在知道了如何持久化 Product 对象到数据库,但是类本身还不具备真正用途。因为 Product 仅仅是一个带有 private 属性的常规PHP类,你需要创建 public 的getter和setter方法(比如 getName() , setName($name) )以便在程序其他部分来访问它的属性(其属性是protected)。幸运的是,下面的命令可以自动生成这些模板化的方法:

$repository = $this->getDoctrine()
    ->getRepository('AppBundle:Product'); // createQueryBuilder() automatically selects FROM AppBundle:Product// and aliases it to "p"// createQueryBuilder() 自动从 AppBundle:Product 进行 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: / 要得到一个结果:// $product = $query->setMaxResults(1)->getOneOrNullResult();

该命令可以确保 Product 类所有的getter和setter都被生成。这是一个安全的命令行——你可以多次运行它,它只会生成那些不存在的getters和setters(即,不会替换已有的方法)。

重要提示下面这句话极其深刻,乃是活用Doctrine的关键。大家一定照做。

记得,doctrine entity generator生成的是简单的getters/setters。你应该复审那些已生成的方法,在必要时,添加逻辑进去,以满足你的程序之需求。

关于 doctrine:generate:entities 的更多内容

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

  • 在entity类中生成getters和setters;

  • 在entity类配置了 @ORMEntity(repositoryClass=”…”) annotation的情况下生成所对应的repository类;

  • 为 1:n 或 n:m 生成合适的构造器。

doctrine:generate:entities 命令会保存原始 Product.php 文件的备份并命名为 Product.php~ 。 有些时候这个文件可能会引发“Cannot redeclare class”错误。它可以被安全删除。你还可以使用 –no-backup

Sebagai contoh, untuk mengelakkan anotasi @fn daripada membuang pengecualian, tambahkan anotasi berikut:

rrreee

Selepas mencipta entiti, anda harus menggunakan arahan berikut Untuk mengesahkan pemetaan:
rrreee🎜🎜

Jana Getters dan Setters

🎜 Walaupun Doctrine kini Kami tahu bagaimana untuk mengekalkan objek Product ke pangkalan data, tetapi kelas itu sendiri tidak mempunyai tujuan sebenar lagi. Memandangkan Product hanyalah kelas PHP biasa dengan sifat private, anda perlu mencipta kaedah getter dan setter public (seperti getName ( ), setName($name)) untuk mengakses sifatnya (sifatnya dilindungi) di bahagian lain program. Mujurlah, arahan berikut boleh menjana kaedah templat ini secara automatik: 🎜rrreee🎜 Perintah ini memastikan semua getter dan setter kelas Product dijana. Ini adalah baris arahan yang selamat - anda boleh menjalankannya beberapa kali dan ia hanya akan menjana getter dan setter yang tidak wujud (iaitu ia tidak akan menggantikan kaedah sedia ada). 🎜🎜
🎜Petua Penting Ayat berikut sangat mendalam dan merupakan kunci untuk menggunakan sepenuhnya daripada Doktrin. Semua orang mesti melakukannya. 🎜🎜🎜Ingat, penjana entiti doktrin menjana getter/penetap mudah. Anda harus menyemak kaedah yang dijana dan, jika perlu, tambah logik untuk memenuhi keperluan aplikasi anda. 🎜🎜🎜🎜

🎜Lebih lanjut mengenai doktrin:generate:entities🎜

🎜Gunakan doctrine:generate:entities untuk memerintahkan anda Ya: 🎜
  • 🎜Janakan getter dan setter dalam kelas entiti 🎜
  • 🎜Konfigurasikan anotasi @ORMEntity(repositoryClass="…") dalam kelas entiti Generate; kelas repositori yang sepadan dalam kes 🎜
  • 🎜 Hasilkan pembina yang sesuai untuk 1:n atau n:m. 🎜
🎜 doctrine:generate:entities arahan akan menyimpan sandaran fail Product.php asal dan menamakannya Product.php ~< /kod> . Kadangkala fail ini boleh menyebabkan ralat "Tidak dapat mengisytiharkan semula kelas". Ia boleh dikeluarkan dengan selamat. Anda juga boleh menggunakan pilihan –no-backup untuk menghalang fail sandaran ini daripada dijana. 🎜🎜Perhatikan bahawa anda tidak 🎜memerlukan🎜 (bergantung pada) arahan ini. Anda juga boleh menulis getter dan setter dengan tangan. Pilihan ini wujud hanya untuk menjimatkan masa anda kerana mencipta kaedah ini adalah tugas biasa semasa pembangunan. 🎜🎜🎜

Anda juga boleh menjana getter dan setter untuk semua entiti yang diketahui dalam satu bundle atau ruang nama entiti (mana-mana kelas PHP yang mengandungi maklumat pemetaan Doktrin):

rrreee

Buat jadual/Skema data

Sekarang anda mempunyai < kod>Produk kelas yang mengandungi maklumat pemetaan, jadi Doktrin tahu dengan tepat cara untuk mengekalkannya. Sudah tentu, anda belum mempunyai jadual data product yang sepadan dalam pustaka. Nasib baik, Doctrine boleh mencipta semua jadual data secara automatik. Untuk melakukan ini, jalankan arahan berikut: Product 类,因此Doctrine确切地知道如何持久化它。当然,你还没有相应的 product 数据表在库中。幸运的是,Doctrine可以自动创建所有的数据表。要这么做,运行以下命令:

rrreee

说真的,这条命令出奇的强大。它会比较你的数据库 理论上应该是 什么样子的(基于你的entities的映射信息)以及 实际上 它应该是什么样,然后执行所需的SQl语句来将数据库的schema 更新到 它所应有的样子。换句话说,如果你添加了一个包含“映射元数据”(mapping metadata)的新属性到 Product 并运行此任务,它将执行所需的 "ALTER TABLE" 语句,向已经存在的 product 表添加那个新列。

一个利用此功能之优势的更佳方式是通过 migrations,它允许你生成这些SQL语句,并把它们并存储到migration类中,这些类能够有序运行在你的生产环境中,进而安全可靠地更新和追踪数据库的schema改变。

不管你是否利用了数据库迁移,doctrine:schema:update 命令只适合在开发环境中使用。它不应该被用于生产环境。

现在你的数据库中有了一个全功能的product表,它的列与你指定的元数据相匹配。

持久化对象到数据库 

现在你有了一个Product实体和与之映射的product数据库表。你可以把数据持久化到数据库里。在Controller内,它非常简单。添加下面的方法到bundle的DefaultController中。

现在你已经把 Product entity 映射到与之对应的 product 表中,你已经准备好把 Product 对象持久化到数据库中。在控制器里面,这极其简单。向bundle的 DefaultControllerrrreee

Serius, arahan ini sangat berkuasa. Ia membandingkan rupa pangkalan data anda dalam teori (berdasarkan maklumat pemetaan entiti anda) dengan apa yang sepatutnya kelihatan seperti dalam amalan, dan kemudian melaksanakan pernyataan SQl yang diperlukan untuk mengemas kini skema pangkalan data kepada perkara itu. sepatutnya. Dalam erti kata lain, jika anda menambah sifat baharu yang mengandungi "metadata pemetaan" pada Produk dan menjalankan tugas ini, ia akan melaksanakan penyataan "ALTER TABLE" yang diperlukan pada lajur yang sudah Tambah itu pada < yang sedia ada. kod>produk
jadual. 🎜 Tidak kira sama ada anda menggunakan migrasi pangkalan data atau tidak, arahan doctrine:schema:update hanya sesuai untuk digunakan dalam persekitaran pembangunan. Ia tidak boleh digunakan dalam persekitaran pengeluaran. 🎜🎜🎜🎜Anda kini mempunyai jadual produk berfungsi sepenuhnya dalam pangkalan data anda dengan lajur yang sepadan dengan metadata yang anda tentukan. 🎜🎜Objek berterusan ke pangkalan data ¶🎜🎜🎜Kini anda mempunyai entiti Produk dan jadual pangkalan data produk dipetakan padanya. Anda boleh menyimpan data ke dalam pangkalan data. Di dalam Pengawal, ia sangat mudah. Tambahkan kaedah berikut pada DefaultController bundle. 🎜🎜Sekarang anda telah memetakan entiti Produk ke jadual product yang sepadan, anda sudah bersedia untuk Produk dikekalkan ke pangkalan data. Di dalam pengawal, ini sangat mudah. Tambahkan kaedah berikut pada DefaultController himpunan: 🎜rrreee🎜🎜Jika anda mengikuti contoh ini, anda perlu mencipta laluan dan menghalakannya ke tindakan ini untuk melihatnya berjalan. 🎜🎜

Contoh ini menunjukkan penggunaan kaedah getDoctrine() Doctrine dalam pengawal. Ini ialah cara cepat untuk mengeluarkan perkhidmatan doctrine. Jika anda menyuntik perkhidmatan ini ke dalam perkhidmatan anda, anda boleh menggunakan doktrin di mana-mana sahaja. Rujuk Bekas Perkhidmatandoctrine 服务的快捷方法。若你在服务中注入此服务,即可在任意地方使用doctrine。参考 服务容器 以了解更多创建服务之内容。

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

  • 10-13行 在此处实例化,并且像其他常规PHP对象一样去使用 $product 对象。
  • 15行 这一行取出了Doctrine的 entity manager 对象,它负责处理数据库的持久化(译注:写入)和取出对象的过程。
  • 18行 persist($product) 调用,告诉Doctrine去 "管理" $product 对象。它 没有 引发对数据库的请求。
  • 21行flush() 方法被调用时,Doctrine会遍历它管理的所有对象以确定是否需要被持久化到数据库。本例中, $product 对象的数据在库中并不存在,因此entity manager要执行 INSERT 请求,在 product 表中创建一个新行。

事实上,由于Doctrine了解你的全部被管理的实体,当你调用 flush() 方法时,它会计算出所有的变更集合(changeset),并按正确顺序执行语句。它利用准备好的缓存语句以略微提高性能。比如,你要持久化总数为100的 Product 对象,然后调用 flush() 方法,Doctrine将用一个单一的prepare语法对象,来执行100次 INSERT 请求。

如果 flush() 调用失败,一个 DoctrineORMORMException untuk mengetahui lebih lanjut tentang membuat perkhidmatan.

Mari analisa contoh sebelumnya secara mendalam:
  • Baris 10-13 Instantiate di sini dan gunakannya seperti mana-mana objek PHP biasa< code class= "notranslate">$product objek.
  • Baris 15 Baris ini mengeluarkan objek pengurus entiti Doctrine, yang bertanggungjawab untuk mengendalikan kegigihan pangkalan data (Anotasi: menulis) dan mendapatkan semula objek.
  • Baris 18 persist($product) dipanggil, memberitahu Doktrin untuk "mengurus" $ produk objek. Ia ada mencetuskan permintaan kepada pangkalan data.
  • Baris 21 Apabila kaedah flush() dipanggil, Doktrin akan merentasi semua objek yang diuruskannya untuk menentukan sama ada ia memerlukan menjadi Kegigihan kepada pangkalan data. Dalam contoh ini, data objek $product tidak wujud dalam pustaka, jadi pengurus entiti perlu melaksanakan INSERT permintaan, dalam < Cipta baris baharu dalam jadual kod class="notranslate">product.
🎜
🎜Malah, memandangkan Doctrine mengetahui semua entiti terurus anda, apabila anda memanggil kaedah flush() , ia akan mengira semua set perubahan (set perubahan) dan melaksanakan pernyataan dalam susunan yang betul. Ia menggunakan kenyataan cache yang disediakan untuk meningkatkan sedikit prestasi. Sebagai contoh, jika anda ingin mengekalkan sejumlah 100 objek Product, dan kemudian memanggil kaedah flush(), Doktrin akan menggunakan objek sintaks penyediaan tunggal untuk melaksanakan INSERT permintaan. 🎜🎜🎜🎜

Jika panggilan flush() gagal, satu DoctrineORMORMException akan dilemparkan. Lihat 🎜Transaksi dan Keselarasan🎜. 🎜🎜🎜

Aliran kerja adalah sama semasa mencipta dan mengemas kini objek. Dalam bahagian seterusnya anda akan melihat bagaimana Doctrine mengeluarkan kenyataan Update secara automatik jika rekod sudah wujud dalam pangkalan data. Update 语句的。

Doctrine提供了一个类库,允许你程序化地加载测试数据到你的项目中(即,"fixture data",固定的数据)。参考 DoctrineFixturesBundle 以了解更多。

从数据库中获取对象 

从数据库中取回对象就更简单了,举个例子,假如你配置了一个路由,基于产品的 id 来显示特定的 Product 对象:

rrreee

你可以使用 @ParamConverter 快捷注释,毋须编写任何代码即可实现同样的功能。参考 FrameworkExtraBundle 以了解更多。

当你要查询某个特定类型的对象时,你总是要使用它的”respository”(宝库)。你可以认为Respository是一个PHP类,它的唯一工作就是帮助你从那个特定的类中取出entity。对于一个entity类,要访问其宝库,通过:

rrreee

appBundle:Product 是快捷写法,你可以在Doctrine里随处使用,以替代entity类的FQCN类名(如 AppBundleEntityProduct )。只要你的entity存放在bundle的 Entity 命名空间下,它就会工作。

一旦有了Repository对象,你就可以访问它的全部有用的方法了。

rrreee

当然,你也可以使用复杂的查询,参考 对象查询 小节 。

你也可以有效利用 findByfindOneBy

Doctrine menyediakan perpustakaan kelas yang membolehkan anda memuatkan data ujian secara pemrograman ke dalam projek anda (iaitu, "data lekapan", data tetap). Lihat DoctrineFixturesBundle untuk mengetahui lebih lanjut.

Mendapatkan objek daripada pangkalan data

1466160351_18163_67861_doctrine_web_debug_toolbar (1).pngMendapatkan objek daripada pangkalan data adalah lebih mudah, contohnya , jika anda mengkonfigurasi laluan untuk memaparkan objek Product tertentu berdasarkan id produk:

rrreee

Anda boleh menggunakan @ParamConverter< /code> Ulasan pantas , anda boleh mencapai fungsi yang sama tanpa menulis sebarang kod. Rujuk FrameworkExtraBundle untuk mengetahui lebih lanjut.

Apabila anda ingin menanyakan jenis objek tertentu, anda sentiasa perlu menggunakan "repositori"nya. Anda boleh menganggap Repositori sebagai kelas PHP yang tugasnya hanya untuk membantu anda mengeluarkan entiti daripada kelas tertentu itu. Untuk kelas entiti, untuk mengakses rumah harta karunnya, lalukan:
rrreee
🎜appBundle:Product ialah kaedah pintasan yang boleh anda gunakan di mana-mana sahaja dalam Doktrin untuk menggantikan Nama kelas FQCN bagi kelas entiti (seperti AppBundleEntityProduct). Selagi entiti anda disimpan dalam ruang nama Entiti himpunan, ia akan berfungsi. 🎜🎜🎜Sebaik sahaja anda mempunyai objek Repositori, anda mempunyai akses kepada semua kaedah bergunanya. 🎜rrreee🎜
🎜Sudah tentu, anda juga boleh menggunakan pertanyaan kompleks, rujuk Pertanyaan Objek bahagian . 🎜🎜🎜🎜Anda juga boleh menggunakan kaedah findBy dan findOneBy untuk mendapatkan objek dengan mudah berdasarkan pelbagai syarat: 🎜rrreee🎜🎜🎜Apabila memaparkan mana-mana halaman, anda boleh Anda boleh melihat banyak pertanyaan di sudut kanan bawah bar alat nyahpepijat web. 🎜🎜🎜🎜🎜Jika anda mengklik pada ikon, pemprofil akan terbuka menunjukkan pertanyaan tepat yang dihasilkan. 🎜🎜Jika pertanyaan halaman anda melebihi 50, ikon akan bertukar menjadi kuning. Ini menunjukkan bahawa ada sesuatu yang tidak betul. 🎜🎜🎜

Kemas Kini Objek

Sebaik sahaja anda mendapat objek daripada Doktrin, mengemas kininya adalah mudah. Katakan anda mempunyai laluan yang memetakan id produk kepada tindakan kemas kini pengawal:

rrreee

Mengemas kini objek melibatkan tiga langkah:

  1. Dapatkan semula objek daripada Doktrin
  2. Ubah suai objek
  3. 'Call pengurus; kaedah class="notranslate">flush()
    .
flush() 方法。

注意调用 $em->persist($product) 是不必要的。回想一下,这个方法只是告诉Doctrine去管理或者“观察” $product 对象。此处,因为你已经取到了 $product 对象了,它已经被管理了。

删除对象 

删除一个对象十分类似,但需要从entity manager调用 remove() 方法:

rrreee

你可能已经预期,remove() 方法通知Doctrine你想从数据库中删除指定的entity。真正的 DELETE 查询不会被真正执行,直到 flush() 方法被调用。

对象查询 

你已经看到repository对象是如何让你执行一些基本查询而毋须做任何工作了:

rrreee

当然,Doctrine 也允许你使用Doctrine Query Language(DQL)来写一些复杂的查询,DQL类似于SQL,只是它用于查询一个或者多个entity类的对象(如 product),而SQL则是查询一个数据表中的行(如 product )。

在Doctrine中查询时,你有两个主要选择:编写纯正的Doctrine查询(DQL) 或者 使用Doctrine的Query Builder。

使用DQL进行对象查询 

假设你要查询价格高于 19.99 的产品,并且按价格从低到高排列。你可以使用DQL,Doctrine中类似原生SQL的语法,来构造一个用于此场景的查询:

rrreee

如果你习惯了写SQL,那么对于DQL也会非常自然。它们之间最大的不同就是你需要就“select PHP对象”来进行思考,而不是数据表的行。正因为如此,你要 AppBundle:Product 这个 entity (可选的一个AppBundleEntityProduct 类的快捷写法)来select,然后给entity一个 p 的别名。

注意 setParameter() 方法。当使用Doctrine时,通过“占位符”来设置任意的外部值(上面例子的 :pricePerhatikan bahawa memanggil $em->persist($product) adalah tidak perlu. Ingat bahawa kaedah ini hanya memberitahu Doktrin untuk mengurus atau "memerhati" objek $product. Di sini, kerana anda telah memperoleh objek $product, objek itu sudah diuruskan.

Memadam objek
🎜🎜Memadam objek adalah sangat serupa, tetapi anda perlu memanggil remove() kaedah daripada pengurus entiti :🎜rrreee🎜 Seperti yang anda jangkakan, kaedah remove() memberitahu Doktrin bahawa anda ingin mengalih keluar entiti yang ditentukan daripada pangkalan data. Pertanyaan DELETE sebenar tidak akan dilaksanakan sehingga kaedah flush() dipanggil. 🎜🎜

Pertanyaan objek ¶🎜

🎜Anda telah melihat bagaimana objek repositori membenarkan anda melakukan beberapa pertanyaan asas tanpa perlu lakukan Sebarang kerja: 🎜rrreee🎜 Sudah tentu, Doctrine juga membenarkan anda menggunakan Doctrine Query Language (DQL) untuk menulis beberapa pertanyaan kompleks adalah serupa dengan SQL, kecuali ia digunakan untuk menanyakan satu atau lebih objek kelas entiti (seperti produk), manakala SQL query baris dalam jadual data (seperti product). 🎜🎜Apabila membuat pertanyaan dalam Doktrin, anda mempunyai dua pilihan utama: tulis pertanyaan Doktrin (DQL) tulen atau gunakan Pembina Pertanyaan Doktrin. 🎜🎜Gunakan DQL untuk pertanyaan objek ¶🎜🎜🎜Andaikan anda ingin menanyakan produk dengan harga yang lebih tinggi daripada 19.99, dan tekan Harga disusun dari terendah hingga tertinggi. Anda boleh menggunakan DQL, sintaks yang serupa dengan SQL asli dalam Doktrin, untuk membina pertanyaan untuk senario ini:🎜rrreee🎜Jika anda biasa menulis SQL, maka DQL juga akan menjadi sangat semula jadi. Perbezaan terbesar antara mereka ialah anda perlu berfikir dari segi "objek PHP pilih", bukan baris jadual data. Oleh sebab itu, anda mulakan entiti daripada AppBundle:Product (sebagai pilihan pintasan untuk kelas AppBundleEntityProduct ) ke pilih, dan kemudian berikan entiti alias p. 🎜🎜
🎜Beri perhatian kepada kaedah setParameter(). Apabila menggunakan Doktrin, adalah idea yang baik untuk menetapkan nilai luaran sewenang-wenangnya melalui "tempat letak" (:price dalam contoh di atas) kerana ia menghalang serangan suntikan SQL. 🎜🎜🎜Kaedah

getResult() mengembalikan tatasusunan hasil. Untuk mendapatkan hasil, anda boleh menggunakan getSingleResult() (kaedah ini akan membuang pengecualian apabila tiada hasil) atau getOneOrNullResult(): getResult() 方法返回一个结果数组。要得到一个结果,可以使用getSingleResult()(这个方法在没有结果时会抛出一个异常)或者 getOneOrNullResult()

rrreee

DQL语法强大到令人难以置信,允许轻松地在entity之间进行join(稍后会覆盖relations)和group等。参考 Doctrine Query Language 文档以了解更多。

使用Doctrine's Query Builder进行对象查询 

不去写DQL的大字符串,你可以使用一个非常有用的QueryBuilder对象,来构建那个字符串。当你的查询取决于动态条件时,这很有用,因为随着你的连接字符串不断增加,DQL代码会越来越难以阅读:

rrreee

QueryBuilder对象包含了创建查询时的所有必要方法。通过调用getQuery()方法,query builder将返回一个标准的Query对象,可用于取得请求的结果集。

Query Builder更多信息,参考Doctrine的 Query Builder 文档。

把自定义查询组织到Repository类中 

前面所有的查询是直接写在你的控制器中的。但对于程序的组织来说,Doctrine提供了一个专门的repository类,它允许你保存所有查询逻辑到一个中心位置。

参考 如何创建自定义Repository类 以了解更多。

配置 

Doctrine是高度可配置的,虽然你可能永远不会去关心那些选项。要了解Doctrine的配置信息,参考 config reference。

Doctrine字段类型参考 ¶

Doctrine配备了大量可用的字段类型。每一个都能把PHP数据类型映射到特定的字段类型中,无论你使用什么数据库。对于每一个字段类型, Column 都可以被进一步配置,可以设置 lengthnullable 行为,namerrreee

DQL syntax is so Hebatnya, ia membolehkan gabungan mudah antara entiti (dilindungi kemudian hubungan), kumpulan, dsb. Rujuk Bahasa Pertanyaan Doktrin dokumentasi untuk mengetahui lebih lanjut.

Gunakan Doctrine's Query Builder untuk pertanyaan objek

Jangan tulis rentetan besar DQL, anda Anda boleh menggunakan objek QueryBuilder yang sangat berguna untuk membina rentetan itu. Ini berguna apabila pertanyaan anda bergantung pada keadaan dinamik, kerana apabila rentetan sambungan anda terus berkembang, kod DQL menjadi semakin sukar dibaca:

rrreee

Objek QueryBuilder mengandungi semua kaedah yang diperlukan semasa membuat pertanyaan. Dengan memanggil kaedah getQuery(), pembina pertanyaan akan mengembalikan objek Pertanyaan standard yang boleh digunakan untuk mendapatkan set hasil yang diminta.
🎜🎜Pembina Pertanyaan Untuk maklumat lanjut, rujuk kepada Pembina Pertanyaan. 🎜

Atur pertanyaan tersuai ke dalam kelas Repositori

🎜Semua pertanyaan sebelumnya ditulis terus dalam pengawal anda . Tetapi untuk organisasi program, Doctrine menyediakan kelas repositori khusus yang membolehkan anda menyimpan semua logik pertanyaan ke lokasi pusat. 🎜🎜Rujuk Cara membuat kelas Repositori tersuai untuk mengetahui lebih lanjut. 🎜🎜Konfigurasi 🎜🎜Doktrin sangat boleh dikonfigurasikan, walaupun anda mungkin tidak mengambil berat tentang pilihan tersebut. Untuk maklumat konfigurasi Doktrin, lihat rujukan konfigurasi. 🎜🎜Rujukan Jenis Medan Doktrin ¶🎜🎜Doktrin datang dengan sejumlah besar jenis medan yang tersedia. Setiap satu boleh memetakan jenis data PHP kepada jenis medan tertentu, tidak kira pangkalan data yang anda gunakan. Untuk setiap jenis medan, Column boleh dikonfigurasikan lagi untuk menetapkan panjang, tingkah laku nullable, name atau Pilihan lain. Untuk senarai jenis medan yang tersedia, lihat dokumentasi Jenis Pemetaan. 🎜🎜Persatuan dan Perhubungan ¶🎜🎜Doktrin menyediakan semua fungsi yang anda perlukan untuk mengurus perhubungan pangkalan data (juga dikenali sebagai persatuan). Untuk maklumat lanjut, lihat Cara menggunakan Persatuan/Perhubungan Doktrin. 🎜

Ringkasan ¶

Dengan Doktrin, anda boleh fokus pada objek anda dan cara menerapkannya pada program anda, manakala ketekunan pangkalan data adalah langkah Kedua. Ini kerana Doktrin membenarkan anda menggunakan sebarang objek PHP untuk menyimpan data anda dan bergantung pada maklumat "pemetaan metadata" untuk memetakan data objek ke jadual data tertentu.

Doktrin mempunyai banyak ciri hebat yang menunggu untuk anda pelajari, seperti perhubungan, pertanyaan kompleks dan pemantauan acara.