全局过滤 - 如何在 Laravel Eloquent 中使用全局作用域

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26095541/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 10:12:02  来源:igfitidea点击:

Global filtering - how to use global scope in Laravel Eloquent

laravellaravel-4eloquent

提问by Thomas Jensen

I have a published filter that I use for my articles. Guests can only view published articles, logged in users can view and apply filter (?published=0/1):

我有一个已发布的过滤器,用于我的文章。访客只能查看已发表的文章,登录用户可以查看和应用过滤器(?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;
}

I apply this in my ArticlesController:

我将其应用于我的ArticlesController

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

And on the article relationships:

关于文章关系:

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

But ideally I would like to only define it in the Articlemodel itself, since it's easy to forget to include this filter when implementing new features or views.

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

How can I make sure that all returned articles from the Articlemodel are run through this filter before returned?

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

回答by Jarek Tkaczyk

UPDATE: Just use this: https://github.com/jarektkaczyk/laravel-global-scopefor global scopes in L5+

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



Better way is a bit too long to paste it and works like SoftDeletingthing in the core.

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

Read this if you want it http://softonsofa.com/laravel-how-to-define-and-use-eloquent-global-scopes/

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



Short way: you need global scope for this. And here's how you do it in 2 steps (squashed a bit):

简短的方法:您需要全局范围。这是你如何分两步完成(压扁一点):

1 Create a class PublishedScopethat implements ScopeInterface

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 Boot that class in your Eloquent model by calling static::addGlobalScope(new AbcScope)

2 通过调用在 Eloquent 模型中启动该类 static::addGlobalScope(new AbcScope)

// the model
public static function boot()
{
    parent::boot();

    static::addGlobalScope(new PublishedScope);
}

If I were you I would use published_atcolumn and check it for nullinstead of = 1, but that's up to you.

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



editremovemethod updated - thanks to @Leon for pointing out unexpected behaviour, when using this scope together with SoftDeletingTrait. The problem is a bit deeper:

编辑remove方法更新 - 感谢@Leon 指出意外行为,当将此范围与SoftDeletingTrait. 问题有点深:

when you use this one with SoftDeletingScopeor another one, that utilizes NULLor NOT NULLconstraint andthis scope is not the first one used (yes, order of usestatements matters here), removemethod will not work as expected. It will not remove any binding or not the one, that it should.

当您使用这个SoftDeletingScope或另一个使用NULLNOT NULL约束并且此范围不是第一个使用的范围时(是的,use这里的语句顺序很重要),remove方法将无法按预期工作。它不会删除任何绑定或不删除它应该删除的绑定。

回答by Sagar Rabadiya

you can use trait and add your method or filter thing in booting method check the following http://laravel.com/docs/4.2/eloquent#global-scopes

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