Doctrine可以定义MySQL的Generated列吗?

问题描述 投票:4回答:3

在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;
php mysql doctrine-orm symfony
3个回答
2
投票

首先,我要为这个答案为时已晚而道歉,但我能够通过DQL为基本的SELECT和JOIN查询创建一个解决方法。由于修改了生成的实体,我没有使用UPDATE查询对此进行测试。

正如上面提到的用户dbu,你需要首先遵循这个指南:https://www.liip.ch/en/blog/doctrine-and-generated-columns

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

您需要确保创建添加生成列的迁移。我这样做了:

/**
 * 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查询期间尝试修改数据库上的结果。

诀窍是创建另一个实体,它扩展了您当前的Contacts实体,并且仅用于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注释会阻止doctrines架构构建器尝试创建具有相同名称的表。

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

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

这将返回一组ContactGenerated实体。如果你试图坚持它们,你会遇到问题,你应该把它们视为只读。

我将由你来决定如何将这些转换为标准的Contact类,这会删除不存在的“fullname”属性,这将允许你从select执行UPDATE查询。

我真的希望这可以帮助你,因为它为我的用例做了 - 任何问题,随时问:)


1
投票

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


0
投票

我相信这是公正的

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

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


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;

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

© www.soinside.com 2019 - 2024. All rights reserved.