在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;
答案 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实体是可能的,但它有一些限制:
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
中解释了它的工作原理