Keeping your Modules independent

One of the goals of using modules is to create discrete units of functionality that do not have many (if any) dependencies, allowing you to use that functionality in other applications without including unnecessary items.

モジュールを使用する目的の 1 つは、多くの (存在する場合) 依存関係を持たない個別の機能単位を作成することです。これにより、不要な項目を含めずに他のアプリケーションでその機能を使用できるようになります。

Doctrine ORM includes a new utility called the ResolveTargetEntityListener, that functions by intercepting certain calls inside Doctrine and rewrite targetEntity parameters in your metadata mapping at runtime. It means that in your bundle you are able to use an interface or abstract class in your mappings and expect correct mapping to a concrete entity at runtime.

Doctrine ORM には ResolveTargetEntityListener と呼ばれる新しいユーティリティが含まれています。これは Doctrine 内の特定の呼び出しをインターセプトすることによって機能し、実行時にメタデータ マッピングの rewritetargetEntity パラメーターを使用します。これは、バンドル内で、マッピングでインターフェイスまたは抽象クラスを使用でき、実行時に具体的なエンティティへの正しいマッピングを期待できることを意味します。

This functionality allows you to define relationships between different entities but not making them hard dependencies.

この機能により、異なるエンティティ間の関係を定義できますが、それらを強い依存関係にすることはできません。

Background

In the following example, the situation is we have an InvoiceModule which provides invoicing functionality, and a CustomerModule that contains customer management tools. We want to keep these separated, because they can be used in other systems without each other, but for our application we want to use them together.

次の例では、請求機能を提供する InvoiceModule と、顧客管理ツールを含む CustomerModule がある状況です。これらは、他のシステムでは互いに使用せずに使用できるため、これらを分離したままにしておく必要がありますが、アプリケーションでは一緒に使用したいと考えています。

In this case, we have an Invoice entity with a relationship to a non-existent object, an InvoiceSubjectInterface. The goal is to get the ResolveTargetEntityListener to replace any mention of the interface with a real object that implements that interface.

この場合、存在しないオブジェクトである InvoiceSubjectInterface との関係を持つ Invoice エンティティがあります。目標は、ResolveTargetEntityListener を取得して、インターフェイスに関する記述を、そのインターフェイスを実装する実際のオブジェクトに置き換えることです。

Set up

We’re going to use the following basic entities (which are incomplete for brevity) to explain how to set up and use the RTEL.

次の基本的なエンティティ (簡潔にするために不完全です) を使用して、RTEL の設定方法と使用方法を説明します。

A Customer entity

顧客エンティティ

<?php
// src/Acme/AppModule/Entity/Customer.php

namespace Acme\AppModule\Entity;

use Doctrine\ORM\Mapping as ORM;
use Acme\CustomerModule\Entity\Customer as BaseCustomer;
use Acme\InvoiceModule\Model\InvoiceSubjectInterface;

/**
 * @ORM\Entity
 * @ORM\Table(name="customer")
 */
class Customer extends BaseCustomer implements InvoiceSubjectInterface
{
    // In our example, any methods defined in the InvoiceSubjectInterface
    // are already implemented in the BaseCustomer
}

An Invoice entity

請求書エンティティ

<?php
// src/Acme/InvoiceModule/Entity/Invoice.php

namespace Acme\InvoiceModule\Entity;

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

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

An InvoiceSubjectInterface

InvoiceSubjectInterface

<?php
// src/Acme/InvoiceModule/Model/InvoiceSubjectInterface.php

namespace Acme\InvoiceModule\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 InvoiceModule
    // 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();
}

Next, we need to configure the listener. Add this to the area you set up Doctrine. You must set this up in the way outlined below, otherwise you can not be guaranteed that the targetEntity resolution will occur reliably:

次に、リスナーを構成する必要があります。これを Doctrine をセットアップする領域に追加します。以下に概説する方法でこれを設定する必要があります。そうしないと、targetEntity の解決が確実に行われることが保証されません。

<?php
$evm  = new \Doctrine\Common\EventManager;
$rtel = new \Doctrine\ORM\Tools\ResolveTargetEntityListener;

// Adds a target-entity class
$rtel->addResolveTargetEntity('Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface', 'Acme\\CustomerModule\\Entity\\Customer', array());

// Add the ResolveTargetEntityListener
$evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $rtel);

$connection = \Doctrine\DBAL\DriverManager::createConnection($connectionOptions, $config, $evm);
$em = new \Doctrine\ORM\EntityManager($connection, $config, $evm);

Final Thoughts

With the ResolveTargetEntityListener, we are able to decouple our bundles, keeping them usable by themselves, but still being able to define relationships between different objects. By using this method, I’ve found my bundles end up being easier to maintain independently.

ResolveTargetEntityListener を使用すると、バンドルを切り離して単独で使用できるようにすることができますが、異なるオブジェクト間の関係を定義することもできます。この方法を使用することで、バンドルを個別に管理しやすくなることがわかりました。

Table Of Contents

Previous topic

Implementing the Notify ChangeTracking Policy

Notify ChangeTracking ポリシーの実装

Next topic

SQL-Table Prefixes

SQL テーブルのプレフィックス

This Page

Fork me on GitHub