跳到内容

如何使用抽象类和接口定义关系

编辑此页

Bundle 的目标之一是创建功能离散的 bundle,这些 bundle 没有很多(如果有的话)依赖项,允许您在其他应用程序中使用该功能,而无需包含不必要的项。

Doctrine 2.2 包含一个名为 ResolveTargetEntityListener 的新实用程序,它的功能是通过拦截 Doctrine 内部的某些调用,并在运行时重写元数据映射中的 targetEntity 参数。这意味着在您的 bundle 中,您可以使用接口或抽象类在映射中,并期望在运行时正确映射到具体的实体。

此功能允许您定义不同实体之间的关系,而无需使它们成为硬依赖项。

背景

假设您有一个 InvoiceBundle,它提供发票功能,以及一个 CustomerBundle,它包含客户管理工具。您希望将它们分开,因为它们可以在没有彼此的其他系统中使用,但对于您的应用程序,您希望将它们一起使用。

在这种情况下,您有一个 Invoice 实体,它与一个不存在的对象 InvoiceSubjectInterface 存在关系。目标是让 ResolveTargetEntityListener 将任何提及接口的地方替换为实现该接口的真实对象。

设置

本文使用以下两个基本实体(为简洁起见,这些实体是不完整的)来解释如何设置和使用 ResolveTargetEntityListener

Customer 实体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/Entity/Customer.php
namespace App\Entity;

use App\Entity\CustomerInterface as BaseCustomer;
use App\Model\InvoiceSubjectInterface;
use Doctrine\ORM\Mapping as ORM;

#[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 实体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// src/Entity/Invoice.php
namespace App\Entity;

use App\Model\InvoiceSubjectInterface;
use Doctrine\ORM\Mapping as ORM;

/**
 * Represents an Invoice.
 */
#[ORM\Entity]
#[ORM\Table(name: 'invoice')]
class Invoice
{
    #[ORM\ManyToOne(targetEntity: InvoiceSubjectInterface::class)]
    protected InvoiceSubjectInterface $subject;
}

InvoiceSubjectInterface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/Model/InvoiceSubjectInterface.php
namespace App\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.

    public function getName(): string;
}

接下来,您需要配置监听器,它会告知 DoctrineBundle 关于替换的信息

1
2
3
4
5
6
7
# config/packages/doctrine.yaml
doctrine:
    # ...
    orm:
        # ...
        resolve_target_entities:
            App\Model\InvoiceSubjectInterface: App\Entity\Customer

最终想法

使用 ResolveTargetEntityListener,您可以解耦您的 bundle,保持它们可以独立使用,但仍然能够定义不同对象之间的关系。通过使用这种方法,您的 bundle 最终将更容易独立维护。

本作品,包括代码示例,根据 Creative Commons BY-SA 3.0 许可协议获得许可。
目录
    版本