Laravel default orderBy

后端 未结 8 1797
离开以前
离开以前 2020-12-24 10:51

Is there a clean way to enable certain models to be ordered by a property by default? It could work by extending the laravel\'s QueryBuilder, but to do so,

8条回答
  •  半阙折子戏
    2020-12-24 11:28

    Before Laravel 5.2

    Nowadays we can solve this problem also with global scopes, introduced in Laravel 4.2 (correct me if I'm wrong). We can define a scope class like this:

    column = $column;
            $this->direction = $direction;
        }
    
        public function apply(Builder $builder, Model $model)
        {
            $builder->orderBy($this->column, $this->direction);
    
            // optional macro to undo the global scope
            $builder->macro('unordered', function (Builder $builder) {
                $this->remove($builder, $builder->getModel());
                return $builder;
            });
        }
    
        public function remove(Builder $builder, Model $model)
        {
            $query = $builder->getQuery();
            $query->orders = collect($query->orders)->reject(function ($order) {
                return $order['column'] == $this->column && $order['direction'] == $this->direction;
            })->values()->all();
            if (count($query->orders) == 0) {
                $query->orders = null;
            }
        }
    }
    

    Then, in your model, you can add the scope in the boot() method:

    protected static function boot() {
        parent::boot();
        static::addGlobalScope(new OrderScope('date', 'desc'));
    }
    

    Now the model is ordered by default. Note that if you define the order also manually in the query: MyModel::orderBy('some_column'), then it will only add it as a secondary ordering (used when values of the first ordering are the same), and it will not override. To make it possible to use another ordering manually, I added an (optional) macro (see above), and then you can do: MyModel::unordered()->orderBy('some_column')->get().

    Laravel 5.2 and up

    Laravel 5.2 introduced a much cleaner way to work with global scopes. Now, the only thing we have to write is the following:

    column = $column;
            $this->direction = $direction;
        }
    
        public function apply(Builder $builder, Model $model)
        {
            $builder->orderBy($this->column, $this->direction);
        }
    }
    

    Then, in your model, you can add the scope in the boot() method:

    protected static function boot() {
        parent::boot();
        static::addGlobalScope(new OrderScope('date', 'desc'));
    }
    

    To remove the global scope, simply use:

    MyModel::withoutGlobalScope(OrderScope::class)->get();
    

    Solution without extra scope class

    If you don't like to have a whole class for the scope, you can (since Laravel 5.2) also define the global scope inline, in your model's boot() method:

    protected static function boot() {
        parent::boot();
        static::addGlobalScope('order', function (Builder $builder) {
            $builder->orderBy('date', 'desc');
        });
    }
    

    You can remove this global scope using this:

    MyModel::withoutGlobalScope('order')->get();
    

提交回复
热议问题