I am modifying the CMS that I use in my projects, and recently I decided to create a controller for default actions BaseController
, where all other controllers will extend this controller 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');
}
}
The problem is: I wrote the BaseController
using the Form Requests of the UserController
and I have no idea how to leave these Request dynamic so that I can implement them from other controllers.
<?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);
}
}
Here is my default controller:
<?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);
}
}
Any idea how to solve this?
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
:
<?php
Route::model('example', App\Example::class);
Route::resource('example', 'Admin\ExampleController');
Requests bindings in the Controller Base:
<?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...
}
And the Example Controller:
<?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...
}
So the Requests interfaces would look something like:
<?php
namespace App\Http\Requests;
interface StoreRequestInterface
{
}
And you should use it as an interface in your Form Requests:
<?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 [
//
];
}
}
Opinionated alternative answer:
General controller base:
<?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);
}
}
}
Resource Controller Base:
<?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'),
]);
}
}
Resource controller for Example
model:
<?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,
];
}
English Language file in 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',
],
];
Portuguese Language file in 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',
],
];
Web Routes in routes/web.php
:
<?php
Route::group([
'as' => 'admin.',
'prefix' => 'admin',
'namespace' => 'Admin',
], function () {
Route::model('example', App\Example::class);
Route::resource('example', 'ExampleController');
});
Request interfaces (for binding):
<?php
namespace App\Http\Requests;
interface StoreRequestInterface
{
}
Same for App\Http\Requests\UpdateRequestInterface
and App\Http\Requests\StatusRequestInterface
...
Example Form Request:
<?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 [
//
];
}
}
Same for App\Http\Requests\Example\UpdateRequest
and App\Http\Requests\Example\StatusRequest
...
Documentation applied for this reply:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With