Я модифицирую 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);
}
}
Есть идеи, как это решить?
Если я правильно понимаю, что вы пытаетесь сделать, тогда простой ответ: в PHP нет чистого способа сделать это. PHP действительно отстой. Здесь мало свободы для (немного большего) продвинутого объектно-ориентированного или общего программирования. Все это сводится к тому, что причина, по которой вы задаете этот вопрос, заключается в том, что вы продвигаетесь в полиморфном мышлении в программировании. PHP Не хватает вашего мышления, а это значит, что пора двигаться дальше! Начните использовать больше объектно-ориентированных языков (возможно, посмотрите C# (и ASP.net)).
Мне нравится реализация viewRender (или, могу я предложить, renderView). Однако что случилось с простотой? Не будет ли из-за этой реализации просачиваться много низкоуровневых деталей? Неужели оно того стоит?
Почему вы ищете ярлыки? Новички склонны делать это таким образом (были там, сделали то). В этом случае небольшое дублирование лучше, чем абстракция. Ваш абстрактный класс обязательно будет расти и усложняться по мере того, как будет вводиться все больше и больше вариантов использования. Вы закончите с большим комом грязи, который будет адом поддерживать только потому, что вы изначально хотели писать меньше кода. Пусть контроллеры будут как можно более простыми, автономными, и всегда отдавайте предпочтение композиции перед наследованием. Я уже привык к DDD и больше не использую extends.






После комментариев я смог лучше понять. У меня была такая же проблема, и это мой способ решения
class BaseController extends Controller
{
protected function _store($request)
{
....
}
...
}
class MyController extends BaseController
{
....
public function store(MyRequest $request)
{
//do something
return parent::_store($request);
}
}
А как отправить UserStoreRequest в BaseController @ store? Не могли бы вы показать код?
Вы не можете использовать одновременно ExampleStatusRequest и UserStoreRequest. Что вы можете сделать, так это создать другой контроллер, названный UserController, который также унаследует BaseController.
Это план! Как я могу передать UserStoreRequest в BaseController из ExampleController?
Замените ExampleStatusRequest на UserStoreRequest и удалите UserStoreRequest из BaseController
Ошибка: объявление App \ Http \ Controllers \ Admin \ ExampleController :: store (App \ Http \ Requests \ ExampleSto reRequest $ request) должно быть совместимо с App \ Http \ Controllers \ Admin \ BaseController :: store ($ request)
Обратите внимание на подчеркивание перед методом
Хммм ... Не могли бы вы объяснить, что здесь происходит?
Вы не можете переопределить метод с другой подписью. Итак, обходной путь - создать другой метод с другой подписью.
Любой CustomFormRequest, который вы будете использовать, будет экземпляром FormRequest ... класса, который, в свою очередь, расширяет класс Request. Итак, в вашем BaseController сделайте следующее:
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..
Затем в ваших дочерних контроллерах:
class AChildController extends BaseController
{
use App\Http\Requests\ACustomFormRequest;
// Some code
public function store(ACustomFormRequest $request)
{
//do something
return parent::_store($request);
}
}
Попробуйте.
class BaseController extends Controller
{
protected function _store($request)
{
....
}
...
}
class MyController extends BaseController
{
....
public function store(MyRequest $request)
{
//do something
return parent::_store($request);
}
}
Короткий ответ:
Привязки.
И один Простая привязка для каждого типа запроса формы с использованием интерфейсов для Внедрение метода в контроллер.
Длинный ответ, примененный к вашему примеру:
Связывание моделей в 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. Некоторый код, использованный в этом ответе, не поддерживается в предварительных версиях. Если вам нужна конкретная версия, дайте мне знать, и я постараюсь ее адаптировать.
Можно использовать php func_get_args.