Get up to 80 % extra points for free! More info:

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:

List of articles in the administration in the Laravel CMS - Laravel Framework for PHP

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

 

Previous article
Simple CMS in Laravel - Article creation
All articles in this section
Laravel Framework for PHP
Skip article
(not recommended)
Simple CMS in Laravel - Laravel Mix
Article has been written for you by Lishaak
Avatar
User rating:
No one has rated this quite yet, be the first one!
Author is interested in programming mostly at web development, sometimes he does funny video edits from his vacations. He also loves memes and a lot of TV series :)
Activities