Пользовательские Методы Модели Laravel

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

например, скажем, я имейте модель под названием 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: как добавить пользовательскую функциональность, которая имеет смысл быть в модели, не делая ее статическим методом и не вызывая ее с помощью модели фасад?


Edit:

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

вероятно, лучшим примером над "сортировкой" является если мне нужно было выполнить расчет или алгоритм на фрагменте данных:

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

для меня имеет смысл, чтобы что-то подобное было в модели, поскольку это специфично для домена.

3 ответов


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

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

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

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

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

или

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

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

Car::getAllSortedByMake();

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

просто используйте "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();
}

на самом деле вы можете расширить 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:

Первый Лоренц пример кода больше подходит для вас репозиторий не для модели, но и вы не можете передать больше методов с этим подходом:

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

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

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

многие люди предлагают использовать области для расширения laravel builder, но это на самом деле плохое решение, потому что области изолированы eloquent builder, и вы не получите тот же запрос с теми же командами внутри vs вне поля зрения. Я предложил 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

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