Laravel 检查相关模型是不是存在
Posted
技术标签:
【中文标题】Laravel 检查相关模型是不是存在【英文标题】:Laravel Check If Related Model ExistsLaravel 检查相关模型是否存在 【发布时间】:2014-07-17 14:43:48 【问题描述】:我有一个 Eloquent 模型,它有一个相关模型:
public function option()
return $this->hasOne('RepairOption', 'repair_item_id');
public function setOptionArrayAttribute($values)
$this->option->update($values);
当我创建模型时,它不一定有相关的模型。当我更新它时,我可能会添加一个选项。
所以我需要检查相关模型是否存在,分别更新或创建它:
$model = RepairItem::find($id);
if (Input::has('option'))
if (<related_model_exists>)
$option = new RepairOption(Input::get('option'));
$option->repairItem()->associate($model);
$option->save();
$model->fill(Input::except('option');
else
$model->update(Input::all());
;
<related_model_exists>
是我要查找的代码。
【问题讨论】:
真棒的问题,谢谢!并为下面的人提供了很好的答案。节省了我的项目时间。 【参考方案1】:在 php 7.2+ 中,您不能在关系对象上使用count
,因此没有适用于所有关系的万能方法。改用查询方法,如下面提供的@tremby:
$model->relation()->exists()
适用于所有关系类型的通用解决方案(pre php 7.2):
if (count($model->relation))
// exists
这适用于每个关系,因为动态属性返回 Model
或 Collection
。两者都实现了ArrayAccess
。
原来是这样的:
单身关系: hasOne
/ belongsTo
/ morphTo
/ morphOne
// no related model
$model->relation; // null
count($model->relation); // 0 evaluates to false
// there is one
$model->relation; // Eloquent Model
count($model->relation); // 1 evaluates to true
多对多关系: hasMany
/ belongsToMany
/ morphMany
/ morphToMany
/ morphedByMany
// no related collection
$model->relation; // Collection with 0 items evaluates to true
count($model->relation); // 0 evaluates to false
// there are related models
$model->relation; // Collection with 1 or more items, evaluates to true as well
count($model->relation); // int > 0 that evaluates to true
【讨论】:
阅读全文。count($relation)
是所有关系的通用解决方案。它适用于Model
和Collection
,而Model
没有->count()
方法。
@CurvianVynes 不是,不是。 Collection
有自己的方法 isEmpty
,但通用的 empty
函数对对象返回 false(因此不适用于空集合)。
当关系尚未设置关联时,count($model->relation)
在 morphTo
上不起作用。外部 id 和 type 为 null 并且 Laravel 构建的 db 查询是伪造的并引发异常。我使用$model->relation()->getOtherKey()
作为解决方法。
@Jocelyn 是的,这是 Eloquent 错误。不幸的是,其中至少有一些用于多态关系,所以显然你不能以任何方式依赖它们。
它将在 php 7.2 上中断,返回:count(): Parameter must be an array or an object that implements Countable
【参考方案2】:
Relation object 将未知的方法调用传递给Eloquent query Builder,该Eloquent query Builder 设置为仅选择相关对象。该 Builder 反过来将未知的方法调用传递给 its 底层 query Builder。
这意味着您可以直接从关系对象中使用exists()
或count()
方法:
$model->relation()->exists(); // bool: true if there is at least one row
$model->relation()->count(); // int: number of related rows
注意relation
后面的括号:->relation()
是一个函数调用(获取关系对象),而不是 ->relation
,它是 Laravel 为您设置的魔法属性 getter(获取相关对象/对象) .
在关系对象上使用count
方法(即使用括号)将比使用$model->relation->count()
或count($model->relation)
(除非关系已经被预先加载)快得多,因为它运行一个计数查询而不是从数据库中提取任何相关对象的所有数据,只是为了计算它们。同样,使用exists
也不需要拉取模型数据。
exists()
和 count()
都适用于我尝试过的所有关系类型,所以至少 belongsTo
、hasOne
、hasMany
和 belongsToMany
。
【讨论】:
exists 在流明中不可用,不知道为什么。 @briankip -- 应该。你确定你得到的是关系对象(通过调用方法)而不是集合(通过使用魔法属性)? 至少在 Laravel 6.x 中,exists
不适用于不存在的 morphTo
关系。它得到一个 SQL 错误。
请记住,在关系上调用exists()
和count()
要求相关模型已经保存在数据库中。如果您需要在保存相关模型之前检查是否存在(例如,如果您使用了 setRelation),那么您应该使用is_null
或empty
。
@OrmanFaghihiMohaddes:我的回答中关于查询构建器的文字只是解释它如何工作的一部分。您正在通过您在模型上定义的关系访问查询构建器,所以是的,您绝对仍然在使用您在模型上定义的关系。【参考方案3】:
不确定这在 Laravel 5 中是否发生了变化,但使用 count($data->$relation)
接受的答案对我不起作用,因为访问关系属性的行为导致它被加载。
最后,直截了当的isset($data->$relation)
为我解决了问题。
【讨论】:
我相信是$data->relation
没有$
(由于6 个字符限制,无法编辑)
啊,$relation
将是您的关系名称,例如 $data->posts
或类似的名称。抱歉,如果这令人困惑,我想澄清relation
不是具体的模型属性:P
这工作了一段时间,但在我将 Laravel 从 5.2.29 更新到 5.2.45 后它停止工作。知道为什么或如何解决它吗?现在由于某种原因导致关系数据被加载。
我添加了一个可以解决此问题的答案。
这在最新版本的 Laravel 中不起作用。至少从 Laravel 5.8 开始,Model::__isset
方法被重载,即使没有相关实体它也会返回 true。您需要使用!is_null
来避免神奇的isset
逻辑。他们已经确认这是 Laravel 中的一个已知错误,将在 Laravel 8 中修复:github.com/laravel/framework/issues/31793【参考方案4】:
我更喜欢使用exists
方法:
RepairItem::find($id)->option()->exists()
检查相关模型是否存在。它在 Laravel 5.2 上运行良好
【讨论】:
+1; count($model->relation) 在 Laravel 5.2 中为我返回 true,即使关系表中没有项目。 ->exists() 可以解决问题。【参考方案5】:您可以在模型对象上使用relationLoaded 方法。这救了我的培根,所以希望它可以帮助别人。当我在 Laracasts 上问同样的问题时,我是 given this suggestion。
【讨论】:
【参考方案6】:Php 7.1 之后,接受的答案将不适用于所有类型的关系。
因为根据关系的类型,Eloquent 将返回 Collection
、Model
或 Null
。而在 Php 7.1 中,count(null)
将抛出一个 error
。
因此,要检查关系是否存在,您可以使用:
对于单一关系:例如hasOne
和belongsTo
if(!is_null($model->relation))
....
对于多重关系:例如:hasMany
和 belongsToMany
if ($model->relation->isNotEmpty())
....
【讨论】:
非常适合我!如果我预先加载关系并在结果的 foreach 循环中执行 ->count(),即使它们在模型中预先加载,它也会为每个项目生成一个 SQL 查询。使用 !is_null($model->relation) 是最快且对 SQL 友好的方式。非常感谢。【参考方案7】:正如 Hemerson Varela 在 Php 7.1 中所说,count(null)
将抛出 error
和 hasOne
如果不存在行则返回 null
。既然你有hasOne
relation 我会使用empty
方法来检查:
$model = RepairItem::find($id);
if (!empty($temp = $request->input('option')))
$option = $model->option;
if(empty($option))
$option = $model->option()->create();
$option->someAttribute = temp;
$option->save();
;
但这是多余的。无需检查关系是否存在,以确定您是否应该进行update
或create
调用。只需使用updateOrCreate 方法。这相当于上面的:
$model = RepairItem::find($id);
if (!empty($temp = $request->input('option')))
$model->option()
->updateOrCreate(['repair_item_id' => $model->id],
['option' => $temp]);
【讨论】:
【参考方案8】:当我将我的 PHP 版本更新到 7.2+ 时,我不得不完全重构我的代码,因为 count($x) 函数的使用不当。这是一个真正的痛苦,也非常可怕,因为有数百种用法,在不同的场景中,没有一个规则适合所有..
我遵循的重构一切的规则,示例:
$x = Auth::user()->posts->find(6);(使用 ->find() 检查用户的帖子 id=6)
[FAILS] if(count($x)) return 'Found';
[GOOD] if($x) return 'Found';
$x = Auth::user()->profile->departments;(检查profile是否有部门,可以有很多部门)
[FAILS] if(count($x)) return 'Found';
[GOOD] if($x->count()) return 'Found';
$x = Auth::user()->profile->get(); (使用 ->get() 后检查用户是否有个人资料)
[FAILS] if(count($x)) return 'Found';
[GOOD] if($x->count()) return 'Found';
希望这能有所帮助,即使在提出问题 5 年后,这篇 *** 帖子也对我有很大帮助!
【讨论】:
【参考方案9】:如果您使用模型类并使用 Eloquent ORM,则创建一个新方法并返回 bool 数据。喜欢
public function hasPosts(): bool
return $this->posts()->exists();
【讨论】:
【参考方案10】:我用于单身关系:hasOne
、belongsTo
和 morphs
if($model->relation)
....
因为如果条件为空,这将是错误的。
对于多个关系:hasMany
、belongsToMany
和 morphs
if ($model->relation->isNotEmpty())
....
【讨论】:
以上是关于Laravel 检查相关模型是不是存在的主要内容,如果未能解决你的问题,请参考以下文章
Laravel 中自动删除相关行(Eloquent ORM)
如何通过多对多关系获取与同一张表相关的行 - Laravel