Lesson 4 - Finishing the calculator in Laravel
In the previous lesson, The first application in Laravel, we developed a simple calculator in Laravel. We created a model, a controller and a view.
In today's lesson, we will connect the individual parts of our application and also look at form validation.
Routing
In order to see how our application currently looks like, we need to access the controller via a URL. We use routing for this, we described its process in the first lesson. Let's now define our first route which connects the controller to an address.
Defined routes for our website can be found in the file
routes/web.php
. This file is unchanged since installation:
<?php use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/', function () { return view('welcome'); });
We have only one default route so far. It display's the main welcome Laravel
page via GET (static get()
method of the Route
class)
and is processed by an anonymous function. This way we can
process any route. We have already created a controller and we will use it now.
At the end of the file we will add our own definition of the GET route, where
instead of creating a function we will refer to the action of our
controller:
Route::get('calculator', 'CalculatorController@index');
Now if we enter /calculator
in the URL of our application, we
will see the following calculator:
It's time to get the calculator up and running. Now we return to the controller, where we process the form for POST method.
Form submission processing
In our CalculatorController
we define a new method called
calculate()
. It will accept the requests from the form and then
display the result:
/** * Process the form request and display the result along with the calculator form. * * @param Calculator $calculator * @return View */ public function calculate(Calculator $calculator): View { $a = request()->input('a'); $b = request()->input('b'); $operation = request()->input('operation'); $result = $calculator->calculate($operation, $a, $b); return view('calculator', [ 'operations' => $calculator->getOperations(), 'result' => $result, 'a' => $a, 'b' => $b, ]); }
As with the calculator display, we get an instance of the
Calculator
model into the $calculator
variable via
dependency injection. But the interesting part starts inside
the method.
To obtain the submitted values, we use the input()
method of the
Laravel class of the Request
class, which we obtain using the
helper function request()
. Then we'll use the obtained values for
our method calculate()
, which we defined in the last lesson in the
Calculator
model. Next, we display the
calculator.blade.php
view via the helper function
view()
just like in the index()
method. We will pass
the available calculator operations to the view, but on top of that we added the
result and the numbers sent via the form so we can display them again.
Now we'll create a new route in the routing file of the
routes/web.php
website as we did above. But now we will add a POST
action:
Route::post('calculator', 'CalculatorController@calculate');
Have you noticed that we have defined two routes with the same name, but one is GET and the other POST? Even so, our calculator will work fine. The framework itself determines which request it is and then calls the given method of the controller:
- The
index()
method in theCalculatorController
is called for display the form (GET). - After the form is submitted (POST), method
calculate()
is called in the same controller.
However, if we try to submit the form now, we will get error 419, even though everything should work. Or not?
CSRF token
Laravel protects our requirements against a CSRF attack. This protection is triggered for all non-read actions (all except GET). And what exactly is the CSRF attack?
Imagine a situation where we created a popular blog and someone would create a completely different page, where they would put a form encouraging the insertion of vulgar posts. However, this form would not allow data to be sent to his site, but to our blog. Unsuspecting users would suddenly write posts on our blog, even if they were not on this page at all and did not know that they were sending something there.
So we need to provide the form with a CSRF token, which verifies that the request was sent through our site and not through other site.
This is done using the Blade directive @csrf
, which is converted
to {{ csrf_field() }}
(this longer notation was used in older
versions). The directive will generate a hidden field with a CSRF token for the
form. So let's edit the form in the calculator.blade.php
view (in
the resources/views
folder):
<form method="POST" action="/calculator"> @csrf Operation:
If we try to submit the form now, everything will work as we imagined.
CSRF protection with middleware
CSRF protection is provided by one of the middlewares of the Laravel
framework. This is the layer by which the request is processed before it reaches
the controller. Such middleware handling all actions can be found in the file
app/Http/Kernel.php
. We have several of them for the
web
group:
protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ 'throttle:60,1', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ];
Our request will pass through all these middleware before it reaches the method of our controller. We can see that some of them are for encrypting cookies and creating a session. If you ever want to create your own middleware, this is exactly where you will add it. Subsequently, it will be applied to all actions on your website.
Form validation
As a programmer, we must assume that the user does not always fill in the field with what we require. If we try to enter text in the number field, our application will cause an error, because the model can only accept integers:
We can avoid this by validation. Validation rules can be
defined in the controller directly in the method of the given action. We will
use only a few rules, all of which can be found in the official
documentation. In our case all form fields must be filled in with whole
numbers only. Also, the selected operation must be supported by our model. So
let's modify the calculate()
method in our controller:
/** * Process the form request and display the result along with the calculator form. * * @param Calculator $calculator * @return View * @throws ValidationException */ public function calculate(Calculator $calculator): View { $this->validate(request(), [ 'a' => ['required', 'integer'], 'b' => ['required', 'integer', 'not_in:0'], 'operation' => [ 'required', Rule::in(array_keys($calculator->getOperations())), ], ]); $a = request()->input('a');
We start with the annotation, where instead of processing the
ValidationException
we pass it to the framework. Framework can take
care of it by himself, he redirects the user back even with the mistakes he has
made.
Then we call the validate()
method, which is defined in the
Controller
class and our controller inherits it. We have to pass an
instance of the Request
class, which can be obtained again via the
helper function request()
. As the second parameter, we pass a field
with validation rules. These can be defined only as a text. For others, such as
the in
rule, it is better to use the Rule
class. Also
note that we have solved the problem for division by zero, where the second
number must not be 0
.
I think that there is no need to explain the other rules, they are all described in the documentation.
If your environment cannot automatically detect classes from another namespace, be sure to add the following imports to the beginning of the controller class file:
use Illuminate\Validation\Rule; use Illuminate\Validation\ValidationException;
Listing errors
Now all we have to do is list the errors in our view to inform the user about
the mistakes he has made. Errors are contained in the $errors
variable, which is automatically passed to all views. There are more such
"undefined" variables, but so far only this is enough for us. So we will modify
our view and add a list of errors under the title:
<h1>Calculator</h1> @if ($errors->any()) <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> @endif <form method="POST" action="/calculator">
$errors
is an instance of the ViewErrorBag
class.
To find out if there are any error messages at all so that we don't display an
empty list, we use the any()
method. Then we get all the error
messages via the all()
method and print them. However, if you only
want to get an error message for a specific field, you can use the
first()
method and pass it the name of the field:
$errors->first('email')
In any case, only a list of errors will suffice for us
If we try to enter zero as the second number and create a non-existent calculator operation via the "Inspect element" (F12 key in the browser), we get two error messages:
Now we have a fully functioning calculator created via Laravel, on which we have shown the very basics of this PHP framework. If you do not understand anything, download the project from the attached archive and look at the solution. You can also ask in the discussion below the article if you are facing a problem. In any case, with the necessary knowledge that I mentioned in the first lesson, everything should be clear after a while.
That's all for this lesson.
In the next lesson, Simple CMS in Laravel - Project structure, we will dive into a new project where we will look at some parts of the framework in more depth.
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 81x (13.59 MB)
Application includes source codes in language php