Методы пользовательской модели Laravel

Всякий раз, когда я добавляю дополнительную логику к моделям Eloquent, мне приходится делать его static методом (т.е. менее чем идеальным), чтобы вызывать его из фасада модели. Я много пытался найти, как это сделать правильно, и почти все результаты говорят о создании методов, которые возвращают части интерфейса Query Builder. Я пытаюсь понять, как добавить методы, которые могут возвращать что угодно и вызываться с использованием фасада модели.

Например, допустим, у меня есть модель с именем Car, и я хочу получить их все:

$cars = Car::all();

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

$cars = array(
  'Ford' => array(
     'F-150' => '...',
     'Escape' => '...',
  ),
  'Honda' => array(
     'Accord' => '...',
     'Civic' => '...',
  ),
);

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

$cars = Car::getAllSortedByMake();

На мгновение забудем ужасное имя метода и тот факт, что он тесно связан со структурой данных. Если я сделаю в модели такой метод:

public function getAllSortedByMake()
{
   // Process and return resulting array
   return array('...');
}

И, наконец, вызовите его в моем контроллере, я получу это исключение:

Нестатический метод Car :: getAllSortedByMake () не должен вызываться статически, если $ this из несовместимого контекста

TL; DR: как добавить настраиваемую функциональность, которая имеет смысл присутствовать в модели, не делая ее статическим методом и не вызывая ее с помощью фасада модели?


Редактировать:

Это теоретический пример. Возможно, перефразировка вопроса имела бы больше смысла. Почему некоторые нестатические методы, такие как all() или which(), доступны на фасаде модели Eloquent, но не добавляются в модель дополнительные методы? Это означает, что используется магический метод __call, но как я могу заставить его распознавать мои собственные функции в модели?

Вероятно, лучший пример по сравнению с «сортировкой» - это если мне нужно было запустить вычисление или алгоритм для фрагмента данных:

$validSPG = Chemical::isValidSpecificGravity(-1.43);

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


person Jeremy Harris    schedule 14.05.2014    source источник
comment
Начните с двух таблиц данных: manufacturers и models, поэтому manufacturers содержит Ford, Honda и т. Д. И models с manufacturer_id, связывающим model с manufacturer и содержащим F-150, Escape, Accord, Civic и т. Д.   -  person Mark Baker    schedule 14.05.2014
comment
@MarkBaker Это теоретический пример. У меня вопрос на более фундаментальном уровне, например, почему all() доступен через фасад? Это не статический метод, что означает, что используется магический метод __call. Из-за этого, почему arbitraryMethodICreate() недоступен?   -  person Jeremy Harris    schedule 14.05.2014
comment
см. Illuminate\Database\Eloquent\Model, all - статический метод   -  person Jeff Lambert    schedule 14.05.2014
comment
Циллез: причина проста: all() фактически является статическим методом на Model, и __call в этой ситуации не вызывается. В классе Model есть больше статических методов, а также другие, которые вы можете использовать так, как Model::method() обрабатываются __callStatic, затем __call магическими методами и передаются в класс Eloquent Builder.   -  person Jarek Tkaczyk    schedule 14.05.2014


Ответы (3)


У меня вопрос на более фундаментальном уровне, например, почему all () доступен через фасад?

Если вы посмотрите на ядро ​​Laravel - all () на самом деле является статической функцией.

public static function all($columns = array('*'))

У вас есть два варианта:

public static function getAllSortedByMake()
{
    return Car::where('....')->get();
}

or

public function scopeGetAllSortedByMake($query)
{
    return $query->where('...')->get();
}

Оба позволят вам делать

Car::getAllSortedByMake();
person Laurence    schedule 14.05.2014
comment
Ах, я слышал о прицелах в Eloquent, но еще не использовал их. Спасибо! Я вижу, что all() теперь статичен (это то, что я получаю за то, что не проверял). Я всегда слышал, что этих методов нет, и просто использовал шаблоны проектирования, чтобы выглядеть таким образом для удобства использования при сохранении тестируемости. - person Jeremy Harris; 14.05.2014
comment
Да - большинство функций не статичны - только некоторые из модельных по какой-то причине - я уверен, что у Тейлора есть веские причины - но это выше моих знаний. - person Laurence; 14.05.2014
comment
Обратите внимание, что области всегда должны возвращать экземпляр построителя запросов. Поэтому, если вы используете ->get(), вам следует использовать статический метод, а не вариант области действия. Также обратите внимание, что области могут быть объединены в цепочку: Car::GetAllSortedByMake()->GetAllSortedByMake()->otherScopeYouHaveDefined()->where(...)->get(). - person Sawny; 03.01.2016
comment
не используйте области, потому что запрос не совпадает, потому что области изолированы - person fico7489; 07.08.2018

На самом деле вы можете расширить Eloquent Builder и поместить туда собственные методы.

Шаги по расширению Builder:

1.Создайте собственный конструктор

<?php

namespace App;

class CustomBuilder extends \Illuminate\Database\Eloquent\Builder
{
    public function test()
    {
        $this->where(['id' => 1]);

        return $this;
    }
}

2. Добавьте этот метод к своей базовой модели:

public function newEloquentBuilder($query)
{
    return new CustomBuilder($query);
}

3. Выполните запрос с методами внутри вашего настраиваемого построителя:

User::where('first_name', 'like', 'a')
    ->test()
    ->get();

для приведенного выше кода сгенерированный запрос mysql будет:

select * from `users` where `first_name` like ? and (`id` = ?) and `users`.`deleted_at` is null

PS:

Пример First Laurence - это код, который больше подходит для вашего репозитория, а не для модели, но вы также не можете передать больше методов с помощью этого подхода:

public static function getAllSortedByMake()
{
    return Car::where('....')->get();
}

Второй пример Лоуренса - худшее событие.

public function scopeGetAllSortedByMake($query)
{
    return $query->where('...')->get();
}

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

Дополнительные объяснения: например, если у вас есть такие области:

public function scopeWhereTest($builder, $column, $operator = null, $value = null, $boolean = 'and')
{
    $builder->where($column, $operator, $value, $boolean);
}

и два красноречивых вопроса:

User::where(function($query){
    $query->where('first_name', 'like', 'a');
    $query->where('first_name', 'like', 'b');
})->get();

vs

User::where(function($query){
    $query->where('first_name', 'like', 'a');
    $query->whereTest('first_name', 'like', 'b');
})->get();

Сгенерированные запросы будут:

select * from `users` where (`first_name` like ? and `first_name` like ?) and `users`.`deleted_at` is null

vs

select * from `users` where (`first_name` like ? and (`id` = ?)) and `users`.`deleted_at` is null

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

person fico7489    schedule 07.08.2018

для лучшего динамического кода вместо использования имени класса модели "Car",

просто используйте "static" или "self"

public static function getAllSortedByMake()
{
    //to return "Illuminate\Database\Query\Builder" class object you can add another where as you want
    return static::where('...');

    //or return already as collection object
    return static::where('...')->get();
}
person jalmatari    schedule 30.08.2017