Как определять отношения с абстрактными классами и интерфейсами

Как определять отношения с абстрактными классами и интерфейсами

Одной из целью пакетов является создание разумных пакетов функциональностей, которые не имеют много зависимостей (или не имеют их вообще), что позволяет вам использовать эту функциональность в других приложениях, не включая ненужные вещи.

Doctrine 2.2 включает новую утилиту, называемую ResolveTargetEntityListener, которая функционирует путём перехвата некоторых вызовов внутри Doctrine и переопределением параметром targetEntity в маршрутизации ваших метаданных во время прогона.

Эта функциональность позволяет вам определять отношения между разными сущностями, не делая их жёсткими зависимостями.

Задний план

Представьте, что у вас есть InvoiceBundle, который предоставляет функциональность выставления счетов-фактур и CustomerBundle, который содержит пользовательские инструменты управления. Вам нужно держать их раздельно, так как они могут быть использованы в других системах друг без друга, но для вашего приложения вы хотите использовать их вместе.

В этом случаев, у вас есть сущность Invoice с отношением к несуществующему объекту InvoiceSubjectInterface. Целью является заставить ResolveTargetEntityListener заменять любое упоминание об интерфейсе реальным объектом, реализующим этот интерфейс.

Установка

Эта статья использует две следующие базовые сущности (которые неполные для краткости) для того, чтобы объяснить, как установить и использовать ResolveTargetEntityListener.

Сущность пользователя:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// src/AppBundle/Entity/Customer.php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Acme\CustomerBundle\Entity\Customer as BaseCustomer;
use Acme\InvoiceBundle\Model\InvoiceSubjectInterface;

/**
 * @ORM\Entity
 * @ORM\Table(name="customer")
 */
class Customer extends BaseCustomer implements InvoiceSubjectInterface
{
    // В этом примере. любые методы, определённые в InvoiceSubjectInterface
    // уже реализованы в BaseCustomer
}

Сущность счёта-фактуры:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// src/Acme/InvoiceBundle/Entity/Invoice.php

namespace Acme\InvoiceBundle\Entity;

use Doctrine\ORM\Mapping AS ORM;
use Acme\InvoiceBundle\Model\InvoiceSubjectInterface;

/**
 * Represents an Invoice.
 *
 * @ORM\Entity
 * @ORM\Table(name="invoice")
 */
class Invoice
{
    /**
     * @ORM\ManyToOne(targetEntity="Acme\InvoiceBundle\Model\InvoiceSubjectInterface")
     * @var InvoiceSubjectInterface
     */
    protected $subject;
}

InvoiceSubjectInterface:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// src/Acme/InvoiceBundle/Model/InvoiceSubjectInterface.php

namespace Acme\InvoiceBundle\Model;

/**
 * An interface that the invoice Subject object should implement.
 * In most circumstances, only a single object should implement
 * this interface as the ResolveTargetEntityListener can only
 * change the target to a single object.
 */
interface InvoiceSubjectInterface
{
    // Укажите все дополнительные методы, к которым InvoiceBundle
    // понадобится получить доступ, чтобы вы могли
    // быть уверены, что у вас есть доступ к этим методам.

    /**
     * @return string
     */
    public function getName();
}

Далее, вам нужно сконфигурировать слушатель, которые сообщает DoctrineBundle о замене:

  • YAML
    1
    2
    3
    4
    5
    6
    7
    # app/config/config.yml
    doctrine:
        # ...
        orm:
            # ...
            resolve_target_entities:
                Acme\InvoiceBundle\Model\InvoiceSubjectInterface: AppBundle\Entity\Customer
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    <!-- 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:orm>
                <!-- ... -->
                <doctrine:resolve-target-entity interface="Acme\InvoiceBundle\Model\InvoiceSubjectInterface">AppBundle\Entity\Customer</doctrine:resolve-target-entity>
            </doctrine:orm>
        </doctrine:config>
    </container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    // app/config/config.php
    use Acme\InvoiceBundle\Model\InvoiceSubjectInterface;
    use AppBundle\Entity\Customer;
    
    $container->loadFromExtension('doctrine', array(
        'orm' => array(
            // ...
            'resolve_target_entities' => array(
                InvoiceSubjectInterface::class => Customer::class,
            ),
        ),
    ));
    

Заключение

С помощью ResolveTargetEntityListener, вы можете разъединять ваши пакеты, чтобы их можно было использовать по-отдельности, но у вас остаётся возможность определять отношения между разными объектами. Используя этот метод, вашими пакетами будет легче управлять по-отдельности.

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.