离线下载
PDF版 ePub版

极客学院团队出品 · 更新于 2018-11-28 11:00:43

如何定义虚拟类和接口之间的关系

Bundles 的目标之一是创建一个不具有多个(如果有的话)依赖关系的谨慎的功能 bundles,允许您在其他应用程序中使用该功能,而不包括不必要的项目。

Doctrine2.2 包括一个新的实用工具,称为 ResolveTargetEntityListener,通过截取 Doctrine 内部一定的调用并且在运行时重写您元数据映射的 targetEntity 参数作用。这意思是在您的 bundle 里,您可以在您的映射中使用一个接口或者虚拟类,并期望在运行时正确映射到一个具体的实体。

这个功能允许您在定义不同实体间的关系,而不让它们很难依赖。

背景

假设您有一个 invoiceBundle 提供进销存功能并且 CustomerBundle 包含客户管理工具。您想保持它们分开的状态,因为它们可以在分开的状态下用于其他系统,但对于您的应用程序,您想将其放在一起使用。

在这种情况下,您有一个与一个不存在对象相关的 Invoice 实体 InvoiceSubjectInterface。目标是让 ResolveTargetEntityListener 来取代任何提及与实体对象实现该接口的接口。

设置

本文章使用以下两种基本实体(简洁但不完整)来解释如何设置并使用 ResolveTargetEntityListener

Customer 实体:

// src/Acme/AppBundle/Entity/Customer.php

namespace Acme\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
{
    // In this example, any methods defined in the InvoiceSubjectInterface
    // are already implemented in the BaseCustomer
}

Invoice 实体:

// 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:

// 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
{
    // List any additional methods that your InvoiceBundle
    // will need to access on the subject so that you can
    // be sure that you have access to those methods.

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

接下来,您需要配置监听器,告知 DoctrineBundle 关于替代的事情:

YAML:

# app/config/config.yml
doctrine:
    # ...
    orm:
        # ...
        resolve_target_entities:
            Acme\InvoiceBundle\Model\InvoiceSubjectInterface: Acme\AppBundle\Entity\Customer

XML:

<!-- app/config/config.xml -->
<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">Acme\AppBundle\Entity\Customer</doctrine:resolve-target-entity>
        </doctrine:orm>
    </doctrine:config>
</container>

PHP:

// app/config/config.php
$container->loadFromExtension('doctrine', array(
    'orm' => array(
        // ...
        'resolve_target_entities' => array(
            'Acme\InvoiceBundle\Model\InvoiceSubjectInterface' => 'Acme\AppBundle\Entity\Customer',
        ),
    ),
));

结语

有了 ResolveTargetEntityListener,您可以分离您的 bundles,让它们自己保持合用的状态,但是仍能够定义不同对象之间的关系。通过使用这种方法,您的 bundles 会更容易保持独立。