【问题标题】:Laravel/Eloquent: calculated database field in model howLaravel/Eloquent:如何在模型中计算数据库字段
【发布时间】:2017-09-02 11:50:44
【问题描述】:

(Laracast 上也发布了问题,还没有给出任何答案,所以我试试 Stackoverflow)

大家好,

我必须用一个模型来解决 Eloquent 中的一个数据库问题,该模型有一个要从不同数据库中读取的字段。由于我很难用英语/OOP/Laravel/Eloquent/PHP 术语正确表达我的问题,我将尝试在一个示例中表达我的需求(不太关心正确的 PHP 语法)。

鉴于有一个类似于树状结构的模型,使用的属性通常是:

[treenode]
id
parent_id (=self referencing key to id)
name
...

现在说树是存储指向不同类型节点的指针,比如房屋、汽车和用户,每个节点都存储在不同的表/模型中:

[house]
id
name
street address
zip
...

[car]
id
name
brand
horsepower
...

[user]
id
name
age
gender
...

问题显然是有一个“treenode”模型,它透明地访问其他数据库以填充所有模型共有的“name”字段,以绘制树。

我想过把treenode模型改成:

[treenode]
id
parent_id
kind (house | car | user)
data_id (key into house | car | user table)
...

下一步是在 Eloquent 中“以某种方式”对“名称”字段进行编码,因此它看起来仍然是“树节点”模型的一部分,这就是我被卡住的地方。我在模型中尝试了一个“计算字段”,如文档中所述,如下所示:

public function name() {

select case $this->kind {
    case house: return House::findorfail($this->data_id)->first()
    case car: return Car::findorfail($this->data_id)->first()
    case user: return User::findorfail($this->data_id)->first()
}
}

乍一看这确实有效,但显然“名称”并未真正包含在模型的属性列表中,因此它不会被序列化,即如果我对树节点进行 JSON_encode,则不包含“名称”。

通过更改树节点设置“名称”时也会出现类似的问题,如果要更改名称,我必须将其写回正确的数据库,并且还需要 JSON 支持。我不知道我怎么能做到这一点。我阅读了有关访问器的信息,例如 getNameAttribute 和 setNameAttribute,但据我所知,它们仅在树节点模型中有“名称”字段时才有效。

任何人都可以将我的鼻子指向正确的方向...我可以以某种方式重新设计我的树节点模型,以便它包含一个“名称”字段,该字段从其他表中获取其内容,并且,如果我为其分配一个值,则写入将名称添加到其他表中,但在其他方面的行为与包含在“treenode”模型中时完全相同?

谢谢,

阿明。

【问题讨论】:

  • 你使用的是什么版本的 Laravel?
  • V5.4,对不起,我忘了说。但是我已经看到了您提出的答案,这似乎很有希望。今晚我会试一试,尽快让你知道结果。同时:谢谢!

标签: php laravel eloquent field


【解决方案1】:

从阅读问题看来,laravel 的 Polymorphic Relations 是您问题的正确答案,因为您可以将模型与不同的模型链接起来,而不是将模型的类型“硬编码”为外键。

由于多态关系允许一个模型在单个关联上属于多个其他模型。

用例子检查一下:

https://laravel.com/docs/5.4/eloquent-relationships#polymorphic-relations

【讨论】:

  • 这似乎是要走的路,但罗斯似乎比你早了几秒钟才能给出答案。不过,非常感谢您的意见,看来这正是我要找的!
  • 先测试,然后欢呼 :-) 投票来了,承诺 :-)
【解决方案2】:

从您的示例看来,您正在关注Polymorphic Relationships

首先,将data_idkind 更改为kind_idkind_type

data_id 变为 kind_id 并且

kind 变为 kind_type

您可以在迁移文件中执行此操作:

$table->morphs('kind');

然后在您的treenode 模型中添加:

/**
 * Kind Relationship
 * 
 * @return \Illuminate\Database\Eloquent\Relations\MorphTo
 */
public function kind()
{
    return $this->morphTo();
}

/**
 * Get the name from the "kind" relationship
 * 
 * @return mixed
 */
public function getNameAttribute()
{
    return $this->kind->name;
}

然后您可以执行以下操作:

$treeNode = App\TreeNode::with('kind')->first();

$treeNode->name; //This would be the name on the other model

(以上假设您的treenode 模型是App\TreeNode。如果不是,则只需将其更改为正确的)。

编辑

如果您已经在使用生产环境/需要保持数据不变,那么您需要将 kind_type 更新为它所引用模型的完全限定命名空间,例如

DB::table('treenode')
    ->where('kind_type', 'user')
    ->update(['kind_type' => \App\User::class]);

您需要对所有不同的kind_types 执行此操作(您只需在开始时执行此操作,之后使用associate() 方法时会自动执行此操作)。

或者,您可以设置变形图:

use Illuminate\Database\Eloquent\Relations\Relation;

Relation::morphMap([
    'user' => 'App\User',
]);

您可以将上述内容添加到您的AppServiceProvider 中的boot() 方法中。

【讨论】:

  • 嗨,罗斯,到目前为止运气不佳。两个问题:首先,我按照您的建议更改了模型,但 Treenode 模型似乎仍然没有包含“名称”属性,这是我想要实现的目标之一。
  • 此外,命名空间似乎存在严重问题,我的模型没有找到。我收到此错误:“Symfony\Component\Debug\Exception\FatalThrowableError: Class 'user' not found in [...]\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Concerns\HasRelationships.php:487 . 我尝试将 use App\User 语句放入我的控制器以及 Treenode 模型文件中,但我可以在这两个文件中成功查询任何用户模型,例如 $test=User::find(6),不过当我从控制器调用 $node->kind->name 时,错误仍然存​​在。
  • 您是否也更改了表格列名?确保您遵循 PSR-1 和 PSR-4,例如类名与文件名完全匹配,它们是用StudlyClass编写的,即User.phpclass User而不是user
  • 我完全按照您的指示进行操作。列名是“kind_id”和“kind_type”,如果我在 kind_type = “user” 的节点上查询 $node->kind->name,则找不到“user”模型,如果我将 kind_type 设置为“house”,消息更改为“house”类未找到。变形确实解决了正确的模型,但未能找到它。它看起来像是名称空间和范围的问题。模型没问题,因为我可以成功查询控制器以及“Treenode”模型中的每个模型。
  • 任何模型都会受到影响,对于我的演示,我选择 Laravel 的身份验证组件提供的(未更改的)“用户”模型来排除我自己的模型的任何问题。
猜你喜欢
  • 2016-08-12
  • 1970-01-01
  • 2021-11-08
  • 2015-07-21
  • 1970-01-01
  • 2017-04-03
  • 2016-12-22
  • 2021-06-13
  • 2013-01-30
相关资源
最近更新 更多