【问题标题】:Symfony Doctrine Readonly Entity with data managed using migrations使用迁移管理数据的 Symfony Doctrine 只读实体
【发布时间】:2018-12-04 13:00:35
【问题描述】:

我正在就以下场景的最佳做法寻求建议。

场景

我有一些实体使用我所说的“数据作为架构的一部分”:它们的数据是软件的一部分,在正常操作期间不能由软件创建/编辑/删除。

我使用这些来为其他实体提供固定的选择列表,而不使用枚举字段 - 因为它们实际上是列定义的一部分,如果我需要以任何其他方式更改列而不是附加一个新值生成完整的表副本。

这样的实体定义如下 - readOnly=true 参数、手动分配的 ID、唯一的 slug、唯一的用户可见名称和私有构造函数。这不是夹具的好候选,因为它不是测试数据(谈论 Doctrine Fixtures 如何删除我的“数据作为架构”是另一个 SO 问题)

/**
 * @ORM\Entity(readOnly=true)
 */
class SubmissionStatus
{
    /**
     * @var int
     * @ORM\Column(type="integer")
     * @ORM\Id()
     */
    protected $id;

    /**
     * @var string
     * @ORM\Column(type="string", length=191, unique=true)
     */
    protected $slug;

    /**
     * @var string
     * @ORM\Column(type="string", length=191, unique=true)
     */
    protected $name;

    private __construct() {}

    // ... other fields
}

我使用 Doctrine Migrations 管理价值观:

final class Version20181023132831 extends AbstractMigration
{
    public function up(Schema $schema) : void
    {
        $statuses = [
            ["id" => 1, "name" => "Pending", "slug" => "pending"],
            ["id" => 2, "name" => "Reported", "slug" => "reported"],
            ["id" => 3, "name" => "Rejected", "slug" => "rejected"],
            ["id" => 4, "name" => "Accepted", "slug" => "accepted"],
        ];

        foreach ($statuses as $status) {
            $this->addSql("INSERT INTO submission_status (id, name, slug) VALUES (:id, :name, :slug WHERE id = :id", [
                "id" => $status['id'],
                "name" => $status['name'],
                "slug" => $status['slug'],
            ]);
        }
    }
}

如果我需要更新这些值,我会在未来的迁移中使用 UPDATEDELETE - 如果需要,还会按摩引用这些行的现有外键。

其他实体与该实体有@ORM\ManyToOne 关系。

问题

在代码中引用这些的最佳方式是什么?

并非所有这些实体都将由用户在表单中的ChoiceType 字段中选择或通过 API 调用进入。其中一些将仅在$otherEntity->setStatus(...)之类的代码中使用

当前解决方案

目前我们已经决定这样做:

我们在实体类中存储 ID 和 slug 的常量。

class SubmissionStatus
{
    const PENDING_ID = 1;
    const PENDING_SLUG = 'pending';
    const REPORTED_ID = 2;
    const REPORTED_SLUG = 'reported';
    const REJECTED_ID = 3;
    const REJECTED_SLUG = 'rejected';
    const ACCEPTED_ID = 4;
    const ACCEPTED_SLUG = 'accepted';
}

当我们需要setStatus时,我们进行数据库查找:

$status = $em->getRepository(SubmissionStatus::class)->findOneBy(['slug' => SubmissionStatus::ACCEPTED_SLUG])
$otherEntity->setStatus($status);

问题

有没有更好的方法来做到这一点?

我不太喜欢一直写getRepository->findOneBy 样板文件,尤其是当我需要引用几个此类实体时。我希望在反复写的时候写一些简短、简单、快速的东西。

我也希望在更改值时不必更改太多地方。一个在迁移中,一个在类常量中就足够了。

到目前为止我的想法

我无法向实体本身添加辅助函数(例如setStatusAccepted()setStatus(SubmissionStatus::ACCEPTED_SLUG)),因为我无法从那里查找引用的实体。

一个注入服务,它在创建时查找并存储引用。它的用法是例如$entity->setStatus($statusRef->accepted)$entity->setStatus($statusRef->get(SubmissionStatus::ACCEPTED_SLUG))。第一个选项会中断 IDE 代码完成。第二个有点太长了,虽然比$em->getRepository->find短了点

【问题讨论】:

    标签: symfony doctrine-orm


    【解决方案1】:

    有一个属性注释可以帮助您的 IDE 保持愉快。

    /**
     * One entry for each status 
     * @property-read SubmissionStatus $accepted 
     */
    class SubmissionStatusReference
    {
        public function __get($slug) {
            switch($slug) {
                // blah blah blah
                case 'accepted': 
                    return $this->entityManager->getReference(etc...
    

    我认为这是真正回答您的问题的最简洁的方式。

    但是话虽如此,我最终还是停止了做这种事情,而不是制作实际的状态实体,我只使用 slug。最初我担心能够改变价值观,但实际上我几乎不需要这样做。我使用视图转换器将蛞蝓转换为您所称的名称。而且根本没有id。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-22
      • 1970-01-01
      • 2018-08-01
      • 1970-01-01
      • 2017-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多