CakePHP 2.X: 复合(连接多个数据库列)虚拟字段作为显示字段

CakePHP 2.X: compound (concatenate several db columns) virtualfield as displayfield

我已经回答了我自己的问题,但我不介意接受任何其他确实比我的方法更好的答案(请提及它如何更好)。请投稿。


我曾遇到过这样一种情况,在我的 User Model 中,我将 first_name, middle_name, last_name 作为 3 个单独的字段,并且只有 first_name 针对 empty/NULL 进行了验证,即其他 2可以是 empty/NULL/whitespace(不想用不合理的验证来惹恼用户,我个人认识一些人 have/use 因为一些疯狂的原因不姓氏)。

有了这样的结构,displayfield 是所有这 3 列的复合,记住其他 2 可能是“”/NULL/white-space(因为它们都是VARCHAR 列)。此外,我的数据库 table 有像 created_bymodified_byapproved_byassigned_to 这样的列...(这个模型还有很多其他别名,但所有其中在 "CakePHP convention").

之后关系密切

我读了official documentation and several posts here on SO like this one。但其中 none 为这种情况提供了解决方案或示例。也许,我的数据库结构太不传统了,或者我会说 "unorthodox" ;)(好吧,官方文档确实回答了多个模型别名,我错过了好几次,这促使我 post 这个问题并在此处回答。

那么,问题是如何正确处理这样的模型关系和结构?

TL; DR; - 跳转到最终解决方案最后

我将分两部分回答我自己的问题 -

  1. 如何解决虚拟字段中的多个模型别名
  2. 如何正确连接字段以获得所需的字段"full name"

1。 模型别名

CakePHP 的官方文档 2.X 有一节关于 Virtual fields and model aliases

When you are using virtualFields and models with aliases that are not the same as their name, you can run into problems as virtualFields do not update to reflect the bound alias. If you are using virtualFields in models that have more than one alias it is best to define the virtualFields in your model’s constructor

所以 User.PHP 中的这样的东西应该允许 virtualFields 为你给模型的任何别名工作:

public function __construct($id = false, $table = null, $ds = null) {
    parent::__construct($id, $table, $ds);
    $this->virtualFields['name'] = sprintf(
        'CONCAT(%s.first_name, " ", %s.middle_name, " ", %s.last_name)', $this->alias, $this->alias, $this->alias
    );
}

2。 空中间名和姓氏

这可以通过模型构造函数中的一些附加 SQL 语法来回答,为了简单起见,我使用 CASE ... WHEN ... THEN ... ELSE ... END,可以用更好的 SQL(如果可能)代替。但是这个也没有失败(而且还不错)。

CONCAT(%s.first_name, CASE WHEN %s.middle_name = "" THEN " " ELSE CONCAT(" ", %s.middle_name, " ") END, %s.last_name)

最终解

在模型构造函数中:

public function __construct($id = false, $table = null, $ds = null) {
    parent::__construct($id, $table, $ds);
    $this->virtualFields['full_name'] = sprintf(
        'CONCAT(%s.first_name, CASE WHEN %s.middle_name = "" THEN " " ELSE CONCAT(" ", %s.middle_name, " ") END, %s.last_name)', $this->alias, $this->alias, $this->alias, $this->alias
    );
}

最后,通常的 public $displayField = 'full_name';

现在,无论您在控制器中执行 $this->RelatedModel->UserAlias->find('list');,您都会得到一个包含 idfull_name 用户的数组。

希望这对某人有帮助,或者至少可以作为我自己的日记:)