全局过滤 - 如何在Laravel Eloquent中使用全局范围

时间:2014-09-29 08:27:24

标签: laravel laravel-4 eloquent

我有一个已发布的过滤器,我用于我的文章。访客只能查看已发布的文章,登录的用户可以查看和应用过滤器(?published=0/1):

public function scopePublishedFilter($query)
{
    if(!Auth::check()) $query->where('published', '=', 1);
    else
    {
        $published = Input::get('published');
        if (isset($published)) $query->where('published', '=', $published);
    }

    return $query;
}

我在ArticlesController

中应用此功能
public function index()
{
    return View::make('articles.index', [
        'articles' => Article::with('owner')
            ->with('category')
            ->with('tags')
            ->publishedFilter()
            ->get()
    ]);
}

关于文章关系:

public function articles()
{
    return $this->hasMany('Article')->publishedFilter();
}

但理想情况下,我只想在Article模型中定义它,因为在实现新功能或视图时很容易忘记包含此过滤器。

如何确保Article模型中所有返回的文章在返回之前都通过此过滤器运行?

2 个答案:

答案 0 :(得分:4)

更新:只需使用:https://github.com/jarektkaczyk/laravel-global-scope用于L5 +中的全局范围


更好的方法是粘贴它有点太长,并且在核心中就像SoftDeleting一样。

如果您需要,请阅读此内容http://softonsofa.com/laravel-how-to-define-and-use-eloquent-global-scopes/


简短方法:您需要全球范围。以下是您分两步进行的操作(稍微压扁):

1创建一个实现PublishedScope

的班级ScopeInterface
class PublishedScope implements ScopeInterface {

public function apply(Builder $builder)
{
    $table = $builder->getModel()->getTable();
    $builder->where($table.'.published', '=', 1);
    $this->addWithDrafts($builder);
}

public function remove(Builder $builder)
{
    $query = $builder->getQuery();
    $column = $builder->getModel()->getTable().'.published';
    $bindingKey = 0;
    foreach ((array) $query->wheres as $key => $where)
    {
        if ($this->isPublishedConstraint($where, $column))
        {
            unset($query->wheres[$key]);
            $query->wheres = array_values($query->wheres);
            $this->removeBinding($query, $bindingKey);
        }

        // Check if where is either NULL or NOT NULL type,
        // if that's the case, don't increment the key
        // since there is no binding for these types
        if ( ! in_array($where['type'], ['Null', 'NotNull'])) $bindingKey++;
    }
}

protected function removeBinding(Builder $query, $key)
{
    $bindings = $query->getRawBindings()['where'];
    unset($bindings[$key]);
    $query->setBindings($bindings);
}

protected function addWithDrafts(Builder $builder)
{
    $builder->macro('withDrafts', function(Builder $builder)
    {
        $this->remove($builder);
        return $builder;
    });
}

2通过调用static::addGlobalScope(new AbcScope)

在您的Eloquent模型中启动该类
// the model
public static function boot()
{
    parent::boot();

    static::addGlobalScope(new PublishedScope);
}

如果我是你,我会使用published_at列并检查null而不是= 1,但这取决于你。


编辑 remove方法已更新 - 感谢@Leon在将此范围与SoftDeletingTrait一起使用时指出了意外行为。问题更深一点:

当您使用SoftDeletingScope或其他NULL时,使用NOT NULLuse约束时,此范围不是第一个使用的范围(是,remove语句的顺序在这里很重要,{{1}}方法将无法按预期工作。它不会删除任何绑定,也不会移除它应该的绑定。

答案 1 :(得分:0)

你可以使用trait并在启动方法中添加你的方法或过滤器检查以下内容 http://laravel.com/docs/4.2/eloquent#global-scopes