Intereting Posts
Бинарное хранилище MySQL с использованием файловой системы BLOB VS OS: большие файлы, большие количества, большие проблемы Как отображать содержимое с одного сайта на другом с помощью PHP? Выберите опцию в PHP, не вернувшись обратно Предупреждение: mysql_query () : доступ запрещен для пользователя «ODBC» @ «localhost» (с использованием пароля: НЕТ) Два необычных PHP-оператора, используемых вместе, чтобы получить цвет пикселя изображения, пожалуйста, объясните Невозможно создать временный файл в PHP, несмотря на то, что существует каталог temp Переупорядочить строки записей базы данных через интерфейс, используя php Существует ли переменная во всех функциях класса? Значения массива группы на основе ключа в php? Правило проверки формы Codeigniter для соответствия (пароль) Добавление содержимого команды UNIX в тег div Как я «эхо» «Идентификатор ресурса №6» из ответа MySql в PHP? Могу ли я использовать зарезервированные имена PHP для своих функций и классов? Выйдите из всех открытых вкладок автоматически, когда пользователь выходит на одну из открытых вкладок в php Динамическое добавление мета-тегов с использованием php

Тот же контроллер ресурсов Laravel для нескольких маршрутов

Я пытаюсь использовать черту в качестве ключа для моих контроллеров ресурсов Laravel.

Метод контроллера:

public function store(CreateCommentRequest $request, Commentable $commentable) 

В которой Commentable является типом типа trait, который используют мои модели Eloquent.

Commentable черта выглядит следующим образом:

 namespace App\Models\Morphs; use App\Comment; trait Commentable { /** * Get the model's comments. * * @return \Illuminate\Database\Eloquent\Relations\MorphMany */ public function Comments() { return $this->morphMany(Comment::class, 'commentable')->orderBy('created_at', 'DESC'); } } 

В моей маршрутизации у меня есть:

 Route::resource('order.comment', 'CommentController') Route::resource('fulfillments.comment', 'CommentController') 

Оба заказа и исполнения могут иметь комментарии, поэтому они используют один и тот же контроллер, так как код будет таким же.

Однако, когда я отправляю order/{order}/comment , я получаю следующую ошибку:

Осветить \ Контракты \ Container \ BindingResolutionException
Цель [App \ Models \ Morphs \ Commentable] не является реальной.

Это вообще возможно?

Solutions Collecting From Web of "Тот же контроллер ресурсов Laravel для нескольких маршрутов"

Таким образом, вы хотите избежать дублирования кода как для контроллеров ресурсов, так и для выполнения и быть немного СУХОЙ. Хорошо.

Черты не могут быть заданы

Как сказал Мэтью, вы не можете вводить черты характера, и именно по этой причине вы получаете ошибку разрешения привязки. Помимо этого, даже если бы это было свойство typehintable, контейнер был бы смущен, какой модели он должен создать, поскольку есть две доступные модели Commentable . Но мы поговорим позже.

Интерфейсы рядом с чертами

Часто бывает хорошей практикой иметь интерфейс для сопровождения признака. Помимо того факта, что интерфейсы можно вводить с клавиатуры, вы придерживаетесь принципа разделения интерфейса, который «при необходимости» является хорошей практикой.

 interface Commentable { public function comments(); } class Order extends Model implements Commentable { use Commentable; // ... } 

Теперь, когда это тип. Давайте перейдем к проблеме путаницы в контейнере.

Контекстная привязка

Контейнер Laravel поддерживает контекстное связывание . Это способность четко указывать, когда и как разрешать абстрактный текст конкретному.

Маршрут – единственный отличительный фактор, который вы получили для своих контроллеров. Мы должны основываться на этом. Что-то вроде:

 # AppServiceProvider::register() $this->app ->when(CommentController::class) ->needs(Commentable::class) ->give(function ($container, $params) { // Since you're probably utilizing Laravel's route model binding, // we need to resolve the model associated with the passed ID using // the `findOrFail`, instead of just newing up an empty instance. // Assuming this route pattern: "order|fullfilment/{id}/comment/{id}" $id = (int) $this->app->request->segment(2); return $this->app->request->segment(1) === 'order' ? Order::findOrFail($id) : Fulfillment::findOrFail($id); }); 

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

Неконтекстное связывание также будет выполнено:

 # AppServiceProvider::register() $this->app->bind(Commentable::class, function ($container, $params) { $id = (int) $this->app->request->segment(2); return $this->app->request->segment(1) === 'order' ? Order::findOrFail($id) : Fulfillment::findOrFail($id); }); 

Неверный инструмент

Мы просто исключили дублирующий код контроллера, внедрив ненужную сложность, которая еще хуже. 👍

Несмотря на то, что он работает, он сложный, не поддерживаемый, не общий и худший из всех, зависящий от URL-адреса. Он использует неправильный инструмент для задания и явно ошибается.

наследование

Правильный инструмент для устранения подобных проблем – это просто наследование. Введем абстрактный класс контроллера комментариев базы и откройте два неглубоких.

 # App\Http\Controllers\CommentController abstract class CommentController extends Controller { public function store(CreateCommentRequest $request, Commentable $commentable) { // ... } // All other common methods here... } # App\Http\Controllers\OrderCommentController class OrderCommentController extends CommentController { public function store(CreateCommentRequest $request, Order $commentable) { return parent::store($commentable); } } # App\Http\Controllers\FulfillmentCommentController class FulfillmentCommentController extends CommentController { public function store(CreateCommentRequest $request, Fulfillment $commentable) { return parent::store($commentable); } } # Routes Route::resource('order.comment', 'OrderCommentController'); Route::resource('fulfillments.comment', 'FulfillCommentController'); 

Простой, гибкий и обслуживаемый.

Arrrgh, неправильный язык

Не так быстро:

Декларация OrderCommentController :: store (CreateCommentRequest $ request, Order $ commentable) должна быть совместима с CommentController :: store (CreateCommentRequest $ request, Commentable $ commentable) .

Несмотря на то, что переопределяющие параметры метода работают в конструкторах просто отлично, это просто не для других методов! Конструкторы – это особые случаи .

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

Хорошо, может быть, в лучшем мире.

Явная привязка модели маршрута

Так что мы будем делать?

Если мы явно укажем маршрутизатору, как загрузить наши Commentable , мы можем просто использовать одинокий класс CommentController . Явная привязка модели Laravel работает путем сопоставления заполнителей маршрутов (например, {order} ) для моделирования классов или логики пользовательского разрешения. Таким образом, хотя мы используем наш единственный CommentController мы можем использовать отдельные модели или логику разрешения для заказов и выполнений на основе их заполнителей маршрутов. Итак, мы бросаем typehint и полагаемся на местозаполнитель.

Для контроллеров ресурсов имя заполнителя зависит от первого параметра, который вы передаете методу Route::resource . Просто сделайте artisan route:list чтобы узнать.

Хорошо давай сделаем это:

 # App\Providers\RouteServiceProvider::boot() public function boot() { // Map `{order}` route placeholder to the \App\Order model $this->app->router->model('order', \App\Order::class); // Map `{fulfillment}` to the \App\Fulfilment model $this->app->router->model('fulfillment', \App\Fulfilment::class); parent::boot(); } 

Код вашего контроллера:

 # App\Http\Controllers\CommentController class CommentController extends Controller { // Note that we have dropped the typehint here: public function store(CreateCommentRequest $request, $commentable) { // $commentable is either an \App\Order or a \App\Fulfillment } // Drop the typehint from other methods as well. } 

И определения маршрута остаются неизменными.

Это лучше, чем первое решение, так как оно не зависит от сегментов URL, которые подвержены изменениям в отличие от замещающих маршрутов, которые редко меняются. Он также является общим, так как все {order} s будут разрешены для \App\Order model и всех {fulfillment} s в App\Fulfillment .

Мы могли бы изменить первое решение для использования параметров маршрута вместо сегментов URL. Но нет причин делать это вручную, когда Ларавел предоставил его нам.


Да, я знаю, я тоже не чувствую себя хорошо.

Вы не можете набирать черты .

Однако вы можете набирать интерфейсы. Таким образом, вы можете создать интерфейс, который требует методов из этого признака и решить эту проблему. Затем ваши классы реализуют этот интерфейс, и вы должны быть в порядке.

EDIT: Как любезно отметил @Stefan, все же, вероятно, будет сложно разрешить интерфейс для конкретного класса, потому что ему придется решать разные классы при разных обстоятельствах. Вы можете получить доступ к запросу у поставщика услуг и использовать путь для определения того, как его решить, но я немного сомневаюсь в этом. Я думаю, что включение их в отдельные контроллеры и использование свойств наследования / характеристик для совместного использования общих функций может быть лучше, поскольку методы в каждом контроллере могут набирать намек на требуемый объект и затем передавать их эквивалентному методу родительского элемента.