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

标签 laravel laravel-4 eloquent

我有一个用于文章的已发布过滤器。 guest 只能查看已发布的文章,登录用户可以查看和应用过滤器(?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 模型本身中定义它,因为在实现新功能或 View 时很容易忘记包含此过滤器。

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

最佳答案

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


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

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


捷径:为此您需要全局范围。以下是分两步完成的方法(略微压缩):

1 创建一个实现ScopeInterface的类PublishedScope

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,但这取决于你。


edit remove 方法已更新 - 感谢@Leon 指出在将此作用域与 SoftDeletingTrait 一起使用时的意外行为。问题有点深:

当你将这个与 SoftDeletingScope 或另一个一起使用时,它利用了 NULLNOT NULL 约束 and此作用域不是第一个使用的(是的,use 语句的顺序在这里很重要),remove 方法将无法按预期工作。它不会删除任何绑定(bind),也不会删除它应该删除的绑定(bind)。

关于laravel - 全局过滤——如何在 Laravel Eloquent 中使用全局作用域,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26095541/

相关文章:

php - 获取数据库信息的更好方法

mysql - 违反完整性约束 : 1052 Column and in where clause is ambiguous

php - 选择前 10 行 - Laravel Eloquent

unit-testing - Laravel mock

laravel - 具有替代键的 Eloquent ManyToMany

javascript - 如何强制 Restangular 的 getList 与 Laravel 5 的分页对象一起使用?

Laravel Eloquent 地从 json 列的值中过滤

laravel - Laravel 4 中命名的 Restful 路线

php - Laravel 4,在发布方法/可能中删除图像?

php - 使用 composer install 在 ProviderRepository.php 错误中找不到类