ChatGPT解决这个技术问题 Extra ChatGPT

Laravel 检查相关模型是否存在

我有一个具有相关模型的 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> 是我要查找的代码。

真棒问题谢谢!并为下面的人提供了很好的答案。节省了我的项目时间。

J
Jarek Tkaczyk

php 7.2+ 中,您不能在关系对象上使用 count,因此没有适用于所有关系的万能方法。改用查询方法,如下面提供的@tremby:

$model->relation()->exists()

适用于所有关系类型的通用解决方案(php 7.2 之前):

if (count($model->relation))
{
  // exists
}

这适用于每个关系,因为动态属性返回 ModelCollection。两者都实现 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) 是所有关系的通用解。它适用于 ModelCollection,而 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
t
tremby

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() 都适用于我尝试过的所有关系类型,因此至少有 belongsTohasOnehasManybelongsToMany


存在在流明中不可用,不知道为什么。
@briankip - 它应该。你确定你得到的是关系对象(通过调用方法)而不是集合(通过使用魔法属性)?
至少在 Laravel 6.x 中,exists 不适用于不存在的 morphTo 关系。它得到一个 SQL 错误。
请记住,对关系调用 exists()count() 要求相关模型已保存在数据库中。如果您需要在保存相关模型之前检查是否存在(例如,如果您使用了 setRelation),那么您应该使用 is_nullempty
@OrmanFaghihiMohaddes:我的回答中关于查询构建器的文字只是解释它如何工作的一部分。您正在通过您在模型上定义的关系访问查询构建器,所以是的,您绝对仍然在使用您在模型上定义的关系。
H
Hafez Divandari

我更喜欢使用 exists 方法:

RepairItem::find($id)->option()->exists()

检查相关模型是否存在。它在 Laravel 5.2 上运行良好


+1; count($model->relation) 在 Laravel 5.2 中为我返回 true,即使关系表中没有项目。 ->exists() 可以解决问题。
H
Hemerson Varela

在 PHP 7.1 之后,接受的答案将不适用于所有类型的关系。

因为根据关系的类型,Eloquent 将返回 CollectionModelNull。而在 Php 7.1 中,count(null) 会抛出一个 error

因此,要检查关系是否存在,您可以使用:

对于单一关系:例如 hasOnebelongsTo

if(!is_null($model->relation)) {
   ....
}

对于多个关系:例如:hasManybelongsToMany

if ($model->relation->isNotEmpty()) {
   ....
}

非常适合我!如果我预先加载关系并在结果的 foreach 循环中执行 ->count(),即使它们在模型中预先加载,它也会为每个项目生成一个 SQL 查询。使用 !is_null($model->relation) 是最快且对 SQL 友好的方式。非常感谢。
A
Anthony

您可以对模型对象使用 relationLoaded 方法。这救了我的培根,所以希望它可以帮助别人。当我在 Laracasts 上问同样的问题时,我是 given this suggestion


A
Adam

正如 Hemerson Varela 在 Php 7.1 中所说,count(null) 将抛出 error,如果不存在行,hasOne 将返回 null。由于您有 hasOne 关系,我将使用 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();
};

但这是多余的。无需检查关系是否存在,以确定您是否应该进行 updatecreate 调用。只需使用 updateOrCreate 方法。这相当于上面的:

$model = RepairItem::find($id);
if (!empty($temp = $request->input('option'))) {  
   $model->option()
         ->updateOrCreate(['repair_item_id' => $model->id],
                          ['option' => $temp]);
}

D
Dave Stewart

不确定这是否在 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
M
Maxim Paladi

我用于单一关系:hasOnebelongsTomorphs

if($model->relation){ 
 ....
}

因为如果条件为空,这将是错误的。

对于多个关系:hasManybelongsToManymorphs

if ($model->relation->isNotEmpty()) {
   ....
}

r
raphjutras

当我将我的 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; (检查配置文件是否有一些部门,可以有很多部门)

[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 年后,这篇 stackoverflow 帖子也对我有很大帮助!


M
MD Rasel

如果您使用模型类并使用 Eloquent ORM,则创建一个新方法并返回 bool 数据。喜欢

public function hasPosts(): bool
{
    return $this->posts()->exists();
}