Complete code for DRY resource controllers in Laravel
Below is the complete code which accompanies DRY resource controllers in Laravel.
GenericResourceController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Session;
class GenericResourceController extends Controller
{
protected $request;
protected $object;
protected $id = false;
// child class properties
protected $model = ''; // e.g. 'App\Product'
protected $fields = [];
protected $intermediaries = []; // e.g. ['tags' => ['save' => false, 'destroy' => true]]
protected $objectLang = ''; // e.g. 'product'
protected $redirectAfter = [
'store' => '', // e.g. 'products.show'
'update' => '',
'destroy' => '',
];
public function store(Request $request){
$this->request = $request;
$this->processFormSubmission();
return $this->redirect('store');
}
public function update(Request $request, $id){
$this->request = $request;
$this->id = $id;
$this->processFormSubmission();
return $this->redirect('update');
}
public function destroy($id){
$this->id = $id;
$this->destroyObject();
return $this->redirect('destroy');
}
/**
* Handle form submissions for new and existing records
*/
protected function processFormSubmission(){
$this->setObject();
$this->validateRequest();
$this->setObjectData();
$this->saveObject();
$this->handleIntermediaries('save');
$this->setSuccessMessage('save');
}
/**
* Handle destroy actions
*/
protected function destroyObject(){
$this->setObject();
$this->handleIntermediaries('destroy');
$this->deleteObject();
$this->setSuccessMessage('destroy');
}
/**
* Set the object property.
* If there's no id, create a new object of the appropriate type,
* otherwise instantiate the object to correspond to the object id.
*/
protected function setObject(){
$model = $this->model;
if($this->id === false){
$this->object = new $model;
} else {
$this->object = $model::find($this->id);
}
}
/**
* Cycle through the form fields and validate them.
*/
protected function validateRequest(){
if(
isset($this->fields['slug']) &&
$this->request->input('slug') != $this->object->slug
){
$this->fields['slug'] = 'required|alpha_dash|min:5|max:255|unique:' . $this->object->getTable() . ',slug';
}
$this->validate($this->request, $this->fields);
}
/**
* Cycle through the form fields and set them in the object.
*/
protected function setObjectData(){
foreach($this->fields as $field => $rules){
$this->object->$field = $this->request->$field;
}
}
/**
* Save the object to the database
*/
protected function saveObject(){
$this->object->save();
}
/**
* Delete the object from the database
*/
protected function deleteObject(){
$this->object->delete();
}
/**
* Sync many-to-many relationships
*/
protected function handleIntermediaries($action){
if(isset($this->intermediaries) && is_array($this->intermediaries)){
foreach($this->intermediaries as $intermediary => $isAllowed){
if($action == 'save' && $isAllowed['save']) {
$this->syncIntermediary($intermediary);
} elseif($action == 'destroy' && $isAllowed['destroy']){
$this->detachIntermediary($intermediary);
}
}
}
}
/**
* Sync intermediaries with the object
*/
protected function syncIntermediary($intermediary){
if(
is_array($this->request->$intermediary) &&
count($this->request->$intermediary) > 0
){
$idList = $this->request->$intermediary;
} else {
$idList = [];
}
$this->object->$intermediary()->sync($idList);
}
/**
* Detach intermediaries from the object
*/
protected function detachIntermediary($intermediary){
$this->object->$intermediary()->detach();
}
/**
* Set a success message in the session
*/
protected function setSuccessMessage($messageType){
$messages = array(
'save' => 'The ' . $this->objectLang . ' was successfully saved.',
'destroy' => 'The ' . $this->objectLang . ' was successfully deleted.'
);
Session::flash('success', $messages[$messageType]);
}
/**
* Redirect to the next route
*/
protected function redirect($action){
$route = $this->redirectAfter[$action];
if(strpos($route, 'index') !== false){
return redirect()->route($route);
} else {
return redirect()->route($route, $this->id);
}
}
}
Sample child controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Session;
use App\Product; // don't forget this!
class ProductController extends GenericResourceController
{
protected $model = 'App\Product';
protected $fields = [
'product_name' => 'required|max:255',
'description' => '',
'slug' => 'required|alpha_dash|min:5|max:255',
'price' => 'required|float',
// etc...
];
protected $intermediaries = ['tags' => ['save' => true, 'destroy' => true]];
protected $objectLang = 'product';
protected $redirectAfter = [
'store' => 'products.show',
'update' => 'products.show',
'destroy' => 'products.index',
];
public function index(){
}
public function show(){
}
public function create(){
}
public function edit(){
}
}