Lesson 9 - Simple CMS in Laravel - Article management
In the last lesson, Simple CMS in Laravel - Article creation, we started creating an administration for a simple content management system in the Laravel framework. We created a list of articles and an editor for their creation.
In today's lesson, we'll take look at article management and talk about HTTP request classes.
HTTP requirements
Before we get into developing new parts of the application, let's look back
at our store()
method in the ArticleController.php
controller, which we defined in the last lesson. As the name suggests, this
method should be used to insert a new record (in our case, an article) into the
database. Among other things, it also contains validation of the received data,
which in the case of larger forms may not be very practical. Therefore, we
introduce the so-called Request
classes.
Request classes
Request
classes check whether the user is authorized to send the
request and validate the sent data. We can move the set of rules of the given
HTTP request to them and the controller method will then contain only the action
logic. Let's create such a class now.
For the store()
action, we generate the
StoreRequest
class using the Artisan make:request
command, which does not need any special options:
php artisan make:request Article/StoreRequest
Since we can have more than one request class for one controller, it is good practice to create a separate folder for each controller.
A new app/Http/Requests/
folder was automatically created for
us, followed by the Article/
subfolder defined by us. In the folder
we find the generated file StoreRequest.php
and the content of
which is as follows:
<?php namespace App\Http\Requests\Article; use Illuminate\Foundation\Http\FormRequest; class StoreRequest extends FormRequest { /** * 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 [ // ]; } }
The newly generated StoreRequest
class that inherits from
FormRequest
class contains only these two methods:
authorize()
- Checks if the user is authorized to send the request. This is one of the places where we can place this verification. Another of them may be middleware, but we will show this in the next lessons.rules()
- Returns a field with set validation rules.
Since we don't deal with permissions (and users) yet, we can completely
remove the authorize()
method. If it is not defined, this
verification is automatically considered successful. But what interests us more
is the rules()
method, which we fill with rules from the
store()
method of our controller. So it will look like this:
/** * Return validation rules for the form responsible for creating articles. * * @return array */ public function rules() { return [ 'title' => ['required', 'min:3', 'max:80'], 'url' => ['required', 'min:3', 'max:80', 'unique:articles,url'], 'description' => ['required', 'min:25', 'max:255'], 'content' => ['required', 'min:50'], ]; }
Subsequently, we can completely remove the validation from the
store()
method. However, we will modify the object type of the
$request
variable to our Request
class instead of the
general one, so that the defined rules can then be applied:
/** * Validate the submitted data from the form and create a new article. * * @param StoreRequest $request * @return RedirectResponse */ public function store(StoreRequest $request) { Article::create($request->all()); return redirect()->route('article.index'); }
Of course, we must not forget to import our new class:
use App\Http\Requests\Article\StoreRequest;
From a method that initially had 25 lines, including documentation, we then created a method with only 12 lines and we kept exactly the same functionality! Just try to create an article now and ignore some of the validation rules. The HTTP request class will not let you in
Editing articles
Let's move on to editing the article now.
Edit () and update () actions
We will return the view with the form for editing in the edit()
method:
/** * Display the form for editing the article and pass the obtained article to the given view. * * @param Article $article * @return Application|Factory|View */ public function edit(Article $article) { return view('article.edit', ['article' => $article]); }
The actual editing of the record will take place in the update()
method:
/** * Validate the submitted data via the form and edit the obtained article. * * @param UpdateRequest $request * @param Article $article * @return RedirectResponse */ public function update(UpdateRequest $request, Article $article) { $article->update($request->all()); return redirect()->route('article.index'); }
As with the store()
action, we only pass the data field from the
form to the Eloquent method, in this case the update()
method.
UpdateRequest
To validate the data, we use our own Request
class called
UpdateRequest
again, which we will now generate in the
app/Http/Requests/Article/
folder using the already mentioned
Artisan command:
php artisan make:request Article/UpdateRequest
We will modify this generated class immediately. Remove the
authorize()
method and define validation rules in the
rules()
method:
<?php namespace App\Http\Requests\Article; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; class UpdateRequest extends FormRequest { /** * * Return validation rules for the form responsible for editing articles. * * @return array */ public function rules() { return [ 'title' => ['required', 'min:3', 'max:80'], 'url' => [ 'required', 'min:3', 'max:80', Rule::unique('articles', 'url')->ignore($this->route('article')->id), ], 'description' => ['required', 'min:25', 'max:255'], 'content' => ['required', 'min:50'], ]; } }
The validation rules for editing the article have become a bit more
complicated. For the unique
validation rule, we must define an
exception for the current record. If we did not define it and wanted to change
the article without modifying its URL, the validation would then throw us out
the error that there is already an article in the database with the given URL
address, even if it's a currently edited article.
We set this exception through the ignore()
method of the
Unique
builder, which accepts the record ID. Also note the other
advantages of parameters that work through dependency injection (so-called route model
binding). An instance of the article model can be easily obtained using the
route()
method of the framework class FormRequest
,
where we pass the name of the parameter defined in the routing file and do not
have to select this record from the database only via the passed identifier (in
our case the article URL).
Finally, we will add an import of this new class to our
ArticleController
:
use App\Http\Requests\Article\UpdateRequest;
View
Now let's create a view named edit.blade.php
in the
resources/views/article/
folder, which is almost identical to the
create.blade.php
view:
@extends('base') @section('title', 'Article editing ' . $article->title) @section('description', 'Editor for editing articles.') @section('content') <h1>Article editing {{ $article->title }}</h1> <form action="{{ route('article.update', ['article' => $article]) }}" method="POST"> @csrf @method('PUT') <div class="form-group"> <label for="title">Title</label> <input type="text" name="title" id="title" class="form-control" value="{{ old('title') ?: $article->title }}" required minlength="3" maxlength="80" /> </div> <div class="form-group"> <label for="url">URL</label> <input type="text" name="url" id="url" class="form-control" value="{{ old('url') ?: $article->url }}" required minlength="3" maxlength="80" /> </div> <div class="form-group"> <label for="description">Article description</label> <textarea name="description" id="description" rows="4" class="form-control" required minlength="25" maxlength="255">{{ old('description') ?: $article->description }}</textarea> </div> <div class="form-group"> <label for="content">Article content</label> <textarea name="content" id="content" class="form-control" rows="8">{{ old('content') ?: $article->content }}</textarea> </div> <button type="submit" class="btn btn-primary">Save article</button> </form> @endsection @push('scripts') <script type="text/javascript" src="{{ asset('//cdn.tinymce.com/4/tinymce.min.js') }}"></script> <script type="text/javascript"> tinymce.init({ selector: '#content', plugins: [ 'advlist autolink lists link image charmap print preview anchor', 'searchreplace visualblocks code fullscreen', 'insertdatetime media table contextmenu paste' ], toolbar: 'insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image', entities: '160,nbsp', entity_encoding: 'raw', }); </script> @endpush
Since the HTTP action store()
is of type
PUT
, we must use the Blade directive @method
in the
form again.
If we try to edit one of the existing articles now, everything will go perfectly. It is also worth noting the date of the last change in the list of articles, which is automatically updated whenever the record is changed. Just try it for yourself
Removing articles
The last part of the administration that we lack is the removal of articles.
Since we already have a form for this action in the list of articles and we
covered the last lesson with it, all we have to do now is edit the
destroy()
action in our controller:
/** * Delete the article from the database. * * @param Article $article * @return RedirectResponse */ public function destroy(Article $article) { try { $article->delete(); } catch (\Exception $exception) { return redirect()->back()->withErrors(['An error occurred during the article deletion process.']); } return redirect()->route('article.index'); }
We use the Eloquent delete()
method to delete()
record, but an exception may occur. We have to handle this so that the user does
not see the page with error 500. So we redirect him back (we use the
back()
method of the redirect()
function builder) and
let him know that the removal process failed.
With this, we have completed a nice and fully functioning administration. If you did not succeed, you can download the project from the attached archive below. Otherwise, we can summarize the result of our efforts with this picture:
In the next lesson, Simple CMS in Laravel - Laravel Mix, we'll focus on the front-end part of our application. We will look at file compilation of JavaScript, SCSS, LESS and more.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 57x (44.45 MB)
Application includes source codes in language php