使用 PHP 8 个属性时,如何在子类中覆盖 Doctrine 的字段关联映射?
How to override Doctrine's field association mappings in subclasses when using PHP 8 attributes?
如何在父 class 中定义 Doctrine 属性 并覆盖扩展父 class 的 class 中的关联?使用注释时,这是通过使用 AssociationOverride 实现的,但是,我认为使用 PHP 8 个属性时它们不可用
我为什么要:
我有一个 class AbstractTenantEntity
,其目的是将对数据的访问限制为拥有数据的给定 Tenant
(即帐户、所有者等)和任何实体扩展此 class 将在创建时将 tenant_id
插入到数据库中,所有其他请求会将 tenant_id
添加到 WHERE 子句中。 Tenant
通常没有扩展 AbstractTenantEntity
的各种实体的集合,但少数有。在使用注释时,我通过将 Doctrine 的 AssociationOverride
注释应用到扩展的 classes 来处理它,它应该在 Tenant
中有一个集合,但是我不知道如何在使用 PHP 8个属性?
我在下面描述的尝试没有成功,因为我错误地认为 annotation class would magically work with attributes if modified appropriately, but now I see other code 必须能够根据属性应用适当的逻辑。因此,我放弃了这种方法,只是保护属性并在具体 class.
中复制它们
我的尝试:
租户实体
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
}
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
}
这是让我卡住的部分。使用注释时,我的代码如下:
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
}
但是 AssociationOverrides
还没有被修改以使用属性,所以基于 official class,我创建了我自己的 class 类似于 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));
}
}
验证映射时,Doctrine 抱怨目标实体不包含所需的 inversedBy
。我花了一些时间研究 Doctrine 源代码,但没有取得太大进展。
我目前的方法是否有价值,如果有,请填补空白。但是,如果没有,您建议如何满足这一需求?
覆盖子classes
中的字段关联映射
有时需要保留实体但覆盖全部或部分映射元数据。有时,要覆盖的映射也来自使用特征的实体,其中特征具有映射元数据。本教程解释了如何覆盖映射元数据,特别是属性和关联元数据。此处的示例显示了使用特征的 class 的覆盖,但在扩展基础 class 时类似,如本教程末尾所示。
假设我们有一个 class ExampleEntityWithOverride。 class 使用特征 ExampleTrait:
<?php
/**
* @Entity
*
* @AttributeOverrides({
* @AttributeOverride(name="foo",
* column=@Column(
* name = "foo_overridden",
* type = "integer",
* length = 140,
* nullable = false,
* unique = false
* )
* )
* })
*
* @AssociationOverrides({
* @AssociationOverride(name="bar",
* joinColumns=@JoinColumn(
* name="example_entity_overridden_bar_id", referencedColumnName="id"
* )
* )
* })
*/
class ExampleEntityWithOverride
{
use ExampleTrait;
}
/**
* @Entity
*/
class Bar
{
/** @Id @Column(type="string") */
private $id;
}
文档块显示属性和关联类型的元数据覆盖。它基本上更改了为 属性 foo 映射的列的名称以及与上面显示的 Bar class 相关的关联栏的名称。这是具有被上面注释覆盖的映射元数据的特征:
<?php
/**
* Trait class
*/
trait ExampleTrait
{
/** @Id @Column(type="string") */
private $id;
/**
* @Column(name="trait_foo", type="integer", length=100, nullable=true, unique=true)
*/
protected $foo;
/**
* @OneToOne(targetEntity="Bar", cascade={"persist", "merge"})
* @JoinColumn(name="example_trait_bar_id", referencedColumnName="id")
*/
protected $bar;
}
扩展 class 的情况是一样的,但是:
<?php
class ExampleEntityWithOverride extends BaseEntityWithSomeMapping
{
// ...
}
通过 XML 和 YAML(示例)也支持覆盖。
已通过此 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)
)]
)]
如何在父 class 中定义 Doctrine 属性 并覆盖扩展父 class 的 class 中的关联?使用注释时,这是通过使用 AssociationOverride 实现的,但是,我认为使用 PHP 8 个属性时它们不可用
我为什么要:
我有一个 class AbstractTenantEntity
,其目的是将对数据的访问限制为拥有数据的给定 Tenant
(即帐户、所有者等)和任何实体扩展此 class 将在创建时将 tenant_id
插入到数据库中,所有其他请求会将 tenant_id
添加到 WHERE 子句中。 Tenant
通常没有扩展 AbstractTenantEntity
的各种实体的集合,但少数有。在使用注释时,我通过将 Doctrine 的 AssociationOverride
注释应用到扩展的 classes 来处理它,它应该在 Tenant
中有一个集合,但是我不知道如何在使用 PHP 8个属性?
我在下面描述的尝试没有成功,因为我错误地认为 annotation class would magically work with attributes if modified appropriately, but now I see other code 必须能够根据属性应用适当的逻辑。因此,我放弃了这种方法,只是保护属性并在具体 class.
中复制它们我的尝试:
租户实体
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
}
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
}
这是让我卡住的部分。使用注释时,我的代码如下:
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
}
但是 AssociationOverrides
还没有被修改以使用属性,所以基于 official class,我创建了我自己的 class 类似于 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));
}
}
验证映射时,Doctrine 抱怨目标实体不包含所需的 inversedBy
。我花了一些时间研究 Doctrine 源代码,但没有取得太大进展。
我目前的方法是否有价值,如果有,请填补空白。但是,如果没有,您建议如何满足这一需求?
覆盖子classes
中的字段关联映射有时需要保留实体但覆盖全部或部分映射元数据。有时,要覆盖的映射也来自使用特征的实体,其中特征具有映射元数据。本教程解释了如何覆盖映射元数据,特别是属性和关联元数据。此处的示例显示了使用特征的 class 的覆盖,但在扩展基础 class 时类似,如本教程末尾所示。
假设我们有一个 class ExampleEntityWithOverride。 class 使用特征 ExampleTrait:
<?php
/**
* @Entity
*
* @AttributeOverrides({
* @AttributeOverride(name="foo",
* column=@Column(
* name = "foo_overridden",
* type = "integer",
* length = 140,
* nullable = false,
* unique = false
* )
* )
* })
*
* @AssociationOverrides({
* @AssociationOverride(name="bar",
* joinColumns=@JoinColumn(
* name="example_entity_overridden_bar_id", referencedColumnName="id"
* )
* )
* })
*/
class ExampleEntityWithOverride
{
use ExampleTrait;
}
/**
* @Entity
*/
class Bar
{
/** @Id @Column(type="string") */
private $id;
}
文档块显示属性和关联类型的元数据覆盖。它基本上更改了为 属性 foo 映射的列的名称以及与上面显示的 Bar class 相关的关联栏的名称。这是具有被上面注释覆盖的映射元数据的特征:
<?php
/**
* Trait class
*/
trait ExampleTrait
{
/** @Id @Column(type="string") */
private $id;
/**
* @Column(name="trait_foo", type="integer", length=100, nullable=true, unique=true)
*/
protected $foo;
/**
* @OneToOne(targetEntity="Bar", cascade={"persist", "merge"})
* @JoinColumn(name="example_trait_bar_id", referencedColumnName="id")
*/
protected $bar;
}
扩展 class 的情况是一样的,但是:
<?php
class ExampleEntityWithOverride extends BaseEntityWithSomeMapping
{
// ...
}
通过 XML 和 YAML(示例)也支持覆盖。
已通过此 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)
)]
)]