ORM de base de données et de doctrine


L'une des tâches les plus courantes et les plus difficiles pour toute application consiste à lire et à conserver les informations sur les données de la base de données. Bien que le framework symfony n'intègre aucun composant nécessitant l'utilisation d'une base de données, il est étroitement intégré à une bibliothèque de classes tierce nommée Doctrine. L'objectif principal de Doctrine est de vous fournir un outil puissant qui rend l'interaction avec les bases de données plus facile et plus flexible.

Dans ce chapitre, vous apprendrez à utiliser la doctrine pour fournir une interaction riche avec les bases de données dans les projets Symfony.

Doctrine est complètement découplée de symfony, et son utilisation ou non est facultative. Ce chapitre est entièrement consacré à Doctrine ORM, qui vise à vous permettre de mapper des objets à des bases de données relationnelles (telles que MySQL, PostgreSQL et Microsoft SQL). Si vous préférez utiliser la requête brute de la base de données, c'est très simple, vous pouvez vous référer à l'article Comment utiliser Doctrine DBAL pour une explication.

Vous pouvez également utiliser la bibliothèque de classes Doctrine ODM pour conserver les données dans MongoDB. Reportez-vous à DoctrineMongoDBBundle pour plus d'informations.

Exemple simple : un produit (Product)

Pour comprendre le fonctionnement de la Doctrine, La manière la plus simple est de regarder une application pratique. Dans cette section, vous devez configurer votre base de données, créer un objet Product, le conserver dans la base de données, puis le récupérer. Product 对象,把它持久化到数据库,再取回它。

配置数据库 

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

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


#🎜🎜 #C'est juste une convention pour définir la configuration via parameters.yml. Lors de la configuration de Doctrine, les paramètres définis dans ce fichier seront référencés par le fichier de configuration principal : version. Vous pouvez également facilement stocker la configuration de la base de données (ou toute information sensible) en dehors du projet, par exemple, tout comme les informations de configuration dans Apache. Reportez-vous à #🎜🎜#Comment définir les paramètres externes du conteneur de service#🎜🎜# pour en savoir plus. #🎜🎜##🎜🎜#

Maintenant que Doctrine peut se connecter à votre base de données, la commande suivante peut générer automatiquement une base de données test_project vide : test_project 数据库:

<!-- 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>

设置数据库为UTF8

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

// 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%',
    ),));

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

$  php bin/console doctrine:database:create

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

$  php bin/console doctrine:database:drop --force
$  php bin/console doctrine:database:create
[mysqld]#Version 5.5.3 introduced "utf8mb4", which is recommendedcollation-server     = utf8mb4_general_ci # Replaces utf8_general_cicharacter-set-server = utf8mb4            # Replaces utf8
# app/config/config.ymldoctrine:
    dbal:
        charset: utf8mb4
        default_table_options:
            charset: utf8mb4
            collate: utf8mb4_unicode_ci

我们推荐避免使用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                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

创建一个Entity类 

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

<!-- 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>

# 🎜🎜#Définir la base de données sur UTF8

Après avoir démarré le projet Symfony, une erreur que feront les programmeurs expérimentés est d'oublier de définir le jeu de caractères par défaut de leur base de données et les règles de classement (jeu de caractères et classement), devient finalement le type latin, qui est la valeur par défaut de la plupart des bases de données. Ils s'en souviennent peut-être la première fois qu'ils l'utilisent, mais ils l'oublient complètement après avoir tapé deux lignes de commandes courantes liées lors du développement ultérieur :

// app/config/config.php$container->loadFromExtension('doctrine', array(
    'dbal' => array(
        'driver'  => 'pdo_sqlite',
        'path'    => '%kernel.root_dir%/sqlite.db',
        'charset' => 'UTF-8',
    ),));
Définir UTF8 comme jeu de caractères par défaut de MySQL est aussi simple que Ajoutez simplement quelques lignes de code au fichier de configuration (généralement le fichier my.cnf) :
// src/AppBundle/Entity/Product.phpnamespace AppBundle\Entity; class Product{
    private $name;
    private $price;
    private $description;}

Vous pouvez également modifier le jeu de caractères par défaut de Doctrine afin que le SQL généré utilise le jeu de caractères correct.
$  php bin/console doctrine:generate:entity
// 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
Nous vous recommandons d'éviter d'utiliser le jeu de caractères uft8 de Mysql car il n'est pas compatible avec les caractères Unicode de 4 octets et sera effacé s'il y a de tels caractères dans la chaîne. Cependant, cette situation a été corrigée, reportez-vous au Nouveau caractère utf8mb4 définir .

Si vous souhaitez utiliser SQLite comme base de données, définissez le chemin de votre base de données dans l'option path : #🎜🎜#
<!-- 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
#🎜🎜##🎜🎜#

Créer une classe d'entité

#🎜🎜# Supposons que vous sont Construisez un programme dans lequel certains produits doivent être présentés. Même sans penser à Doctrine ou à la base de données, vous savez déjà que vous avez besoin d'un objet Product pour représenter ces produits. Créez cette classe dans le répertoire Entity de votre AppBundle : #🎜🎜#
$  php bin/console doctrine:generate:entities AppBundle/Entity/Product
#🎜🎜#Cette classe - souvent appelée "Entité", signifie #🎜🎜# celle qui contient des données Classe de base #🎜 🎜# - C'est simple et répond aux besoins métiers des produits requis dans le programme. Cette classe ne peut pas encore être enregistrée dans la base de données - c'est juste une simple classe PHP. #🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜#Une fois que vous avez appris les concepts derrière Doctrine, vous pouvez laisser Doctrine créer des classes d'entités pour vous. Il vous posera quelques questions interactives pour vous aider à créer n'importe quelle entité : #🎜🎜#
# 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
#🎜🎜##🎜🎜#

Ajouter des informations de mappage

Doctrine vous permet d'utiliser la base de données d'une manière plus intéressante au lieu de simplement supprimer des lignes de données scalaires dans le tableau. Doctrine vous permet de récupérer l'intégralité de l' objet de la base de données et de conserver l'intégralité de l'objet dans la base de données en même temps. Pour que Doctrine y parvienne, vous devez mapper la table de données à une classe PHP spécifique, et les colonnes de ces tables doivent être mappées à des propriétés spécifiques de la classe PHP correspondante.

1466153595_56497_19601_doctrine_image_1.png

Vous devez fournir ces informations cartographiques sous la forme de « données de viande », et il existe un ensemble de règles qui peuvent indiquer avec précision la Doctrine Produit< /code> Comment une classe et ses propriétés doivent Product 类及其属性应该如何 映射到 一个特定的数据表。这个metadata可以通过不同的格式来指定,包括YAML,XML或者通过DocBlock注释(译注:annotations)直接定义到 Product 类中:

$  php bin/console doctrine:schema:update --force
// 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());}
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 对象传入模板}

一个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 être mappées à

une table de données spécifique. Ces métadonnées peuvent être spécifiées dans différents formats, notamment YAML, XML ou directement définies dans la classe Product via les annotations DocBlock (Annotation : annotations) :
$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'));
#🎜🎜#Un bundle ne peut accepter qu'un seul format de définition de métadonnées. Par exemple, vous ne pouvez pas mélanger des définitions de métadonnées YAML et des classes d'entités PHP avec des annotations ajoutées. #🎜🎜##🎜🎜##🎜🎜#
#🎜🎜#Le nom de la table est facultatif, s'il est omis, le sera dépendent automatiquement du nom de la classe d'entité. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#Doctrine vous permet de choisir parmi un large éventail de types de champs, chacun avec sa propre configuration. Pour plus d’informations sur les types de champs disponibles, consultez Référence des types de champs de doctrine. #🎜🎜#
#🎜🎜#Vous pouvez également consulter le document officiel de Doctrine Basic Mapping Documentation pour connaître tous les détails. sur les informations cartographiques. Si vous utilisez des annotations, vous devez attacher ORM (tel que ORMColumn(...)) à toutes les annotations, ce qui n'est pas indiqué dans la documentation Doctrine. Vous devez également inclure l'instruction use DoctrineORMMapping as ORM;, qui peut être le préfixe d'annotation #🎜🎜#import (import) #🎜🎜# ORM. #🎜🎜##🎜🎜##🎜🎜#
#🎜🎜#Soyez prudent avec le nom de la classe Entity (ou ses attributs) C'est aussi un mot-clé réservé SQL (tel que group et user). Par exemple, si le nom de votre classe d'entité est Group, alors par défaut, le nom de votre table sera group, ce qui peut provoquer des erreurs SQL dans certains moteurs de base de données. Reportez-vous à la documentation sur les mots clés SQL réservés pour savoir comment contourner correctement ces noms. En option, vous pouvez choisir arbitrairement le schéma de base de données et le mapper facilement à différents noms de table ou de colonne. Consultez la documentation Création de classes pour la base de données et le mappage de propriétés. #🎜🎜##🎜🎜##🎜🎜#

Lorsque vous utilisez d'autres bibliothèques de classes ou programmes qui "utilisent des annotations" (comme Doxygen), vous devez ajouter l'annotation @IgnoreAnnotation à la classe, pour demander Symfony quelle annotation doit être ignorée. @IgnoreAnnotation 注释添加到类中,来指示Symfony应该忽略哪个annotation。

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

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');}

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

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

生成Getters和Setters 

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

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

该命令可以确保 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

Par exemple, pour éviter que l'annotation @fn ne lève une exception, ajoutez l'annotation suivante :

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

Après avoir créé l'entité, vous devez utiliser la commande suivante pour vérifier les mappages :
$product = $query->setMaxResults(1)->getOneOrNullResult();
#🎜🎜#

Générer des getters et Setters

#🎜🎜#Bien que Doctrine sache désormais comment conserver le Produit objet à la base de données, mais la classe elle-même n'a pas encore de véritable objectif. Puisque Product n'est qu'une classe PHP normale avec des propriétés private, vous devez créer des méthodes getter et setter publiques (telles que getName ( ), setName($name)) pour accéder à ses propriétés (ses propriétés sont protégées) dans d'autres parties du programme. Heureusement, la commande suivante peut générer automatiquement ces méthodes modélisées : #🎜🎜#
$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();
#🎜🎜#Cette commande garantit que tous les getters et setters de la classe Product sont générés. Il s'agit d'une ligne de commande sécurisée - vous pouvez l'exécuter plusieurs fois et elle ne générera que des getters et des setters qui n'existent pas (c'est-à-dire qu'elle ne remplacera pas les méthodes existantes). #🎜🎜##🎜🎜#
#🎜🎜#Rappel importantLa phrase suivante est extrêmement La profondeur est la clé pour utiliser la Doctrine. Tout le monde doit le faire. #🎜🎜##🎜🎜##🎜🎜#Rappelez-vous, le générateur d'entités de doctrine génère des getters/setters simples. Vous devez revoir les méthodes générées et, si nécessaire, ajouter une logique pour répondre aux besoins de votre application. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#

#🎜🎜#En savoir plus sur doctrine:generate:entities#🎜🎜## 🎜🎜#En utilisant la commande doctrine:generate:entities vous pouvez : #🎜🎜#
  • #🎜🎜#Générer des getters et des setters dans la classe d'entité #🎜🎜#
  • #🎜🎜#Générer la classe de référentiel correspondante lorsque la classe d'entité est configurée avec l'annotation @ORMEntity(repositoryClass="...") ; #🎜🎜#
  • < li>#🎜🎜# Générez des constructeurs appropriés pour 1:n ou n:m. #🎜🎜#
#🎜🎜# La commande doctrine:generate:entities enregistrera une sauvegarde du fichier Product.php d'origine et de son nom. il Product.php~. Parfois, ce fichier peut provoquer une erreur « Impossible de redéclarer la classe ». Il peut être retiré en toute sécurité. Vous pouvez également utiliser l'option –no-backup pour empêcher la génération de ces fichiers de sauvegarde. #🎜🎜##🎜🎜# Notez que vous n'avez pas #🎜🎜# besoin de #🎜🎜# (dépend de) cette commande. Vous pouvez également écrire des getters et des setters à la main. Cette option existe uniquement pour vous faire gagner du temps puisque la création de ces méthodes est une tâche courante lors du développement. #🎜🎜##🎜🎜##🎜🎜#

Vous pouvez également générer des getters et des setters pour toutes les entités connues dans un bundle ou un espace de noms d'entité (n'importe quelle classe PHP contenant des informations de mappage de doctrine) :

rrreee

Créez une table de données/un schéma

Vous avez maintenant un < disponible code>Product qui contient les informations de mappage, donc Doctrine sait exactement comment les conserver. Bien entendu, vous n'avez pas encore la table de données produit correspondante dans la bibliothèque. Heureusement, Doctrine peut créer automatiquement tous les tableaux de données. Pour ce faire, exécutez la commande suivante : 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

Sérieusement, cette commande est étonnamment puissante. Il compare ce à quoi votre base de données devrait ressembler en théorie (en fonction des informations de mappage de vos entités) avec ce à quoi elle devrait ressembler en pratique, puis exécute l'instruction SQL requise pour mettre à jour le schéma de la base de données selon ce qu'il contient. devrait être. En d'autres termes, si vous ajoutez une nouvelle propriété contenant des « métadonnées de mappage » à Product et exécutez cette tâche, elle exécutera l'instruction « ALTER TABLE » requise dans la colonne déjà ajoutée. table code>produit
. 🎜 Que vous utilisiez ou non la migration de bases de données, la commande doctrine:schema:update ne convient que pour une utilisation dans un environnement de développement. Il ne doit pas être utilisé dans des environnements de production. 🎜🎜🎜🎜Vous disposez désormais d'une table de produits entièrement fonctionnelle dans votre base de données avec des colonnes correspondant aux métadonnées que vous avez spécifiées. 🎜🎜Objet persistant vers la base de données ¶🎜🎜🎜Vous avez maintenant une entité Produit et la table de base de données produit qui y est mappée. Vous pouvez conserver les données dans une base de données. Dans un contrôleur, c'est très simple. Ajoutez la méthode suivante au DefaultController du bundle. 🎜🎜Maintenant que vous avez mappé l'entité Product à la table product correspondante, vous êtes prêt à Produit est conservé dans la base de données. À l’intérieur du contrôleur, c’est extrêmement simple. Ajoutez la méthode suivante au DefaultController du bundle : 🎜rrreee🎜🎜Si vous suivez cet exemple, vous devrez créer une route et la pointer vers cette action pour la voir s'exécuter. 🎜🎜

Cet exemple montre l'utilisation de la méthode getDoctrine() de Doctrine dans un contrôleur. C'est un moyen rapide d'extraire le service doctrine. Si vous injectez ce service dans votre service, vous pouvez utiliser la doctrine n'importe où. Reportez-vous à Service Containerdoctrine 服务的快捷方法。若你在服务中注入此服务,即可在任意地方使用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 pour en savoir plus sur la création de services.

Analysons l'exemple précédent en profondeur :
  • Lignes 10 à 13 Instanciez-le ici et utilisez-le comme n'importe quel autre objet PHP classique< code class= Objet "notranslate">$product.
  • Ligne 15 Cette ligne supprime l'objet entity manager de Doctrine, qui est responsable de la gestion de la persistance de la base de données (Annotation : écriture) et du processus de récupération des objets.
  • La ligne 18 persist($product) est appelée, indiquant à Doctrine de "gérer" Objet $ produit. Cela déclenche une requête vers la base de données.
  • Ligne 21 Lorsque la méthode flush() est appelée, Doctrine parcourra tous les objets qu'elle gère pour déterminer si elle a besoin être la persistance dans la base de données. Dans cet exemple, les données de l'objet $product n'existent pas dans la bibliothèque, le gestionnaire d'entités doit donc exécuter la commande INSERT demande, dans < Créer une nouvelle ligne dans la table code class="notranslate">product.
🎜
🎜En fait, puisque Doctrine connaît toutes vos entités gérées, lorsque vous appelez la méthode flush(), elle calculera tous les changements (changeset) et exécutera les instructions dans le bon ordre. Il utilise des instructions préparées en cache pour améliorer légèrement les performances. Par exemple, si vous souhaitez conserver un total de 100 objets Product, puis appeler la méthode flush(), Doctrine utilisera un seul objet de syntaxe de préparation pour exécuter INSÉRER. 🎜🎜🎜🎜

Si l'appel flush() échoue, un DoctrineORMORMException sera levée. Voir 🎜Transactions et concurrence🎜. 🎜🎜🎜

Le flux de travail est le même lors de la création et de la mise à jour d'objets. Dans la section suivante, vous verrez comment Doctrine émet intelligemment automatiquement une instruction Update si l'enregistrement existe déjà dans la base de données. 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 fournit une bibliothèque de classes qui vous permet de charger par programme des données de test dans votre projet (c'est-à-dire des « données de montage », des données fixes). Voir DoctrineFixturesBundle pour en savoir plus.

Obtenir des objets de la base de données

1466160351_18163_67861_doctrine_web_debug_toolbar (1).pngObtenir des objets de la base de données est plus facile, par exemple , si vous configurez une route pour afficher un objet Product spécifique en fonction de l'id du produit :

rrreee

Vous pouvez utiliser @ParamConverter Commentaire rapide , vous pouvez obtenir la même fonction sans écrire de code. Reportez-vous à FrameworkExtraBundle pour en savoir plus.

Lorsque l'on souhaite interroger un type d'objet spécifique, il faut toujours utiliser son "référentiel". Vous pouvez considérer un référentiel comme une classe PHP dont le seul travail est de vous aider à extraire des entités de cette classe particulière. Pour une classe d'entité, pour accéder à son trésor, transmettez :
rrreee
🎜appBundle:Product est une méthode de raccourci que vous pouvez utiliser n'importe où dans Doctrine pour remplacer Le nom de classe FQCN de la classe d'entité (par exemple AppBundleEntityProduct). Tant que votre entité est stockée dans l'espace de noms Entity du bundle, cela fonctionnera. 🎜🎜🎜Une fois que vous disposez d'un objet Repository, vous avez accès à toutes ses méthodes utiles. 🎜rrreee🎜
🎜Bien sûr, vous pouvez également utiliser des requêtes complexes, référez-vous à la Requête d'objet rubrique . 🎜🎜🎜🎜Vous pouvez également utiliser efficacement les méthodes findBy et findOneBy pour obtenir facilement des objets en fonction de plusieurs conditions : 🎜rrreee🎜🎜🎜Lors du rendu d'une page, vous pouvez Vous pouvez voir de nombreuses requêtes dans le coin inférieur droit de la barre d'outils de débogage Web. 🎜🎜🎜🎜🎜Si vous cliquez sur l'icône, le profileur s'ouvrira et affichera la requête exacte générée. 🎜🎜Si vos requêtes de page dépassent 50, l'icône deviendra jaune. Cela indique que quelque chose ne va pas. 🎜🎜🎜

Mises à jour d'objets

Une fois que vous obtenez un objet de Doctrine, sa mise à jour est facile. Supposons que vous disposiez d'un itinéraire qui mappe un identifiant de produit à l'action de mise à jour du contrôleur :

rrreee

La mise à jour d'un objet implique trois étapes :

  1. Récupérer l'objet de Doctrine ;
  2. Modifier l'objet ;
  3. Appeler le 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时,通过“占位符”来设置任意的外部值(上面例子的 :price

Notez qu'appeler $em->persist($product) n'est pas nécessaire. Rappelons que cette méthode indique simplement à Doctrine de gérer ou "d'observer" l'objet $product. Ici, parce que vous avez déjà obtenu l'objet $product, il est déjà géré.
Supprimer un objet ¶🎜🎜🎜Supprimer un objet est très similaire, mais vous devez appeler le remove() méthode du gestionnaire d'entités :🎜rrreee🎜 Comme vous vous en doutez peut-être, la méthode remove() notifie à Doctrine que vous souhaitez supprimer l'entité spécifiée de la base de données. La vraie requête DELETE ne sera réellement exécutée que lorsque la méthode flush() sera appelée. 🎜🎜

Requête d'objet ¶🎜

🎜Vous avez vu comment l'objet référentiel vous permet d'effectuer certaines requêtes de base sans avoir à le faire faire n'importe quel travail : 🎜rrreee🎜 Bien sûr, Doctrine vous permet également d'utiliser Doctrine Query Language (DQL) pour écrire certaines requêtes complexes. DQL est similaire à SQL, sauf qu'il est utilisé pour interroger un ou plusieurs objets de classe d'entité (tels que). product), tandis que SQL interroge les lignes d'une table de données (telle que product). 🎜🎜Lorsque vous interrogez dans Doctrine, vous avez deux options principales : écrire des requêtes Doctrine pures (DQL) ou utiliser le générateur de requêtes Doctrine. 🎜🎜Utilisez DQL pour la requête d'objet ¶🎜🎜🎜Supposons que vous souhaitiez interroger des produits dont le prix est supérieur à 19,99, et appuyez sur Les prix sont classés du plus bas au plus élevé. Vous pouvez utiliser DQL, une syntaxe similaire au SQL natif dans Doctrine, pour construire une requête pour ce scénario :🎜rrreee🎜Si vous avez l'habitude d'écrire du SQL, alors DQL sera également très naturel. La plus grande différence entre eux est que vous devez penser en termes de "sélectionner des objets PHP", et non en termes de lignes d'un tableau de données. Pour cette raison, vous démarrez l'entité de AppBundle:Product (éventuellement un raccourci pour la classe AppBundleEntityProduct) vers select, puis donnez à l'entité un alias de p. 🎜🎜
🎜Faites attention à la méthode setParameter(). Lorsque vous utilisez Doctrine, c'est une bonne idée de définir des valeurs externes arbitraires via des "espaces réservés" (:price dans l'exemple ci-dessus) car cela empêche les attaques par injection SQL. 🎜🎜🎜La méthode

getResult() renvoie un tableau de résultats. Pour obtenir un résultat, vous pouvez utiliser getSingleResult() (cette méthode lèvera une exception lorsqu'il n'y a pas de résultat) ou 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

La syntaxe DQL est ainsi puissant qui, incroyablement, permet des jointures faciles entre des entités (couvertes plus tard relations), des groupes, etc. Reportez-vous au Langage de requête de la Doctrine documentation pour en savoir plus.

Utilisez le générateur de requêtes de Doctrine pour les requêtes d'objets

N'écrivez pas de grandes chaînes de DQL, vous Vous pouvez utiliser un objet QueryBuilder très utile pour créer cette chaîne. Ceci est utile lorsque votre requête dépend de conditions dynamiques, car à mesure que votre chaîne de connexion grandit, le code DQL devient de plus en plus difficile à lire :

rrreee

L'objet QueryBuilder contient toutes les méthodes nécessaires à la création d'une requête. En appelant la méthode getQuery(), le générateur de requêtes renverra un objet Query standard qui peut être utilisé pour obtenir l'ensemble de résultats demandé.
🎜🎜Query Builder Pour plus d'informations, reportez-vous au Documentation du générateur de requêtes. 🎜

Organisez les requêtes personnalisées dans la classe Repository

🎜Toutes les requêtes précédentes sont écrites directement dans votre contrôleur . Mais pour l'organisation du programme, Doctrine fournit une classe de référentiel dédiée qui vous permet de sauvegarder toute la logique de requête dans un emplacement central. 🎜🎜Référez-vous à Comment créer une classe de référentiel personnalisée pour en savoir plus. 🎜🎜Configuration 🎜🎜La doctrine est hautement configurable, même si vous ne vous souciez peut-être jamais de ces options. Pour obtenir des informations sur la configuration de Doctrine, consultez la référence de configuration. 🎜🎜Référence des types de champs de doctrine ¶🎜🎜La doctrine est livrée avec un grand nombre de types de champs disponibles. Chacun peut mapper des types de données PHP à des types de champs spécifiques, quelle que soit la base de données que vous utilisez. Pour chaque type de champ, Colonne peut être configuré davantage pour définir le comportement longueur, nullable, nom ou d'autres options. Pour obtenir la liste des types de champs disponibles, consultez la documentation sur les types de mappage. 🎜🎜Associations et relations ¶🎜🎜Doctrine fournit toutes les fonctionnalités dont vous avez besoin pour gérer les relations avec les bases de données (également appelées associations). Pour plus d’informations, consultez Comment utiliser les associations/relations de doctrine. 🎜

Résumé ¶

Avec Doctrine, vous pouvez vous concentrer sur votre objet et comment l'appliquer à votre programme, tandis que la persistance de la base de données est la deuxième étape. En effet, Doctrine vous permet d'utiliser n'importe quel objet PHP pour enregistrer vos données et s'appuie sur les informations de « mappage de métadonnées » pour mapper les données d'un objet à une table de données spécifique.

Doctrine possède de nombreuses fonctionnalités puissantes qui n'attendent que vous pour apprendre, telles que les relations, les requêtes complexes et la surveillance des événements.