Контроллеры

Содержание:


Введение

Вместо определения логигики обработки запроса как замыканий в файлах маршрута, вы можете организовать такое поведение используя классы контроллера. Контроллеры могут сгруппировать похожие по обработке запросов догику в один класс. Они находятся в папке app/Http/Controllers.


Базовый контроллер

Определение котроллера

Ниже представлен пример базового класса контроллера. Данный контроллер расширяет базовый класс контроллера включённый в Ларавел. Базовый класс обеспечивает несколько надёжных методов, таких как метод посредников. Посредника можно прикрепить к дейсвию контроллера:

namespace App\Http\Controllers;

use App\User;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    /**
     * Show the profile for the given user.
     *
     * @param  int  $id
     * @return View
     */
    public function show($id)
    {
        return view('user.profile', ['user' => User::findOrFail($id)]);
    }
}

Вы можете определить маршрут для данного действия контроллера так:

Route::get('user/{id}', 'UserController@show');

Теперь, когда запрос сопоставят с URI маршрута, запустится выполнения метода show в контроллере UserController. Параметры маршрута будут переданы в метод.

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

Контроллеры и пространства имён

Важно заметить, что мы не указали пространство имён для контроллера, при определении в маршруте. RouteServiceProvider загружает файлы маршрута, внутри которых содержатся группы с пространстом имён. Нам необходимо лишь указать часть имени класса, которая идёт после App\Http\Controllers.

Если вы хотите разместить ваши контроллеры глубже директории App\Http\Controllers, используйте имена классаов, связанных с пространством имён App\Http\Controllers. Поэтому, если ваш контроллер находится в папке App\Http\Controllers\Photos\AdminController, вам необходимо регистрировать маршрут к контроллеру так:

Route::get('foo', 'Photos\AdminController@method');

Контроллер одного действия

Если вы хотите сделать контроллер с одним одинственным действием, вам необходимо разместить метод __invoke в вашм контроллере:

namespace App\Http\Controllers;

use App\User;
use App\Http\Controllers\Controller;

class ShowProfile extends Controller
{
    /**
     * Show the profile for the given user.
     *
     * @param  int  $id
     * @return View
     */
    public function __invoke($id)
    {
        return view('user.profile', ['user' => User::findOrFail($id)]);
    }
}

При регистрации маршрутов для контроллера с одиночным действием, метод указывать не нужно:

Route::get('user/{id}', 'ShowProfile');

Одиночные контроллеры можно сгенерировать командой Artisan:

php artisan make:controller ShowProfile --invokable

Контроллер и посредники

Вы можете прикрепить к контроллеру посредника в файле маршрута:

Route::get('profile', 'UserController@show')->middleware('auth');

Часто более удобно указать посредника внутри вашего конструктора контроллера. Используя метод middleware из конструктора контроллера, вы можете легко прикрепить посредника к действию. Также можно детализировать для каких конкретно методов в классе контроллера:

class UserController extends Controller
{
    /**
     * Instantiate a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');

        $this->middleware('log')->only('index');

        $this->middleware('subscribed')->except('store');
    }
}

Контроллеры также позволяеют вам регистрировать посредников используя замыкание. Это даёт прекрасный способ определить посредника для одного контроллера без определиния всего класса посредника:

$this->middleware(function ($request, $next) {
    // ...

    return $next($request);
});
Вы можете назначить посредника для подгруппы действий контроллера. Такие действия могут привести к разрастанию контроллера. Вместо этого, можно разбить контроллер на несколько, меньших по размеру.

Контроллеры ресурсов

Маршрутизация ресурсов Laravel для типового "CRUD" привязывается к контроллеру с помощью одной строки кода. Например, вы хотите создать контроллер, который обрабатывает все запросы HTTP для фото в вашем приложении. Используя команду Artisan make:controller, вы можете быстро создать такой контроллер:

php artisan make:controller PhotoController --resource

Эта команда сгенерирует контроллер в app/Http/Controllers/PhotoController.php. Контроллер будет содержать соответсвующий метод для каждой из доступных операций ресурса.

Следующим шагом необходимо зарегистрировать маршрут контроллера для ресурса:

Route::resource('photos', 'PhotoController');

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

Можно регистрировать сразу несколько контроллеров типа "ресурс" путём передачи массива в метод resources:

Route::resources([
    'photos' => 'PhotoController',
    'posts' => 'PostController'
]);

Действия обрабатываемые контроллером ресурса

Метод URI Действие Имя маршрута
GET photos index photos.index
GET photoscreate create photos.create
POST photos store photos.store
GET photos/{photo} show photos.show
GET photos/{photo}/edit edit photos.edit
PUT/PATCH photos/{photo} update photos.update
DELETE photos/{photo} destroy photos.destroy

Детализация модели ресурса

Если вы используете сввзывание модели в маршруте и хотели бы вписывать модели в методы контроллера ресурсов, можно использовать опцию --model при генерации контроллера:

php artisan make:controller PhotoController --resource --model=Photo

Имитация методов форм

В силу того, что HTML формы не могут сделать запросы PUT, PATCH, или DELETE вам необходимо добавить скрытое поле _method, чтобы имитировать эти HTTP методы. Для этого используйте команду Blade @method, которая и создаст это поле:

<form method='POST' action='/foo/bar'>
    @method('PUT')

    ...
</form>

Частичные маршруты ресурса

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

Route::resource('photos', 'PhotoController')->only([
    'index', 'show'
]);

Route::resource('photos', 'PhotoController')->except([
    'create', 'store', 'update', 'destroy'
]);

API мрашрутов ресурса

При декларации маршрутов API ресурсов вы захотите исключить некоторые пути в которых присутсвует HTML. Например, create и edit. Для удобства, вам необходимо использовать метод apiResource, чтобы исключить данные маршруты:

Route::apiResource('photos', 'PhotoController');

Вы можете регистрировать сразу несколько контроллеров для API ресурсов сразу, путём передачи массива в метод apiResources:

Route::apiResources([
    'photos' => 'PhotoController',
    'posts' => 'PostController'
]);

Чтобы быстро сгенерировать контроллер API ресурса, которые не включает методы the create или edit, используйте метку --api при выполнении команды make:controller:

php artisan make:controller API/PhotoController --api

Названные имена ресурса

По умолчанию, все действия контроллера ресурсов имеют имена маршрута. Однако, вы может переписать эти имена путём передачи массива names с вашими параметрами:

Route::resource('photos', 'PhotoController')->names([
    'create' => 'photos.build'
]);

Параметры названных ресурсов маршрута

По умолчанию, Route::resource создаст параметры маршрута для ваших ресурсов основанных на имени ресурса. Вы можете перезаписать эти имена, используя метод parameters. Массив, который передаётся методу parameters, должен быть ассоциативный массивом имён ресурсов и имён параметров:

Route::resource('users', 'AdminUserController')->parameters([
    'users' => 'admin_user'
]);

В приведенном выше примере создаются следующие URI для маршрута ресурса show:

/users/{admin_user}

Локализация URIs ресурсов

По умолчанию, Route::resource содаст URI ресурса на английском языке. Если вам необходима локализация create и edit, вы можете использовать метод Route::resourceVerbs. Это можно сделать в методе boot в вашем AppServiceProvider:

use Illuminate\Support\Facades\Route;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Route::resourceVerbs([
        'create' => 'crear',
        'edit' => 'editar',
    ]);
}

После такой настройки, регистрация маршрута ресурса такого как Route::resource('fotos', 'PhotoController') создаст следующие URI:

/fotos/crear

/fotos/{foto}/editar

Дополнение ресурса контроллеров

Если вам необходимо добавить дополнительные маршруты для контроллера ресурса за пределами стнадартного набора маршрутов ресурса, вам необходимо определить маршруты до момента вызова Route::resource; в противном случае маршруты определённые методом resource могут невольно иметь приоритет над вашими дополнительными маршрутами:

Route::get('photos/popular', 'PhotoController@method');

Route::resource('photos', 'PhotoController');
Держите контроллеры в порядке. Не нужно их чрезмерно расширять. Если вам ситуативно нужно расширить стандартный перечень действий ресурсов контроллера, то вы можете разбить контроллер на 2 более мелких контроллера.

Внедрение зависимости и контроллеры

Внедрение в конструктор

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

namespace App\Http\Controllers;

use App\Repositories\UserRepository;

class UserController extends Controller
{
    /**
     * The user repository instance.
     */
    protected $users;

    /**
     * Create a new controller instance.
     *
     * @param  UserRepository  $users
     * @return void
     */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }
}

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

Внедрение в метод

В дополнение к внедрению в конструктор, вы можете также вписывать зависимости в методы контроллера. Распросранённый пример использования — это внедрение объекта Illuminate\Http\Request в метод контроллера store

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * Store a new user.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        $name = $request->name;

        //
    }
}

Если метод контроллера дополнительно ожидает данные и параметров маршрута, перечислите их после зависимостей. Например, если маршрут определён так:

Route::put('user/{id}', 'UserController@update');

Вы можете вписать Illuminate\Http\Request и указать параметр id определяя метод контроллера следующим образом:

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * Update the given user.
     *
     * @param  Request  $request
     * @param  string  $id
     * @return Response
     */
    public function update(Request $request, $id)
    {
        //
    }
}

Кэширование маршрута

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

Если ваше приложение использует маршруты основанные на контроллерах, вам необходимо воспользоваться преимуществом кэширования маршрутов Laravel. Кэширование позволит вам свести время, необходимое для регистрации всех маршрутов вашего приложения, к минимому. Чтобы сгенерировать кэш маршрутов, выполните команду Artisan route:cache:

php artisan route:cache

После выполнения этой команды, ваш файл закешированных маршрутов будет загружаться для каждого запроса. Важно помнить, что если вы добавляете новые маршруты в приложение, тогда вам будет необходимо сгенерировать новый кэш. Поэтому, выполняйте команду route:cache после завершения основных работ. Например, при развёртывании проекта.

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

php artisan route:clear