Single Responsibility Principle and Laravel

  • URIs to access the pages for the posts and actions
  • blade templates for holding the HTML for final rendering
  • A Controller to handle the HTTP requests.
  • index – Displays a list of all posts
  • create – Displays the form to create a post
  • store – Stores a post in the database
  • show – Displays a specific post
  • edit – Displays the form to update a specific post
  • update – Updates a previously stored post in the database
  • publish – sets the deleted_at value to null
  • unpublish – sets the deleted_at value to the current timestamp
  • destroy – permanently deletes a post from the database
Route::group(['middleware' => ['can:create posts']], function () {
Route::get('/blog/post/create',
'PostController@create')->name('post.create');
Route::post('/blog/post/store',
'PostController@store')->name('post.store');
});
Route::get('/blog/post/',
'PostController@index')->name('post.index');
Route::get('/blog/post/{post}',
'PostController@show')->name('post.show');
Route::group(['middleware' => ['can:edit posts']], function () {
Route::get('/blog/post/{post}/edit',
'PostController@edit')->name('post.edit');
Route::post('/blog/post/{post}/update',
'PostController@update')->name('post.update');
});
Route::group(['middleware' => ['can:unpublish posts']], function () {
Route::get('/blog/post/{post}/delete',
'PostController@unpublish')->name('post.delete');
});
Route::group(['middleware' => ['can:publish posts']], function () {
Route::get('/blog/post/{post}/restore',
'PostController@publish')->name('post.restore');
});
Route::group(['middleware' => ['can:destroy posts']], function () {
Route::get('/blog/post/{post}/destroy',
'PostController@destroy')->name('post.destroy');
});
<?phpnamespace App\Http\Controllers;//core
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
//model
use App\Models\Post;
//events
use App\Events\PostCreated;
use App\Events\QueuePostForReview;
use App\Events\UpdatePostSchedule;
use App\Events\PostPublished;
use App\Events\PostUnpublished;
use App\Events\PostDestroyed;
class PostController extends Controller
{
/**
* Display all posts
*
* @return View
*/
public function index(): View
{
return view('posts.index');
}
/**
* Show the form to create a new post
*
* @return View
*/
public function create(): View
{
return view('posts.create');
}
/**
* Store a newly created post in the database
*
* @param Request $request
* @return RedirectResponse
*/
public function store(Request $request): RedirectResponse
{
$validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);

// The blog post is valid...
$post = Post::create([
'title' => $validatedData['title'], //the post title
'body' => $validatedData['body'], //the post body
'deleted_at' => Carbon::now() //sets the post to be 'unpublished' by default
'author' => Auth::user()->id, //sets the post author ID
]);
PostCreated::dispatch($post);
QueuePostForReview::dispatch($post);
UpdatePostSchedule::dispatch($post);
return redirect()
->route('posts.index')
->with('success', 'The post was created, but not published!');
}
/**
* Display a specific post
*
* @param Post $post
* @return View
*/
public function show(Post $post): View
{
return view('posts.show', ['post' => $post]);
}
/**
* Show the form to edit a specific post
*
* @param Post $post
* @return View
*/
public function edit(Post $post): View
{
return view('posts.edit', ['post' => $post]);
}
/**
* Update a specific post in the database
*
* @param Request $request
* @param Environment $environment
* @return RedirectResponse
*/
public function update(Request $request, $post): RedirectResponse
{
$postInstance = Post::withTrashed()->find($post);
$validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// The blog post is valid...
$postInstance->title = $validatedData['title']; //the post title
$postInstance->body = $validatedData['body']; //the post body
$postInstance->deleted_at = Carbon::now(); //sets the post to be 'unpublished' by default
$postInstance->author = Auth::user()->id; //sets the post author ID
$postInstance->save();
PostUpdated::dispatch($postInstance);
QueuePostForReview::dispatch($postInstance);
UpdatePostSchedule::dispatch($postInstance);
return redirect()
->route('posts.index')
->with('success', 'The post was updated, but not published!');
}
/**
* SoftDeletes post from Database
*
* @param Post $post
* @return RedirectResponse
*/
public function delete(Post $post): RedirectResponse
{
$environment->delete();
return redirect()->route('posts.index')->with('success', 'The post was successfully unpublished');
}
/**
* Recovers post from SoftDelete
*
* @param $post
* @return RedirectResponse
*/
public function restore($post): RedirectResponse
{
$postInstance = Post::withTrashed()->find($post);
$postInstance->restore();
PostUnpublished::dispatch($postInstance);
QueuePostForReview::dispatch($postInstance);
UpdatePostSchedule::dispatch($postInstance);
PostPublished::dispatch($postInstance); return redirect()->route('posts.index')->with('success', 'The post was successfully published');
}
/**
* Permanently deletes post from the database
*
* @param $post
* @return RedirectResponse
*/
public function destroy($post): RedirectResponse
{
$postInstance = Post::withTrashed()->find($post);
PostDestroyed::dispatch($post); $postInstance->forceDelete();
return redirect()->route('posts.index')->with('success', 'The post was permanantly deleted');
}
}
app
├── Console
├── Events
├── Listeners
├── Exceptions
├── Http
│ ├── Controllers
│ │ ├── Controller.php
│ │ └── PostController.php
│ ├── Middleware
│ └── Requests
│ └── PostFormRequest.php
├── Models
│ └── Post.php
└── Processors
└── Post
├── PostProcessor.php
└── PostEventProcessor.php
<?phpnamespace App\Processors\Post;use App\Models\Post;class PostProcessor
{
/**
* @param $data
* @return Post
*/
public function start($data, $type)
{
switch($type) {
case 'create':
if(Auth::user()->can(['create post'])) {
return self::create($data);
} else {
throw new Exception(__('isAuthorized.false'), 1);
}
break;
case 'update':
if(Auth::user()->can(['edit post'])) {
return self::update($data);
} else {
throw new Exception(__('isAuthorized.false'), 1);
}
break;
case 'publish':
if(Auth::user()->can(['publish post'])) {
return self::publish($data);
} else {
throw new Exception(__('isAuthorized.false'), 1);
}
break;
case 'unpublish':
if(Auth::user()->can(['unpublish post'])) {
return self::unpublish($data);
} else {
throw new Exception(__('isAuthorized.false'), 1);
}
break;
case 'destroy':
if(Auth::user()->can(['destroy post'])) {
return self::destroy($data);
} else {
throw new Exception(__('isAuthorized.false'), 1);
}
break;
default:
throw new Exception(__('processRequest.error'), 1);
}
}
/**
* @param array $requestData
* @return Post
*/
private static function create(Array $requestData): Post
{
$post = Post::create($requestData);
$post->deleted_at = Carbon::now();
$post->save();
return $post;
}
/**
* @param array $requestData
* @return Post
*/
private static function update(Array $requestData): Post
{
$post = Post::withTrashed()->find($requestData['id']);
$post->title = $requestData['title'];
$post->body = $requestData['body'];
$post->deleted_at = Carbon::now();
$post->updated_at = Carbon::now();
$post->save();
return $post;
}
/**
* @param Post $post
* @return void
*/
private static function publish($post)
{
$postInstance = Post::withTrashed()->find($post);
$postInstance->deleted_at = null;
$postInstance->updated_at = Carbon::now();
$postInstance->save();
return $post;
}
/**
* @param Post $post
* @return void
*/
private static function unpublish(Post $post)
{
$post->deleted_at = Carbon::now();
$post->updated_at = Carbon::now();
$post->save();
return $post;
}
/**
* @param $post
* @return void
*/
private static function destroy($post)
{
$postInstance = Post::withTrashed()->find($post);
$postInstance->forceDelete();
}
}
<?phpnamespace App\Http\Requests\Post;use Auth;
use Illuminate\Foundation\Http\FormRequest;
class PostFormRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return Auth::user()->can(['control posts']);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}
public function messages(): array
{
return [
'title.required' => 'A post must have a title',
'title.unique' => 'A post with that title already exists',
'title.max' => 'The title is too long, max title is 255 characters',
'body.required' => 'A post body is required',
];
}
}
<?phpnamespace App\Processors\Post;//models
use App\Models\Post;
//events
use App\Events\PostCreated;
use App\Events\QueuePostForReview;
use App\Events\UpdatePostSchedule;
use App\Events\PostPublished;
use App\Events\PostUnpublished;
use App\Events\PostDestroyed;
class PostEventProcessor
{
/**
* @param $post
* @param $type
*/
public function start($post, $type)
{
switch($type) {
case 'create':
PostCreated::dispatch($post);
QueuePostForReview::dispatch($post);
UpdatePostSchedule::dispatch($post);
case 'update':
PostUpdated::dispatch($post);
QueuePostForReview::dispatch($post);
UpdatePostSchedule::dispatch($post);
case 'publish':
PostPublished::dispatch($post);
case 'unpublish':
PostUnpublished::dispatch($post);
QueuePostForReview::dispatch($post);
UpdatePostSchedule::dispatch($post);
case 'destroy':
PostDestroyed::dispatch($post);
default:
throw new Exception(__('processEventRequest.error'), 1);
}
}
}
<?phpnamespace App\Http\Controllers\Post;//core
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
//model
use App\Models\Post;
//requests
use App\Http\Requests\Post\PostFormRequest;
//processor
use App\Processor\Post\PostProcessor;
use App\Processor\Post\PostEventProcessor;
class PostController extends Controller
{
/**
* Display all posts
*
* @return View
*/
public function index(): View
{
return view('post.index');
}
/**
* Show the form to create a new post
*
* @return View
*/
public function create(): View
{
return view('post.create');
}
/**
* Store a newly created post in the database
*
* @param StorePostRequest $request
* @return RedirectResponse
*/
public function store(StorePostRequest $request): RedirectResponse
{
try {
$post = PostProcessor::start($request->validated(), 'create');
PostEventProcessor::start($post, 'create');
return redirect()
->route('posts.show', ['post' => $post])
->with('success', __('post.create.success'));
} catch (\Exception $e) {
return redirect()
->route('posts.index')
->with('error', __('post.create.error'));
}
}
/**
* Display a specific post
*
* @param Post $post
* @return View
*/
public function show(Post $post): View
{
return view('post.show', ['post' => $post]);
}
/**
* Show the form to edit a specific post
*
* @param Post $post
* @return View
*/
public function edit(Post $post): View
{
return view('post.update', ['post' => $post]);
}
/**
* Update a specific post in the database
*
* @param UpdatePostRequest $request
* @param Post $post
* @return RedirectResponse
*/
public function update(PostFormRequest $request, Post $post): RedirectResponse
{
try{
$updatedPost = PostProcessor::start($request->validated(), 'update');
PostEventProcessor::start($updatedPost, 'update');
return redirect()
->route('posts.show', ['post' => $updatedPost->id])
->with('success', __('post.update.success'));
} catch (\Exception $e) {
return redirect()
->route('posts.index')
->with('success', __('post.update.error'));
}
}
/**
* SoftDeletes Post from Database
*
* @param Post $post
* @return RedirectResponse
*/
public function unpublish(Post $post): RedirectResponse
{
try{
PostProcessor::start($post, 'unpublish');
PostEventProcessor::start($post, 'unpublish');
return redirect()
->route('posts.index')
->with('success', __('post.unpublish.success'));
} catch (\Exception $e) {
return redirect()
->route('posts.index')
->with('error', __('post.unpublish.error'));
}

}
/**
* Recovers Post from SoftDelete
*
* @param $post
* @return RedirectResponse
*/
public function publish($post): RedirectResponse
{
$post = Post::withTrashed()->where('id', $post)->first();
try{
PostProcessor::start($post, 'publish');
PostEventProcessor::start($post, 'publish');
return redirect()
->route('posts.index')
->with('success', __('post.publish.success'));
} catch (\Exception $e) {
return redirect()
->route('posts.index')
->with('error', __('post.publish.error'));
}

}
/**
* Permanently deletes post from the database
*
* @param $post
* @return RedirectResponse
*/
public function destroy($post): RedirectResponse
{
$post = Post::withTrashed()->where('id', $post)->first();
try{
PostEventProcessor::start($post, 'destroy');
PostProcessor::start($post, 'destroy');
return redirect()
->route('posts.index')
->with('success', __('post.destroy.success'));
} catch (\Exception $e) {
return redirect()
->route('posts.index')
->with('error', __('post.destroy.error'));
}

}
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Garrett Massey

Garrett Massey

I am a web designer and developer. I like to play the piano, write, and pet dogs. Coffee lover, gaybro, nerd. In no particular order. (garrettmassey.net)