Laravel default orderBy

Есть ли чистый способ включения определенных моделей по умолчанию ? Он может работать, расширяя QueryBuilder от laravel, но для этого вам придется переделать некоторые из его основных функций – плохую практику.

причина

Главное, чтобы это сделать – одна из моих моделей сильно переработана многими другими, и сейчас вам приходится прибегать к приказанию снова и снова. Даже при использовании закрытия для этого вам все равно придется называть его. Было бы намного лучше использовать сортировку по умолчанию, поэтому все, кто использует эту модель и не предоставят настраиваемые параметры сортировки, получат записи, отсортированные по умолчанию. Использование репозитория здесь не является опцией, потому что он становится загруженным.

РЕШЕНИЕ

Расширение базовой модели:

protected $orderBy; protected $orderDirection = 'ASC'; public function scopeOrdered($query) { if ($this->orderBy) { return $query->orderBy($this->orderBy, $this->orderDirection); } return $query; } public function scopeGetOrdered($query) { return $this->scopeOrdered($query)->get(); } 

В вашей модели:

 protected $orderBy = 'property'; protected $orderDirection = 'DESC'; // ordering eager loaded relation public function anotherModel() { return $this->belongsToMany('SomeModel', 'some_table')->ordered(); } 

В вашем контроллере:

 MyModel::with('anotherModel')->getOrdered(); // or MyModel::with('anotherModel')->ordered()->first(); 

Да, вам нужно будет расширить Eloquent, чтобы всегда делать это стандартно для любого запроса. Что не так, если добавить заказ по запросу в запрос, когда вам это нужно? Это самый чистый путь, т. Е. Вам не нужно «вскрывать» «Красноречивый», чтобы получать результаты по естественному порядку.

 MyModel::orderBy('created_at', 'asc')->get(); 

Кроме того, самое близкое к тому, что вы хотите, – это создавать области запросов в ваших моделях.

 public function scopeOrdered($query) { return $query->orderBy('created_at', 'asc')->get(); } 

Затем вы можете вызвать ordered метод, а не get полученные упорядоченные результаты.

 $data = MyModel::where('foo', '=', 'bar')->ordered(); 

Если вы хотите, чтобы это в разных моделях, вы могли бы создать базовый класс и просто расширить его до моделей, которые вы хотите получить доступ к этому методу с областью.

До Laravel 5.2

В настоящее время мы можем решить эту проблему и с глобальными областями, представленными в Laravel 4.2 (исправьте меня, если я ошибаюсь). Мы можем определить класс области следующим образом:

 <?php namespace App; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\ScopeInterface; class OrderScope implements ScopeInterface { private $column; private $direction; public function __construct($column, $direction = 'asc') { $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; } } } 

Затем в вашей модели вы можете добавить область в метод boot() :

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

Теперь модель упорядочена по умолчанию. Обратите внимание, что если вы определяете заказ также вручную в запросе: MyModel::orderBy('some_column') , то он добавит его только в качестве вторичного заказа (используется, когда значения первого порядка одинаковы), и это не будет переопределения. Чтобы сделать возможным использование другого заказа вручную, я добавил (необязательный) макрос (см. Выше), а затем вы можете сделать: MyModel::unordered()->orderBy('some_column')->get() .

Laravel 5.2 и выше

Laravel 5.2 представил более чистый способ работы с глобальными областями. Теперь единственное, что нам нужно написать, это следующее:

 <?php namespace App; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; class OrderScope implements Scope { private $column; private $direction; public function __construct($column, $direction = 'asc') { $this->column = $column; $this->direction = $direction; } public function apply(Builder $builder, Model $model) { $builder->orderBy($this->column, $this->direction); } } 

Затем в вашей модели вы можете добавить область в метод boot() :

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

Чтобы удалить глобальную область, просто используйте:

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

Решение без дополнительного класса видимости

Если вам не нравится иметь целый класс для области видимости, вы можете (начиная с Laravel 5.2) также определить глобальную область видимости в методе boot() вашей модели:

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

Вы можете удалить эту глобальную область, используя это:

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

Другим способом сделать это может быть переопределение метода newQuery в вашем классе модели. Это работает только в том случае, если вы никогда не хотите, чтобы результаты были заказаны другим полем (так как добавление другого ->orderBy() позже не удалит этот по умолчанию). Так что это, вероятно, не то, что вы обычно хотели бы сделать, но если у вас есть требование всегда сортировать определенный способ, то это будет работать:

 protected $orderBy; protected $orderDirection = 'asc'; /** * Get a new query builder for the model's table. * * @param bool $ordered * @return \Illuminate\Database\Eloquent\Builder */ public function newQuery($ordered = true) { $query = parent::newQuery(); if (empty($ordered)) { return $query; } return $query->orderBy($this->orderBy, $this->orderDirection); } 

вы должны использовать красноречивую глобальную область действия, которая может применяться ко всем запросам (также вы можете установить для нее параметр).

И для отношений вы можете использовать этот полезный трюк:

 class Category extends Model { public function posts(){ return $this->hasMany('App\Models\Post')->orderBy('title'); } } 

это добавит order by всем сообщениям, когда мы получим их из категории. Если вы добавите order by вашему запросу, этот order by умолчанию будет отменен!

Немного улучшенный ответ Джошуа Джаббура

вы можете использовать код, который он предложил в Trait, а затем добавить эту черту к моделям, в которых вы хотите их заказать.

 <?php namespace App\Traits; trait AppOrdered { protected $orderBy = 'created_at'; protected $orderDirection = 'desc'; public function newQuery($ordered = true) { $query = parent::newQuery(); if (empty($ordered)) { return $query; } return $query->orderBy($this->orderBy, $this->orderDirection); } } 

то в любой модели, которую вы хотите заказать, вы можете использовать :

 class PostsModel extends Model { use AppOrdered; .... 

теперь каждый раз, когда вы запрашиваете эту модель, данные будут упорядочены, это как-то более организовано, но мои ответы – ответ Джаббура.