Laravel - Reusable resource controller
After the comments I could understand better.I had the same issue here and that's my workaround
class BaseController extends Controller{ protected function _store($request) { .... } ... } class MyController extends BaseController { .... public function store(MyRequest $request) { //do something return parent::_store($request); } }
Short answer:
Bindings.
One Route::model
.
And one Simple Binding for each Form Request type, using Interfaces for the Method Injection in the Controller.
Long answer applied to your example:
Models binding in routes/web.php
:
<?phpRoute::model('example', App\Example::class);Route::resource('example', 'Admin\ExampleController');
Requests bindings in the Controller Base:
<?phpnamespace 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...}
And the Example Controller:
<?phpnamespace 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...}
So the Requests interfaces would look something like:
<?phpnamespace App\Http\Requests;interface StoreRequestInterface{}
And you should use it as an interface in your Form Requests:
<?phpnamespace 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 [ // ]; }}
Opinionated alternative answer:
General controller base:
<?phpnamespace 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); } }}
Resource Controller Base:
<?phpnamespace 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'), ]); }}
Resource controller for Example
model:
<?phpnamespace 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, ];}
English Language file in resources/lang/en/resource.php
:
<?phpreturn [ 'title' => [ 'index' => 'All :Name', 'create' => 'Create :Name', 'show' => 'Show :Name', 'edit' => 'Edit :Name', ], 'status' => [ 'success' => 'Status updated successfully', ],];
Portuguese Language file in resources/lang/pt/resource.php
:
<?phpreturn [ 'title' => [ 'index' => 'Todos os :Name', 'create' => 'Cadastrar :Name', 'show' => 'Show :Name', 'edit' => 'Editar :Name', ], 'status' => [ 'success' => 'O status foi alterado com sucesso', ],];
Web Routes in routes/web.php
:
<?phpRoute::group([ 'as' => 'admin.', 'prefix' => 'admin', 'namespace' => 'Admin',], function () { Route::model('example', App\Example::class); Route::resource('example', 'ExampleController');});
Request interfaces (for binding):
<?phpnamespace App\Http\Requests;interface StoreRequestInterface{}
Same for App\Http\Requests\UpdateRequestInterface
and App\Http\Requests\StatusRequestInterface
...
Example Form Request:
<?phpnamespace 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 [ // ]; }}
Same for App\Http\Requests\Example\UpdateRequest
and App\Http\Requests\Example\StatusRequest
...
Documentation applied for this reply:
- https://laravel.com/docs/5.6/routing#route-model-binding
- https://laravel.com/docs/5.6/container#binding
- https://laravel.com/docs/5.6/controllers#dependency-injection-and-controllers
- https://laravel.com/docs/5.6/validation#form-request-validation
- https://laravel.com/docs/5.6/views
- https://laravel.com/docs/5.6/controllers#resource-controllers
- https://laravel.com/docs/5.6/localization
Notice: This reply is for Laravel 5.6. Some code used in this reply is not supported in previews versions. If you want it for a specific version, let me know and I will try to adapt it.
Any CustomFormRequest
that you will use, will be an instance of FormRequest
.. a class that in turn extends the Request
class. So in your BaseController
do this:
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..
Then in your child Controllers:
AChildController.php
class AChildController extends BaseController{use App\Http\Requests\ACustomFormRequest;// Some code public function store(ACustomFormRequest $request) { //do something return parent::_store($request); }
}
Give it a try.