DocDestrine可以定义MySQL的Generated列吗?

时间:2016-09-12 11:42:19

标签: php mysql doctrine-orm symfony

在Symfony实体中使用mySQL's generated column是否正确(如果是这样)?

例如,以下示例中的GENERATED ALWAYS

CREATE TABLE contacts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    fullname varchar(101) GENERATED ALWAYS AS (concat(first_name,' ',last_name)),
    email VARCHAR(100) NOT NULL
);

我理解这可以在实体__construct()中完成,但是让它由Doctrine / mySQL处理会更正确吗?类似的东西:

/**
 * @ORM\Column(type="text")
 * @ORM\Generated(concat(first_name,' ',last_name))
 */
private $fullname;

4 个答案:

答案 0 :(得分:2)

首先,我想为这个答案这么晚表示歉意,但是我能够通过DQL为基本的SELECT和JOIN查询创建解决方法。由于修改了结果实体,因此我尚未使用UPDATE查询进行测试。

正如上面的dbu用户所述,您首先需要遵循以下指南: https://www.liip.ch/en/blog/doctrine-and-generated-columns

这可以防止在运行架构构建器时该学说试图修改生成的列。实体更新发生时,这没有教义忽略列。

您将要确保创建一个迁移,以添加生成的列。我是这样实现的:

/**
 * Adds a generated column for fullname to the contacts table
 *
 * @param Schema $schema
 */
public function up(Schema $schema)
{
    $this->addSql('
        ALTER TABLE
            contacts
        ADD COLUMN 
            fullname varchar(101) GENERATED ALWAYS AS (concat(first_name,' ',last_name));
    ');
}

通过上述基础工作,您应该能够使学说使用模式构建器正常生成数据库模式,并且在通过迁移添加生成的列时不会受到干扰。

现在,下一个问题是确保您的数据可以合并到联系人实体上,而无需尝试在UPDATE和INSERT查询期间修改数据库上的结果。

技巧是创建另一个扩展您当前联系人实体的实体,该实体仅用于SELECT和JOIN查询。

<?php

    namespace Embark\ApiBundle\Entity;

    use Doctrine\ORM\Mapping as ORM;

    /**
     * Generated values from the contacts table
     *
     * @ORM\Table(name="contacts")
     * @ORM\MappedSuperclass
     */
    class ContactGenerated extends Contact
    {
        /**
         *
         * @ORM\Column(name="fullname", type="string", nullable=true)
         */
        private $fullname;
    }

@MappedSuperClass批注可防止教义模式构建器尝试创建具有相同名称的表。

然后您可以使用DQL来获取数据:

    $queryBuilder->select('contact')
        ->from(ContactGenerated::class, 'contact');

这将返回一个ContactGenerated实体数组。如果尝试保留这些问题,则会遇到问题,您应该真正将它们视为只读。

我将由您自己决定如何将它们转换为标准的Contact类,该类将删除不存在的“全名”属性,该属性将允许您从select进行UPDATE查询。

我真的希望这对您的用例有帮助,对您有帮助-如有任何疑问,请随时提问:)

答案 1 :(得分:0)

我相信它只是

/**
 * @ORM\Column(name="fullname", type="text")
 */
private $fullname;

数据将从通常的列中读取。但是你不应该允许编辑这个字段(不要设置setter)。

答案 2 :(得分:0)

将生成的列映射到Doctrine实体是可能的,但它有一些限制:

  • 您无法持久保存新实体,因为Doctrine会尝试插入生成的列,但会导致错误。
  • 架构工具(orm:schema-tool:update --dump-sql)始终认为您需要更改列。

一种可能的解决方法是使用过去的触发器:

CREATE TABLE contacts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    fullname varchar(101) NOT NULL,
    email VARCHAR(100) NOT NULL
);

CREATE TRIGGER contacts_before_insert BEFORE INSERT ON contacts
FOR EACH ROW BEGIN
    SET NEW.fullname = (CONCAT(NEW.first_name, ' ', NEW.last_name));
END;

CREATE TRIGGER contacts_before_update BEFORE UPDATE ON contacts
FOR EACH ROW BEGIN
    SET NEW.fullname = (CONCAT(NEW.first_name, ' ', NEW.last_name));
END;

其他可能的解决方法是将更新逻辑移动到实体中,但之后它不会反映直接执行到数据库中的更改。

答案 3 :(得分:0)

当您还需要编写实体时,有一种解决方法可在SQL中添加Generated Column,但使用Schema Listener将其从Doctrine ORM中完全隐藏。然后,您只能在Doctrine DBAL查询中使用该列,而不能与QueryBuilder / DQL一起使用。我在这篇博客文章https://www.liip.ch/en/blog/doctrine-and-generated-columns

中解释了它的工作原理