Laravel - многоразовый контроллер ресурсов
Я изменяю CMS, который я использую в своих проектах, и недавно я решил создать контроллер для действий по умолчанию BaseController
, где все остальные контроллеры расширят этот контроллер BaseController
.
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
class BaseController extends Controller
{
protected $viewFolder = 'admin';
protected $title;
protected $model;
protected $key = 'id';
protected $files = [];
public function __construct()
{
$this->setVariable('title', $this->title);
}
public function index()
{
$items = $this->model::paginate();
$this->setVariable('items', $items);
return $this->viewRender('index');
}
public function create()
{
return $this->viewRender('create');
}
public function store(ExampleStoreRequestFROMEXAMPLECONTROLLER $request)
{
$item = $this->model::create($request->all());
return redirect()->route($this->viewFolder.'.'.$this->viewType.'.show', $item[$this->key]);
}
public function show($id)
{
$item = $this->model::where($this->key, $id)->firstOrFail();
$this->setVariable('item', $item);
return $this->viewRender('show');
}
public function edit($id)
{
$item = $this->model::where($this->key, $id)->firstOrFail();
$this->setVariable('item', $item);
return $this->viewRender('edit');
}
public function update(ExampleUpdateRequestFROMEXAMPLECONTROLLER $request, $id)
{
$item = $this->model::where($this->key, $id)->firstOrFail();
$item->update($request->except(['_token', '_method']));
return redirect()->route($this->viewFolder.'.'.$this->viewType.'.show', $item[$this->key]);
}
public function status(ExampleStatusRequestFROMEXAMPLECONTROLLER $request, $id)
{
$this->model::where($this->key, $id)->update($request->except('_method'));
return response()->json([
'message' => 'O status foi alterado com sucesso'
]);
}
public function destroy($id)
{
$this->model::where($this->key, $id)->delete();
return redirect()->route($this->viewFolder.'.'.$this->viewType.'.index');
}
}
Проблема в том, что я написал BaseController
используя Запросы формы UserController
и я не знаю, как оставить динамический запрос, чтобы я мог реализовать их с других контроллеров.
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Requests\ExampleStatusRequest;
use App\Http\Requests\ExampleStoreRequest;
use App\Http\Requests\ExampleUpdateRequest;
class ExampleController extends BaseController
{
protected $viewType = 'users';
protected $model = 'App\Example';
public function index()
{
$this->setVariable('title', 'Usuários');
return parent::index();
}
public function create()
{
$this->setVariable('title', 'Cadastrar usuário');
return parent::create();
}
public function store(ExampleStoreRequest $request)
{
return parent::store($request);
}
public function edit($id)
{
$this->setVariable('title', 'Editar usuário');
return parent::edit($id);
}
public function update(ExampleUpdateRequest $request, $id)
{
return parent::update($request, $id);
}
public function status(ExampleStatusRequest $request, $id)
{
return parent::status($request, $id);
}
}
Вот мой контроллер по умолчанию:
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
protected $viewFolder = '';
protected $viewType = '';
protected $viewVariables = [];
protected function setVariable($key, $value)
{
$this->viewVariables[$key] = $value;
}
protected function viewRender($view)
{
return view($this->viewFolder.'.pages.'.$this->viewType.'.'.$view, $this->viewVariables);
}
}
Любая идея, как это решить?
Ответы
Ответ 1
После комментариев я мог понять лучше. У меня была такая же проблема здесь и что мой обходной путь
class BaseController extends Controller
{
protected function _store($request)
{
....
}
...
}
class MyController extends BaseController
{
....
public function store(MyRequest $request)
{
//do something
return parent::_store($request);
}
}
Ответ 2
Любой CustomFormRequest
который вы будете использовать, будет экземпляром FormRequest
.. класса, который, в свою очередь, расширяет класс Request
. Поэтому в вашем BaseController
выполните следующее:
BaseController.php
use Illuminate\Http\Request;
// some code
public function store(Request $request)
{
$item = $this->model::create($request->all());
return redirect()
->route($this->viewFolder.'.'.$this->viewType.'.show', item[$this->key]);
}
// The rest of your code..
Затем в дочернем контроллере:
AChildController.php
class AChildController extends BaseController
{
use App\Http\Requests\ACustomFormRequest;
// Some code
public function store(ACustomFormRequest $request)
{
//do something
return parent::_store($request);
}
}
Попробуйте.
Ответ 3
class BaseController extends Controller
{
protected function _store($request)
{
....
}
...
}
class MyController extends BaseController
{
....
public function store(MyRequest $request)
{
//do something
return parent::_store($request);
}
}
Ответ 4
Короткий ответ:
Наручники.
Один Route::model
.
И одно простое связывание для каждого типа запроса формы, используя интерфейсы для инъекции метода в контроллере.
На ваш пример применяется длинный ответ:
Модели, routes/web.php
:
<?php
Route::model('example', App\Example::class);
Route::resource('example', 'Admin\ExampleController');
Запросы привязки в базе контроллера:
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class BaseController extends Controller
{
// Your code...
/**
* @var string[]|callable[]
*/
protected $bindings = [];
/**
* Controller constructor.
*/
public function __construct()
{
$this->addBindings();
}
/**
* Add controller specific bindings.
*/
protected function addBindings()
{
$app = Container::getInstance();
foreach ($this->getBindings() as $abstract => $concrete) {
$app->bind($abstract, $concrete);
}
}
// Your code...
public function store(Requests\StoreRequestInterface $request)
{
// Your code...
}
public function show(Model $item)
{
// Your code...
}
public function edit(Model $item)
{
// Your code...
}
public function update(Requests\UpdateRequestInterface $request, Model $model)
{
// Your code...
}
public function status(Requests\StatusRequestInterface $request, Model $model)
{
// Your code...
}
public function destroy(Model $item)
{
// Your code...
}
// Your code...
}
И пример контроллера:
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Requests;
class ExampleController extends BaseController
{
// Your code...
/**
* @var string[]|callable[]
*/
protected $bindings = [
Requests\StatusRequestInterface::class => Requests\ExampleStatusRequest::class,
Requests\StoreRequestInterface::class => Requests\ExampleStoreRequest::class,
Requests\UpdateRequestInterface::class => Requests\ExampleUpdateRequest::class,
];
// Your code...
}
Таким образом, интерфейсы запросов выглядят примерно так:
<?php
namespace App\Http\Requests;
interface StoreRequestInterface
{
}
И вы должны использовать его в качестве интерфейса в своих запросах формы:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ExampleStoreRequest extends FormRequest implements StoreRequestInterface
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}
Мнение альтернативного ответа:
Общая база контроллера:
<?php
namespace App\Http\Controllers;
use Illuminate\Container\Container;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
abstract class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
/**
* The view subdirectory that must be used.
*
* @var string
*/
protected $viewDir;
/**
* Bindings
*
* @var string[]|callable[]
*/
protected $bindings = [];
/**
* Controller constructor.
*/
public function __construct()
{
$this->addViewPath();
$this->addBindings();
$this->init();
}
/**
* @return void
*/
protected function init()
{
//
}
/**
* @return string
*/
protected function getViewDir()
{
return $this->viewDir;
}
/**
* @return callable[]|string[]
*/
protected function getBindings()
{
return $this->bindings;
}
/**
* Add controller specific view path.
*/
protected function addViewPath()
{
if (null !== ($dir = $this->getViewDir()) && ($path = realpath(base_path('resources/views/' . $dir)))) {
view()->getFinder()->addLocation($path);
}
}
/**
* Add controller specific bindings.
*/
protected function addBindings()
{
$app = Container::getInstance();
foreach ($this->getBindings() as $abstract => $concrete) {
$app->bind($abstract, $concrete);
}
}
}
База контроллера ресурсов:
<?php
namespace App\Http\Controllers;
use Illuminate\Database\Eloquent\Model;
use App\Http\Requests\StoreRequestInterface;
use App\Http\Requests\UpdateRequestInterface;
use App\Http\Requests\StatusRequestInterface;
abstract class ResourceController extends Controller
{
/**
* Model class
*
* @var string
*/
protected $modelClass;
/**
* @var string
*/
protected $viewDirPrefix;
/**
* @return string
*/
protected function getViewDir()
{
return $this->viewDir ?:
ltrim($this->viewDirPrefix . DIRECTORY_SEPARATOR . 'pages', DIRECTORY_SEPARATOR) .
DIRECTORY_SEPARATOR . strtolower(class_basename($this->modelClass));
}
/**
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function getQuery()
{
return call_user_func($this->modelClass . '::query');
}
/**
* @return string
*/
protected function getName()
{
return __(class_basename($this->modelClass));
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
protected function index()
{
$title = __('resource.title.index', ['name' => str_plural($this->getName())]);
$models = $this->getQuery()->paginate();
return view('index', compact('title', 'models'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
protected function create()
{
$title = __('resource.title.create', ['name' => $this->getName()]);
return view('create', compact('title'));
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request|StoreRequestInterface $request
*
* @return \Illuminate\Http\Response
*/
public function store(StoreRequestInterface $request)
{
$model = $this->getQuery()->create($request->all());
return redirect()
->route(substr($request->route()->getName(), 0, -5) . 'show', $model);
}
/**
* Display the specified resource.
*
* @param \Illuminate\Database\Eloquent\Model $model
*
* @return \Illuminate\Http\Response
*/
public function show(Model $model)
{
$title = __('resource.title.show', ['name' => $this->getName()]);
return view('show', compact('title', 'model'));
}
/**
* Show the form for editing the specified resource.
*
* @param \Illuminate\Database\Eloquent\Model $model
*
* @return \Illuminate\Http\Response
*/
public function edit(Model $model)
{
$title = __('resource.title.edit', ['name' => $this->getName()]);
return view('edit', compact('title', 'model'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request|UpdateRequestInterface $request
* @param \Illuminate\Database\Eloquent\Model $model
*
* @return \Illuminate\Http\Response
*/
public function update(UpdateRequestInterface $request, Model $model)
{
$model->update($request->except(['_token', '_method']));
return redirect()
->route(substr($request->route()->getName(), 0, -6) . 'show', $model);
}
/**
* Remove the specified resource from storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
*
* @return \Illuminate\Http\Response
* @throws \Exception
*/
public function destroy(Model $model)
{
$model->delete();
return redirect()
->route(substr(request()->route()->getName(), 0, -7) . 'index');
}
/**
* @param \Illuminate\Http\Request|StatusRequestInterface $request
* @param \Illuminate\Database\Eloquent\Model $model
*
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
protected function status(StatusRequestInterface $request, Model $model)
{
$model->update($request->except('_method'));
return response()->json([
'message' => __('resource.status.success'),
]);
}
}
Контроллер ресурсов для Example
модели:
<?php
namespace App\Http\Controllers\Admin;
use App\Example;
use App\Http\Requests;
use App\Http\Controllers\ResourceController;
class ExampleController extends ResourceController
{
/**
* Model class
*
* @var string
*/
protected $modelClass = Example::class;
/**
* @var string
*/
protected $viewDirPrefix = 'admin';
/**
* @var string[]|callable[]
*/
protected $bindings = [
Requests\StatusRequestInterface::class => Requests\Example\StatusRequest::class,
Requests\StoreRequestInterface::class => Requests\Example\StoreRequest::class,
Requests\UpdateRequestInterface::class => Requests\Example\UpdateRequest::class,
];
}
Файл английского языка в resources/lang/en/resource.php
:
<?php
return [
'title' => [
'index' => 'All :Name',
'create' => 'Create :Name',
'show' => 'Show :Name',
'edit' => 'Edit :Name',
],
'status' => [
'success' => 'Status updated successfully',
],
];
Файл португальского языка в resources/lang/pt/resource.php
:
<?php
return [
'title' => [
'index' => 'Todos os :Name',
'create' => 'Cadastrar :Name',
'show' => 'Show :Name',
'edit' => 'Editar :Name',
],
'status' => [
'success' => 'O status foi alterado com sucesso',
],
];
Веб-маршруты в routes/web.php
:
<?php
Route::group([
'as' => 'admin.',
'prefix' => 'admin',
'namespace' => 'Admin',
], function () {
Route::model('example', App\Example::class);
Route::resource('example', 'ExampleController');
});
Интерфейсы запроса (для привязки):
<?php
namespace App\Http\Requests;
interface StoreRequestInterface
{
}
То же самое для App\Http\Requests\UpdateRequestInterface
и App\Http\Requests\StatusRequestInterface
...
Пример запроса формы:
<?php
namespace App\Http\Requests\Example;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\StoreRequestInterface;
class StoreRequest extends FormRequest implements StoreRequestInterface
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}
То же самое для App\Http\Requests\Example\UpdateRequest
и App\Http\Requests\Example\StatusRequest
...
Документация, применяемая для этого ответа:
Примечание: Этот ответ предназначен для Laravel 5.6. Некоторый код, используемый в этом ответе, не поддерживается в версиях предварительного просмотра. Если вы хотите получить определенную версию, сообщите мне, и я попытаюсь ее адаптировать.