DEV Community

Cover image for Laravel101: Validate your Application's Incoming Data
Kazem
Kazem

Posted on • Updated on

Laravel101: Validate your Application's Incoming Data

In the previous article, we learned how to send information to the server and save tasks in a table. However, what if the user sends wrong information or leaves the tasks title field empty? I told you! In such cases, we will encounter the following page:

Image description

To solve this problem, we need to ensure that the requested data is correct before adding a new record. We can evaluate the information entered using Laravel’s validate property in the request helper function.


Request Validation

To do this, let’s go back to the store function in the controller and define a set of rules to evaluate the entered information.

The first rule is to check if the Task title is required, so that the user cannot send an empty name.

public function store()
{
    request()->validate([
        'title' => 'required'
    ]);

    $task = new Task();
    $task->title = request('title');
    $task->save();
    return back();
}
Enter fullscreen mode Exit fullscreen mode

Now, if we leave the title field empty and send a request, we will see that the previous error page is not displayed, and only the page is refreshed. However, if we want to display the error related to the user’s request and maintain the user interface of the program, we need to display the errors variable. The errors variable contains JSON data, and when our request is rejected during the evaluation stage, it will return the relevant error message for us.

To display the error message from the evaluation stage, we need to make some changes to the blade file related to our create task. By doing so, when we send a request with an empty field, we will see the following result:

Image description

Let’s add some additional rules, such as a character limit:

Image description

Or ensuring that the Task title is not repeated (although this is usually used for usernames or internal site addresses, here we are using it for educational purposes):

Image description

You can also consider other conditions for the entered information, the list of which can be found at the laravel official doc here.

Another thing you may have noticed is that when the user encounters an error in their request, the field gets cleared. To prevent this, there is a useful helper function called old that allows you to return the value previously entered by the user. Let’s add this to our form:

Image description

Now, Let’s add another attribute to our task model which is tasks description, which let users time more about a task but this attribute definitely isn’t required and we want to user add it as an optional data.

To do that first we need to add another mitigation to our project to add another column into task table!

Please remember this important tip when working on a project that is still in development and not yet released. It’s crucial to keep the migrations simple and avoid creating too many separate migration files. Having numerous migrations can make it difficult to track and understand the data model attributes. when it comes to adding changes to a live, production-ready application, it’s necessary to create a new migration. Since, it’s crucial to avoid accidentally resetting any existing data.

But here, only for learning purposes I add another migration:

php artisan make:migration add_description_to_tasks_table
Enter fullscreen mode Exit fullscreen mode

As you see in following up and down function I just add one column into our tasks table:

/**
 * Run the migrations.
 */
public function up(): void
{
    Schema::table('tasks', function (Blueprint $table) {
        $table->text('description')->after('title')->nullable();
    });
}

/**
 * Reverse the migrations.
 */
public function down(): void
{
    Schema::table('tasks', function (Blueprint $table) {
        $table->dropColumn('description');
    });
}
Enter fullscreen mode Exit fullscreen mode

As you noticed you can use after method to specify the exact position where you want to add the column. Additionally, you can use the nullable attribute to allow the column to contain null values. Although you can also set a default value by default method for the column, but it recommended using nullable as it’s more optimized for query speed. However, please note that there are cases, such as with boolean columns, where using nullable may not be feasible or appropriate.

Now let’s run it and check the results:

Image description

Everything works fine but the description is not right after!

While Laravel provides convenient methods for working with databases, there are limitations with certain methods, especially when using SQLite. Unfortunately, SQLite does not support inserting a column at a specific position in a table. So, even if you use the after method in Laravel, the column will not be placed immediately after the specified column as expected. This is a limitation of SQLite itself, and it's not something that can be directly addressed through Laravel methods.

Fortunately, this limitation only applies to SQLite databases. If you’re using PostgreSQL or MySQL, you won’t encounter this

To fix this, We can remove last migration and as just change tasks migration files. We can reset all tables with:

php artisan migrate:reset
Enter fullscreen mode Exit fullscreen mode

But suppose that we have some data we won’t to reset them, so the solution here is rollback all migration files!

To rollback last migration you can use following command:

php artisan migrate:rollback 
Enter fullscreen mode Exit fullscreen mode

And to rollback certain amount of migrations:

php artisan migrate:rollback --step=<steps_count>
Enter fullscreen mode Exit fullscreen mode

I just remove last migration file and do our changes in tasks migration file, you’ll see after running php artisan migrate command our desired changes will be added!

I add some rule for description and as you can see in following pic everything works fine:

Image description

That was super simple? huh!

Now let’s add another field? what about due to date and time? To do that let’s add another time column called expired_at which is also nullable

Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('description')->nullable();
    $table->timestamp('expired_at')->nullable();
    $table->timestamps();
});
Enter fullscreen mode Exit fullscreen mode

That’s it let’s migrate tasks table again:

php artisan migrate:rollback
php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Then We have to update store function as bellow:

public function store()
{
    request()->validate([
        'title' => 'required|min:3|max:120|unique:tasks,title',
        'description' => 'nullable|min:3|max:255',
        'expired_at' => 'nullable|date|after:now'
    ]);

    $task = new Task();
    $task->title = request('title');
    $task->description = request('description');
    $task->expired_at = new \DateTime(request('expired_at'));
    $task->save();

    return redirect("/");
}
Enter fullscreen mode Exit fullscreen mode

There are a few important points to consider. Firstly, when you need to validate a date in Laravel, you can use the built-in date validation rules. Let’s review them below:

date: This validation rule checks if a given input is a valid date:

'date_field' => 'required|date'
Enter fullscreen mode Exit fullscreen mode

date_format: This rule validates if the input matches a specific date format:

'date_field' => 'required|date_format:Y-m-d',
Enter fullscreen mode Exit fullscreen mode

after:date: This rule ensures that the input date is after a specified date:

'date_field' => 'required|date|after:2023–01–01', // or maybe tommorow
Enter fullscreen mode Exit fullscreen mode

after_or_equal:date: This rule validates if the input date is equal to or after a specified date:

'date_field' => 'required|date|after_or_equal:2023–01–01',
Enter fullscreen mode Exit fullscreen mode

before:date: This rule ensures that the input date is before a specified date:

'date_field' => 'required|date|before:2023–01–01',
Enter fullscreen mode Exit fullscreen mode

before_or_equal:date: This rule validates if the input date is equal to or before a specified date:

'date_field' => 'required|date|before_or_equal:2023–01–01',
Enter fullscreen mode Exit fullscreen mode

By utilizing these date format validation rules, you can ensure that the input dates meet your specified criteria within your Laravel application.

The second point to note is casting expired_at data as the exact datetime PHP data type. Sometimes, Laravel doesn't automatically recognize the data type, even if we validate it. to resolve it we can use Datetime helper class. or casting inside the data model! How?!

When we created our Task model, we actually created an Eloquent model, which is an amazing feature in Laravel. However, so far, we haven't made any modifications to it. By default, every Eloquent model we create is stored in the app/models directory of our Laravel project (note that this location may vary in older versions). To cast the data in our model, we need to make some changes by adding a rule to the casts variable, like this:

protected $casts = [
    'expired_at'  => 'datetime'
];
Enter fullscreen mode Exit fullscreen mode

By this defining we don’t need to use Datetime class anymore:

public function store()
{
    request()->validate([
        'title' => 'required|min:3|max:120|unique:tasks,title',
        'description' => 'nullable|min:3|max:255',
        'expired_at' => 'nullable|date|after:now'
    ]);

    $task = new Task();
    $task->title = request('title');
    $task->description = request('description');
    $task->expired_at = request('expired_at');
    $task->save();

    return redirect("/");
}
Enter fullscreen mode Exit fullscreen mode

Please note that you don’t need to casting for the created_at and updated_at columns, as Laravel handles them by default. However, in general, it's considered a best practice to explicitly define all the data model's fields and cast them accordingly:

protected $casts = [
    'title'       => 'string',
    'description' => 'string',
    'created_at'  => 'datetime',
    'updated_at'  => 'datetime',
    'expired_at'  => 'datetime'
];
Enter fullscreen mode Exit fullscreen mode

This approach ensures that you have a clear understanding of each model's attributes and their respective data types. Just keep in mind that in real-world projects, it can sometimes be challenging to determine all the fields of a data model by solely relying on migrations.

Here is the result:

Image description


Again every time you need to walk through project you can find out here!

That’ it! I hope you found this tutorial enjoyable!

Top comments (0)