Laravel предоставляет нам возможность получать данные из базы вызывая методы классаIlluminate\Database\Query\Builder
на наших моделях. Сегодня речь пойдёт об одном из таких методов, а именно о динамическом where
. Благодаря нему мы можем выбирать данные, фильтруя их по различным атрибутам нашей модели. Например:
|
$activeUsers = User::whereActive(true)->get(); $publishedPosts = Post::wherePublished(true)->get(); $activeMenuProducts = Product::whereActiveAndShowInMenu(true, true)->get(); |
Разберёмся, что за магия здесь происходит и откуда берутся эти методы. Для начала посмотрим в наш класс Illuminate\Database\Eloquent\Model
, так как его наследуют все наши модели. Ничего похожего на методы выше в нём нет, но есть магические методы__call
и __callStatic
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
/** * Handle dynamic method calls into the model. * * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { if (in_array($method, ['increment', 'decrement'])) { return call_user_func_array([$this, $method], $parameters); } $query = $this->newQuery(); return call_user_func_array([$query, $method], $parameters); } /** * Handle dynamic static method calls into the method. * * @param string $method * @param array $parameters * @return mixed */ public static function __callStatic($method, $parameters) { $instance = new static; return call_user_func_array([$instance, $method], $parameters); } |
При обращении к несуществующим статическим методам (а в примерах выше именно они и есть) нашей модели, вызывается метод __callStatic
. Он создаёт экземпляр класса в котором мы вызвали метод и пытается вызвать его в нём. Тут в игру вступает метод__call
, потому как и в экземпляре нашего класса тоже нет динамических методов where
. Он создаёт экземпляр класса Illuminate\Database\Eloquent\Builder
в котором также нет нашего динамического where
. Но, зато в нём есть ещё один магический метод __call
, который и приводит нас к конечному классу, который нас интересует, а именноIlluminate\Database\Query\Builder
. В нём, как вы уже наверное догадались, нас ждёт ещё один метод __call
, который и помогает нам использовать динамические методы начинающиеся с where
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
/** * Handle dynamic method calls into the method. * * @param string $method * @param array $parameters * @return mixed * * @throws \BadMethodCallException */ public function __call($method, $parameters) { if (Str::startsWith($method, 'where')) { return $this->dynamicWhere($method, $parameters); } $className = get_class($this); throw new BadMethodCallException("Call to undefined method {$className}::{$method}()"); } |
Если наш метод начинается с where
, то вызывается метод dynamicWhere
. В нём заботливо написаны комментарии на английском, которые я ниже распишу на русском:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
/** * Handles dynamic "where" clauses to the query. * * @param string $method * @param string $parameters * @return $this */ public function dynamicWhere($method, $parameters) { // Убираем слово `where` из названия нашего метода $finder = substr($method, 5); // Разбиваем название метода на сегменты по заданному регулярному выражению, и возвращаем строку сегментов метода. // Например `nameAndId` преобразуется в массив ['Name', 'And', 'Id'] $segments = preg_split('/(And|Or)(?=[A-Z])/', $finder, -1, PREG_SPLIT_DELIM_CAPTURE); // Оператор по умолчанию у нас `and`. То есть, например, если у нас цепочка методов `whereName()->whereId()`, то между ними по умолчанию стоит `and`. // Этот оператор служит для связи с предыдущей частью запроса $connector = 'and'; $index = 0; foreach ($segments as $segment) { // Проходя по всем нашим сегментам мы проверяем, если сегмент не `And` и не `Or`, то добавляем наш динамический where. // В методе `addDynamic` можно увидеть по сути одну строку(помимо преобразования оператора в lowercase): `$this->where(Str::snake($segment), '=', $parameters[$index], $bool);`. // Именно в нём наш динамический метод превращается в обычный `where` с параметрами if ($segment != 'And' && $segment != 'Or') { $this->addDynamic($segment, $connector, $parameters, $index); $index++; } // Иначе мы меняем наш оператор связи с предыдущей частью запроса, в случае если у нас более сложное название метода, например `whereNameAndId` или `whereNameAndIdOrColor` else { $connector = $segment; } } return $this; } |
Дальше происходит различная магия Eloquent для создания и подготовки запроса и тд, но где преобразуются наши динамические where в обычные, мы нашли.
Понравилась статья или книга? Поделись с друзями: