使用 PHP 8 属性时如何覆盖子类中 Doctrine 的字段关联映射?

use*_*531 7 php attributes doctrine symfony doctrine-orm

如何在父类中定义 Doctrine 属性并覆盖扩展父类的类中的关联?当使用注释时,这是通过使用AssociationOverride实现的,但是,我认为它们在使用 PHP 8 属性时不可用

为什么我想:

我有一个类,AbstractTenantEntity其目的是将数据访问限制为Tenant拥有数据的给定(即帐户、所有者等),并且扩展该类的任何实体将tenant_id在创建时插入到数据库中,并且所有其他请求将添加tenant_id到 WHERE 子句。 Tenant通常没有扩展的各种实体的集合AbstractTenantEntity,但有一些有。当使用注释时,我通过将 Doctrine 的AssociationOverride注释应用到扩展类来处理它,这些扩展类应该在 中具有集合Tenant,但我不知道在使用 PHP 8 属性时如何实现这一点?


下面描述的我的尝试是不成功的,因为我错误地认为如果进行适当的修改,注释类将神奇地与属性一起工作,但现在我看到其他代码必须能够基于属性应用适当的逻辑。因此,我放弃了这种方法,只是将属性保护起来并在具体类中复制它们。

我的尝试:

租户实体

use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\OneToMany;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;

#[Entity()]
class Tenant
{
    #[Id, Column(type: "integer")]
    #[GeneratedValue]
    private ?int $id = null;

    #[OneToMany(targetEntity: Asset::class, mappedBy: 'tenant')]
    private array|Collection|ArrayCollection $assets;

    // Other properties and typical getters and setters
}
Run Code Online (Sandbox Code Playgroud)

AbstractTenantEntity 实体

use Doctrine\ORM\Mapping\ManyToOne;
use Doctrine\ORM\Mapping\JoinColumn;

abstract class AbstractTenantEntity implements TenantInterface
{
    /**
     * inversedBy performed in child where required
     */
    #[ManyToOne(targetEntity: Tenant::class)]
    #[JoinColumn(nullable: false)]
    protected ?Tenant $tenant = null;

    // Typical getters and setters
}
Run Code Online (Sandbox Code Playgroud)

这是让我陷入困境的部分。当使用注释时,我的代码如下:

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity()
 * @ORM\AssociationOverrides({
 *     @ORM\AssociationOverride(name="tenant", inversedBy="assets")
 * })
 */
class Asset extends AbstractTenantEntity
{
    // Various properties and typical getters and setters
}
Run Code Online (Sandbox Code Playgroud)

AssociationOverrides尚未修改为使用属性,因此基于官方类,我创建了自己的类,类似于 Doctrine 更新的其他类:

namespace App\Mapping;

use Attribute;
use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
use Doctrine\ORM\Mapping\Annotation;

/**
 * This annotation is used to override association mapping of property for an entity relationship.
 *
 * @Annotation
 * @NamedArgumentConstructor()
 * @Target("ANNOTATION")
 */
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
final class AssociationOverride implements Annotation
{
    /**
     * The name of the relationship property whose mapping is being overridden.
     *
     * @var string
     */
    public $name;

    /**
     * The join column that is being mapped to the persistent attribute.
     *
     * @var array<\Doctrine\ORM\Mapping\JoinColumn>
     */
    public $joinColumns;

    /**
     * The join table that maps the relationship.
     *
     * @var \Doctrine\ORM\Mapping\JoinTable
     */
    public $joinTable;

    /**
     * The name of the association-field on the inverse-side.
     *
     * @var string
     */
    public $inversedBy;

    /**
     * The fetching strategy to use for the association.
     *
     * @var string
     * @Enum({"LAZY", "EAGER", "EXTRA_LAZY"})
     */
    public $fetch;

    public function __construct(
        ?string $name = null,
        ?array  $joinColumns = null,
        ?string $joinTable = null,
        ?string $inversedBy = null,
        ?string $fetch = null
    ) {
        $this->name    = $name;
        $this->joinColumns = $joinColumns;
        $this->joinTable = $joinTable;
        $this->inversedBy = $inversedBy;
        $this->fetch = $fetch;
        //$this->debug('__construct',);
    }

    private function debug(string $message, string $file='test.json', ?int $options = null)
    {
        $content = file_exists($file)?json_decode(file_get_contents($file), true):[];
        $content[] = ['message'=>$message, 'object_vars'=>get_object_vars($this), 'debug_backtrace'=>debug_backtrace($options)];
        file_put_contents($file, json_encode($content, JSON_PRETTY_PRINT));
    }
}
Run Code Online (Sandbox Code Playgroud)

在验证映射时,Doctrine 抱怨 target-entity 不包含所需的inversedBy. 我花了一些时间浏览 Doctrine 源代码,但没有取得太大进展。

我目前的方法是否有优点,如果有,请填补空白。但是,如果没有,您建议如何满足这一需求?

小智 2

此pr已解决: https: //github.com/doctrine/orm/pull/9241

ps:需要 PHP 8.1

#[AttributeOverrides([
new AttributeOverride(
    name: "id",
    column: new Column(name: "guest_id", type: "integer", length: 140)
),
new AttributeOverride(
    name: "name",
    column: new Column(name: "guest_name", nullable: false, unique: true, length: 240)
)]
)]
Run Code Online (Sandbox Code Playgroud)