【问题标题】:Laravel relationships avoid query where foreign key is nullLaravel 关系避免外键为空的查询
【发布时间】:2016-10-12 22:05:41
【问题描述】:

在预加载时,当关系中的外键为空,因此不匹配任何相关记录时,是否可以避免执行额外的查询?在我的示例中,我有一个 ProductUser

ProductUser 所有,但也可以选择由User 编辑。所以我的模型看起来像这样:

class Product extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function editor()
    {
        return $this->belongsTo(User::class, 'editor_id');
    }
}

当一个产品没有被编辑时,它的editor_id属性是NULL

如果我不急于加载,我知道我可以执行以下操作:

$product = Product::find(1);
if (!is_null($product->editor_id)) {
    // Load the relation
}

但是,这对我来说不是一个选项,理想情况下,我希望避免在急切加载时运行额外的、不必要的查询:

Query: select * from `users` where `users`.`id` in (?)
Bindings: [0]

我想知道类似以下的事情是否可行?

public function editor()
{
    if (!is_null($this->editor_id)) {
        return $this->belongsTo(User::class, 'editor_id');
    }
}

执行上述操作时出现此错误:

Call to a member function addEagerConstraints() on a non-object

我猜这是因为不能保证此方法返回 Relation 对象。

谢谢

【问题讨论】:

  • 这只是您帖子中的错字还是您实际运行了此代码?因为USer::class 应该是User::class
  • 好吧,你不能那样做,你的关系必须返回一些东西,否则,Laravel 会尝试在 null 上调用一个方法
  • 使用 whereHas 条件,也许对你有帮助。
  • 大家好,感谢您的回复。 @PeterPan666 - 是的,我知道它需要返回一些东西,这就是我收到错误的原因。 @Imtiaz Pabel - 在这种情况下我将如何使用whereHas?能举个例子吗?

标签: laravel orm laravel-5 eloquent


【解决方案1】:

Laravel 文档

选择时查询关系

访问模型的记录时,您可能希望根据关系的存在来限制结果。

 $posts= Post::has('editor')->get();

您还可以指定运算符和计数:

  $posts = Post::has('editor', '>=', 3)->get();

【讨论】:

    【解决方案2】:

    我通过创建一个新的Relation 子类解决了这个问题,该子类实现了所需的方法,但在实际获取结果时只返回 null:

    namespace My\App;
    
    use Illuminate\Database\Eloquent\Collection;
    use Illuminate\Database\Eloquent\Relations\Relation;
    
    class NullRelation extends Relation {
    
        public function __construct() {}
    
        public function addConstraints() {}
    
        public function addEagerConstraints(array $models) {}
    
        public function initRelation(array $models, $relation) {}
    
        public function match(array $models, Collection $results, $relation) {
            return [];
        }
    
        public function getResults() {
            return null;
        }
    
    }
    

    然后在您的关系方法中,您可以检查 null 并返回 NullRelation 的实例:

    public function editor() {
        if ($this->editor_id === null) {
            return new NullRelation();
        } else {
            return $this->belongsTo(User::class, 'editor_id');
        }
    }
    

    它有点难看,而且你会经常重复自己,所以如果我在不止一个地方使用它,我可能会创建Model 的子类,然后创建belongsTo 的版本, hasOne 方法并在那里执行检查:

    public function belongsToIfNotNull($related, $foreignKey = null, $otherKey = null, $relation = null) {
        if ($this->$foreignKey === null) {
            return new NullRelation();
        } else {
            return parent::belongsTo($related, $foreignKey, $otherKey, $relation);
        }
    }
    

    最后在继承新子类的模态中,你的关系方法就变成了

    public function editor() {
        return $this->belongsToIfNotNull(User::class, 'editor_id');
    }
    

    【讨论】:

    • +1 我真的很喜欢你的方法。我什至会考虑简单地覆盖模型类上的默认关系方法来执行此操作,因为我发现额外的查询完全没有必要。
    猜你喜欢
    • 2015-12-05
    • 2014-11-27
    • 1970-01-01
    • 2012-02-02
    • 2020-01-02
    • 1970-01-01
    • 2010-09-10
    • 1970-01-01
    • 2016-12-22
    相关资源
    最近更新 更多