Skip to content

Request Handling & Responses

Adarsh Kumar Maurya edited this page Dec 2, 2018 · 2 revisions

Request Handling & Responses

Chapter Overview

Hi everyone, welcome back to chapter four in this tutorial on building a RESTful service with Laravel. Until now we had a look at how we could set up our routes, and get our requests to the appropriate control actions. In this chapter, I want to have a look at what we actually do with these requests, how we can interact with the data passed with the requests, how we can create appropriate responses and send them back, and why validation is so important.

Laravel Request & Response Flow

I want to start by again having a look at the Laravel request and response flow.

        Simplified Laravel Request & Response flow

Issue Request     --->    Client <-----------------------
                            |                            |
Forward request             |                            |
to correct        --->    Router <----------------       |
recipient                   |                     |      |
                            |               Redirect  Respond     
Scans requests and          |  (Request rejected) |      |
takes appropriate  ---> | Middleware |----------------   |
action                      |                            |
                            |(Request may pass)          |
Finally handles    --->   Controller----------------------
request(Business Logic)

As you are aware the client, for example the browser or Postman like we used in the last demo, issues a request, which should reach some controller in the back end on our service. It's the job of Laravel's router to make sure that the request is forwarded to the correct controller. However, one concept I haven't talked about that much is middleware.

Middleware is applied to requests, if we specify that in the controller or the routes/web.php or api.php etc file, and the job of middleware is to scan the request and check if some conditions specified in the middleware are fulfilled. If those conditions aren't fulfilled, the request will be rejected and either be redirected to another route in the router file, or a response will be sent back to the client containing an error message. If the request may pass, then of course we reach the controller, and now the controller may do whatever it has to do with that request. After finishing that, the controller will send back either a redirection to get to another route, or a response declined.

Handling Input

So if the request successfully reaches the controller, how can we handle the input sent with the request? More specifically, we're of course interested in fetching the data, which might be sent with a request.

For example, we might have a JSON, an image, or an XML file being sent with the request. Laravel offers us a lot of help with handling the data, which is attached to a request. It makes it very easy to extract JSON or URL encoded data, as well as to access a file. If we have data like, for example, XML which is not natively supported, we would have to write that logic on our own though, or implement some kind of third-party package.

Once the data is extracted, we of course want to apply our business logic on the data, and use it, for example, to store it in the database, or store a file somewhere on our server. And again, for XML we would have to come up with our own business logic here to get to the data in the first place.

After we finished everything we wanted to do with the data, it's of course time to build a response, and this response will then be sent back to the client, but I'll come back to responses later in this chapter.

The question is how can we actually access the data? It's great that Laravel supports us, but how does it support us? Well, accessing input is really straightforward. We may use the input method on the request object injected into our controller action.

Now this input method takes one argument, and that is the name of the field we want to access in the data sent with the request.

//JSON/ urlencode data
$title = $request->input('title');

For example, if we get some post data sent to our controller, and the data is encoded as JSON, then we might retrieve the 'title' of the post by accessing the title field in the JSON data, provided this data has a 'title' field, but it is something your service has to kind of dictate or which your service sets up, and of course clients or client applications using your service will have to stick to the field names you set up in your application, and therefore the documentation of your application. Keep in mind that the request object here is only injected into controller actions which receive data encoded in the body of a request.

If you have a controller action serving, for example, a GET request where all the data we need is appended to the URL, in such a case we wouldn't get a request object injected because, well, the data is available from the URL, and therefore Laravel would directly pass data appended to the URL as arguments to the controller action, and you will also see this in the demo later.

//File
$photo = $request->file('photo');

Now of course you may not only retrieve fields from JSON or URL encoded data, you may also retrieve files. And Laravel makes it easy too by using the file method on the request object, which allows you to do just that, retrieve the file from a field in the data passed to this controller action.

Demo: Handling Input

Let's see this in action, and I will start with the MeetingController in our project. Here, I want to start with the store action, which allows us to create a new meeting, and later on in this tutorial, save it to the database. Now this controller action will be reached through a POST request, holding all the data encoded in the body of the request. Therefore, Laravel set this controller action up in a way that we get the request object, which allows us to access the data fields encoded in the body of the request, injected into this controller action. So we have it available to use like a normal variable throughout this controller action. Then I may retrieve the data from the request by, for example, creating a new variable, and accessing the different fields, like the title, through the input method on this injected request object. So our service will only work correctly if we get data in the format we need it to be to retrieve it correctly. Now I will just copy this three times to also retrieve the description, the time, and the user_Id which is passed with this request. So all I do here for the moment is retrieve all the data from my request, and store it in separate variables.

Now let's just copy that, and paste it into my update method, because there I will have to very same data fields since it is pretty similar, right? I'm updating it with the same fields I was creating it, but notice one thing. In this method we're not only getting the request object injected, but we also receive the Id as an argument of this function. And we're getting the ID here as an argument, because when updating a resource we not only get the data encoded in the body of the request, but we also have the ID of the resource of the meeting we want to update appended to the URL at the very end. And therefore, Laravel passes us both arguments, the request, which we need to extract the data encoded into it in the body of the request, and the ID, which is appended to the URL. So these mixed actions are possible.

Now regarding the ID, the same is true for the show method where we are only getting an ID. Now we're not receiving a request object here because this method doesn't require any data being passed to it, except for the ID of the meeting we want to retrieve. Therefore, this argument is all we need here for this method to work correctly. So just to clarify that again, Laravel passes us data appended to the URL, or sent via the URL, as arguments to our controller actions, and therefore we may directly use these variable we're getting there throughout this controller action.

So since I've got no more POST requests in the MeetingControllerI will continue with the RegistrationController, and here I want to start with the store method, which allows us to sign up a new user for a meeting, or basically register a user for a meeting. Now this method gets the request object because, well, it is reached through a POST request, and regarding the data sent to this controller action, I will extract the meeting_id and the user_id of the user who should be registered for this meeting.

Now finally to finish this demo here, I will have a look at AuthController, where we will have two controller actions, both reached by a POST request. The first one is to create a new user, so to sign up a user. Now here, according to the blueprint laid out in chapter two, I will retrieve the name of the user, the email, and the password. Now we'll copy the email and password field to the signin method, because here I basically need the same fields, but I don't need the name since we're signing in a user, I'm only interested in the email with just the unique identification criteria provided by user, and of course the password.

Responses

So extracting the data is a major first step, because without data, or anything sent with the request, it's hard to do anything useful, right? However, it's not the end of the road. The next interesting question is how can we actually build a response, and send it back?And how should such a response be structured?

First, I want to have a look at some common responses. For example, we might send an HTML file, though we won't with our RESTful service, this is more of a response if you have a traditional server-side application where you send back a view to the browser. For a RESTful service, it's more interesting to send back JSON-encoded data, which is certainly a common data structure to be seen. We might also send back a file, think of Dropbox for example, if we want to provide a file to the user, or we might just send plain text, like we're currently doing this in the demo, just issuing a single message, and therefore just sending a single line of text. So these are common types of responses, and I already had a look at data types in chapter two.

Building Responses

So the more interesting question is, how are these responses created and sent to the client? Well, as before with requests, Laravel makes this easy too. Returning plain text is what I'm currently doing in the demo, where it just returns a string with each method, or each controller action.

//HTML Page/View
return response()->view('index');

However, if the goal is to return something else than just plain text, for example a view might be returned by using Laravel's built-in response helper method, which may be executed like this, and this method will return a response object, or actually the response object which will be sent back to the user. With this response helper method, it's easy to use another method on this response, like for example, the view method, to send back a view. Now this method would simply take the name of the view, which would be sent back as an input, and send back the HTML code to the browser. Now again, HTML will not really be what you use in RESTful services, here a JSON method is much more helpful.

//JSON
return response()->json([
    'msg' => 'post created',
    'post' => [ 'title' => $post->title]
], 200);

With this method, it's easy to send back, well, JSON encoded data to the client. The first argument to that method is the data we want to send back, but since you can't write JavaScript objects, or JSON data in PHP, here instead an associative array is used. This associative array is structured like JSON data to be sent back, and if you were to replace the square brackets with curly braces, and the arrows with colons, I think you're able to see the JSON object, or JavaScript object, which is created by the JSON function in the end, and then sent back to the client. Now the second argument you see here is one you may specify for all response methods, and this argument sets the status code of the response. Now here for example, the status code might be 200 to indicate that everything is okay.

// File download
return response()->download($pathToFile);

Now to pick up the example with the file being sent back, you might use the download method to indicate that a file is being sent back, and should be handled as such.

Response Content

So this is how you create and send responses, but what should be inside the response? How is the response structured, and which content should it contain? Now that of course greatly depends on the service you're building, and there is no clear right or wrong here. It has to be decided upon your requirements, and upon the decision of what you want to provide with your service.

But here are some ideas, or here is some content you will probably see a lot in responses being sent by RESTful services.

  1. The first important piece is a message. The messages oftentimes make sense, because clearly in an error case you want to indicate what went wrong, or how it could possibly be fixed, but also in a success case you may want to indicate what was done successfully, which data was written to the database, and so on. So providing a message is certainly a great idea.

  2. Now the next piece is of course very important, data. You may not always append data because, well not all your routes will return data, but oftentimes they will. In the end most of the time data is what makes up your service, and therefore you will probably return data a lot. Now concerning the structure of the data, that is really up to you because it greatly depends on the service you are building. The data sent back should of course satisfy the needs of clients using your RESTful service, but it's also up to you which kind of data you want to hide, which kind of data you want to show, and that really is what makes up your service in the end.

  3. Now the last piece, or the last idea here is to send a link. Now that is something which is part of the RESTful service specification, if you want to put it that way. Oftentimes though, it is left out. A lot of so called RESTful services don't provide such links even though they should, and it is certainly a good practice to do so. RESTful services should be able to describe themselves, and this is a very good practice to provide links to related resources with the responses you send back. So let me give an example. If you create a new post, for example, through a RESTful service, then it would be a great response to not only tell the user that the post was created successfully, and possibly send back data about that created post, but also to send back a link to that newly created post. So to the RESTful route, which should be accessed, to fetch data for that post.

Demo: Responses

Back in the MeetingController, it's now time to actually create and send responses. Now since we're currently not storing any data in the database, for now all I will do is create responses with a message in the link, and send back data, or some dummy data, whatever is appropriate, which we'll send with the request. Of course this will change later in this tutorial when the database is introduced to this project, and when database operations were explained. So I want to start with the store action here in the MeetingController again. We already fetched all the data in separate variables, and now I want to create a new variable, which I will call meeting. This variable will be an associative array, and here of course, I will create a kind of meeting object, of course, later on when the database is available an actual object will be created, but for now this associative array should represent a meeting object. And this of course is important as later it will be transformed or sent back as JSON data through the JSON method.

   public function store(Request $request)
    {
        $title = $request->input('title');
        $description = $request->input('description');
        $time= $request->input('time');
        $user_id = $request->input('user_id');

        $meeting = [
            'title' => $title,
            'description' => $description,
            'time' => $time,
            'user_id' => $user_id,
            'view_meeting' => [
                'href' => 'api/v1/meeting/1',
                 'method' => 'GET'
            ]
        ];

        $response =[
            'msg' => 'Meeting created',
            'meeting' => $meeting
        ];

        return response()->json($response,201);
        //return "It works";
    }

Now this meeting object or array here, will receive a title, and of course I will use the title variable as a value here. The same is true for description and time, as well as the user_id. Now I also want to append something else, or I want to implement something else in this meeting object, or array here, the link. I will give this link a key name of view_meeting to describe what the link does, and then the link itself should also be an associative array, where the first element is the actual link, api/v1/meeting, and then /, and a dummy meeting ID as we currently don't have any real IDs as nothing stored in the database. The second element will have the key method, and should just describe which method needs to be used to actually access this route successfully, so this is GET in this case. So this will be the meeting being sent back, including the link, and with this data I'm able to create a new response, and this should also be a new variable called response, and this response again, will be an associative array to easily transform this into a JSON data later. And this array should have the message as a first element, where let's just say, Meeting created, and of course the second element will be the meeting itself, with a key of meeting to identify it. Now with this response variable created, it's now time to use Laravel's response() helper method to get a reference to the response object being sent, and use the JSON method to transform this into a JSON response, and also to transform the data set with the response into JSON. So here, of course, I pass the response variable, and I will also set the status code to 201 to indicate that everything was successful and the new resource was created.

Now I won't go through the creation of this response object, and the response for all controller actions since it really doesn't vary that much. It's always the same, and all I do is exchange the link names, the links, and of course the actual data being sent back, as well as the messages, but the overall structure doesn't change. However, I will go through all the important changes, or interesting parts so that nothing gets lost. And of course, the whole code is appended to this chapter. Now with all the other responses created, I want to guide you through some interesting things, or things I want to point out.

For example, when having a look at the index controller action,

    public function index()
    {
        $meeting = [
            'title' => 'Title',
            'description' => 'Description',
            'time' => 'Time',
            'user_id' => 'User Id',
            'view_meeting' => [
                'href' => 'api/v1/meeting/1',
                'method' => 'GET'
            ]
        ];


        $response =[
            'msg' => 'List of all Meetings',
            'meeting' => [
                $meeting,
                $meeting
            ]
        ];

        return response()->json($response, 200);
        //return "It works";
    }

Note that no longer one single meeting is being passed, but instead an array of meetings, and this of course is the case because the index controller action should actually return a list of all meetings, and not just one.

Now when having a look at the destroy controller action, which is responsible for deleting a meeting, you will see that no longer is the view meeting link provided, but instead the link leads to the creation of a new meeting.

  public function destroy($id)
    {
        $response =[
            'msg' => 'Meeting deleted',
            'create' => [
                'href' => 'api/v1/meeting',
                'method' => 'POST',
                'params' => 'title, description, time'
            ]
        ];

        return response()->json($response, 200);
        //return "It works";
    }

However, the more interesting part is the new key, which is introduced in the link array here. This third element, params, lists all the required parameters this route or this controller action needs to work correctly. Now I think this is a good idea since this is a post request route, and therefore data is not appended in the URL automatically, but instead has to be provided by the user.

In the RegistrationController, in the store controller action where users are registered for meetings, I not only return the meeting or the user, but I instead return both data fields since I think it's interesting to know which user signed up for which meeting.

public function store(Request $request)
    {
        $meeting_id = $request->input('meeting_id');
        $user_id = $request->input('meeting_id');

        $meeting = [
            'title' => 'Title',
            'description' => 'Description',
            'time' => 'Time',
            'user_id' => 'User Id',
            'view_meeting' => [
                'href' => 'api/v1/meeting/1',
                'method' => 'GET'
            ]
        ];

        $user = [
            'name' => 'Name'
        ];

        $response =[
            'msg' => 'List of all Meetings',
            'meeting' => $meeting,
            'user' => $user,
            'unregister' => [
                'href' => 'api/v1/meeting/registration/1',
                'method' => 'DELETE'
            ]
        ];

        return response()->json($response, 200);
       // return "It works";
    }

The same is of course here true for the destroy method, which is responsible for unregistering a user from a meeting. We don't delete the meeting or the user, simply the registration is deleted, or the link between meeting and user, and therefore there is no reason not to return meeting and user, or to delete them, this would be very wrong, but instead I will still return the data, or the information about which users signed out of which meeting.

    public function destroy($id)
    {
        $meeting = [
            'title' => 'Title',
            'description' => 'Description',
            'time' => 'Time',
            'user_id' => 'User Id',
            'view_meeting' => [
                'href' => 'api/v1/meeting/1',
                'method' => 'GET'
            ]
        ];

        $user = [
            'name' => 'Name'
        ];

        $response =[
            'msg' => 'List of all Meetings',
            'meeting' => $meeting,
            'user' => $user,
            'unregister' => [
                'href' => 'api/v1/meeting/registration/1',
                'method' => 'DELETE'
            ]
        ];

        return response()->json($response, 200);
        //return "It works";
    }

However, note that only the name of the user is returned, and not the complete user data like password or email. Because I think this data should be kept private, and not responded or sent out to the public.

Now in the AuthController I do return the complete user data, but it is only for testing purposes and will change later on.

class AuthController extends Controller
{
    public function store(Request $request){
        $name = $request->input('name');
        $email = $request->input('email');
        $password = $request->input('password');

        $user = [
            'name' => $name,
            'email' => $email,
            'password' => $password,
            'signin' => [
                'href' => 'api/v1/user/signin',
                'method' => 'POST',
                'params' => 'email, password'
            ]
        ];

        $response =[
            'msg' => 'User created',
            'user' => $user
        ];

        return response()->json($response,201);
        //return "It works";
    }

    public function signin(Request $request){
        $email = $request->input('email');
        $password = $request->input('password');

        $user = [
            'email' => $email,
            'password' => $password,
            'signin' => [
                'href' => 'api/v1/user/signin',
                'method' => 'POST',
                'params' => 'email, password'
            ]
        ];

        $response =[
            'msg' => 'Authenticated',
            'user' => $user
        ];

        return response()->json($response,200);
        //return "It works";
    }
}

Nothing spectacular outside of that in this controller though. Now with all these changes in place, it's finally time to test it.

And for this I will of course use Postman again. Now here in order to send a request, like for example creating a new meeting, the first step is to go to the Body tab here, and provide the data, which should be sent with the request. Also, make sure to provide the header X-Requested-With and set it to XMLHttpRequest for everything to work correctly. Now when creating a new meeting, which data has to be provided?

{
	"time" : "20181203330IST",
	"title" : "Test Meeting 2",
	"description" : "Test",
	"user_id" :2
}	

Well of course title, description, and user ID, and the time. Now note that in the data I entered here, the time format I have is a very long string, which consists of a four-digit year, the month, the day, the date time with hours and minutes, and a time zone of this time. Now this is a certain date format I want to enforce throughout this application, and I will come back to this in this chapter. Now with that data provided, if I click Send, you now see the success message, you see the data I sent there gets sent back, and you also can see the link, which already routed which should be used to access the individual meeting.

{
    "msg": "Meeting created",
    "meeting": {
        "title": "Test Meeting 2",
        "description": "Test",
        "time": "201801301330IST",
        "user_id": 2,
        "view_meeting": {
            "href": "api/v1/meeting/1",
            "method": "GET"
        }
    }
}

I can also try out another route, like for example, creating a new user. And again here, I prepopulated the body field and set the header, and of course I'm sending a name, the password, and the email, so basically the data our controller action expects to get to function correctly. Now if I click Send, you again see that this is successful, that the user was created, and you see the data we sent there. Now of course you may try out all the other routes, and don't forget to set a header as shown before, and pass the right data, and you should be able to see the success messages, and the data being sent back for all those routes.

Validation

So that's all great, but what happens if I create a new meeting, and get rid of the title, and then send this request?

{
	"time" : "20181203330IST",
	~~"title" : "Test Meeting 2",~~
	"description" : "Test",
	"user_id" :2
}	

After sending this I still get a response, but notice that I have this null value here at the title field.

{
    "msg": "Meeting created",
    "meeting": {
        "title": null,
        "description": "Test",
        "time": "201801301330IST",
        "user_id": 2,
        "view_meeting": {
            "href": "api/v1/meeting/1",
            "method": "GET"
        }
    }
}

This, of course, is the case because I did not supply a title. However, the biggest issue here is not that the response shows me Null, but if that were a real application we might have tried to create a database entry with an empty title, or with no title, or in the worst case we might not only have tried that, we may have been successful, and that certainly shouldn't happen. So this clearly is a case for validation.

But, do we really need it, can't we trust our users? No, we really can't because we have different user profiles.

User Profiles

  1. We might have The Geek. The Geek is an expert, he reads all the documentation, and he never forgets anything. Therefore all the data we receive by The Geek is complete and without any errors, we don't receive invalid emails addresses, or anything like that. If we only had The Geek, we wouldn't need validation because we would never receive wrong data, and therefore our controller action could never fail.

  2. However, we not only have The Geek, we also have The Hacker. Now The Hacker is kind of the opposite, The Hacker tries to test our application. The Hacker will deliberately pass wrong or incomplete data to see what happens, and we certainly have to defend against that. And we don't want our application to crash or to store wrong data, just because some wrong data is sent.

  3. However, in most cases we neither have The Geek nor The Hacker, instead we have The Normal One, and that's you and me. And from time to time we forget to pass the @ sign in the email address, or we have a typo. And, of course, a good application should handle such a case, and therefore we need validation to make sure we're only working with data, which has the format and the structure we need it to have.

REAL CONTROLLER DUTIES

Request - JSON | PNG | XML
  |
Validate Input - Laravel Validator(JSON, PNG)| Custom ThirdParty(XML)
  |
Extract Data - Access Input | Access File | Access File(parse manually)
  |
Apply Business Logic - Use Parsed Data | Store File 
  |
Response - Build Response


Therefore, our controller no longer looks planned, but instead we have our real controller duties for a real life application. And in such a controller action, before we do anything with the data, and that includes extracting it, we validate it, and Laravel helps us a lot here because Laravel has a built-in validator, which covers all the basic file formats, or data formats, we might want to validate in our application. Now, for example, file formats or data formats like XML are not supported out of the box, and we would have to write our own validators here, or install some third-party plugins, but JSON encoded data can be handled with the Laravel default validator, and this is a great addition and help.

Input Validation

So how does validation then work in Laravel? In the controller action which gets the request object injected we can simply use the validate method on the controller itself. So each controller extending Laravel's base controller has this validate method built in. Now this validate method takes two arguments.

$this->validate($request, [
    'title' => 'required | max:120',
    'content' => 'required'
]);

The first argument is the date of the request, which should be validated, and the second argument specifies which field should be validated, and which rules should be used for validation. So the key is to find the fields, and the values define the rules.

So for example here where we have post, if this post has a title and we want to make sure that this title is provided, we would use the require rule, and we can also chain multiple rules by using the pipe or | separator. So for example here, we might also specify a maximum length of this title, and as you can see some rules take input or take an argument. Such an argument is provided by using or specifying a colon followed by the value which should be passed into this rule, like 120 in this case to set the maximum length to 120 characters.

Now with this validator you could also check the file type of a file by using the mimes rule, and then passing the accepted file types separated by commas.

//Check filetype
$this -> validate($request, [
    'photo' => 'mimes:jpg, png'
]);

Now maybe you're wondering how you could validate GET requests, or any requests receiving the data in the URL.

Well, there is no need to validate these requests, because if you think about it if the data is left out, then the URL wouldn't be reached anyway, and if the data is provided, but for example we want to retrieve a POST by an ID which doesn't exist, well then the select action accessing the database will just return that no meeting was found, so nothing bad happens here.

This leads to the question, which validation rules Laravel offers out of the box?

And the answer can be found in the official Laravel documentation, and of course you will find a link to this page in the materials provided with this chapter. So as you can see, these are quite a lot of rules, and these rules should really cover the majority of cases you need in your application.

Now what happens if a rule is violated? What do we have to do then? The easy answer is, nothing. Laravel automatically detects if the right headers are set, that is why we had to set them with Postman. If the request was sent as an AJAX request, so an XMLHTTP request, and if this is the case, Laravel will automatically reply with a JSON object, which holds the appropriate error messages. So nothing more to do for the creator of the service besides throwing in this validate method and let Laravel do the rest.

Demo: Validation

So now after seeing this in theory, it's time to apply it in the demo of this tutorial.

I will again start in the store controller action in the MeetingController, and of course validation is the very first thing which happens in this controller, because data shouldn't be used before being validated, right?

 /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request $request
     * @return \Illuminate\Http\Response
     * @throws \Illuminate\Validation\ValidationException
     */
    public function store(Request $request)
    {
        $this->validate($request,[
            'title' => 'required',
            'description' => 'required',
            'time' => 'required|date_format:YmdHie',
            'user_id' => 'required'
        ]);
        
        $title = $request->input('title');
        $description = $request->input('description');
        $time= $request->input('time');
        $user_id = $request->input('user_id');

        $meeting = [
            'title' => $title,
            'description' => $description,
            'time' => $time,
            'user_id' => $user_id,
            'view_meeting' => [
                'href' => 'api/v1/meeting/1',
                 'method' => 'GET'
            ]
        ];

        $response =[
            'msg' => 'Meeting created',
            'meeting' => $meeting
        ];

        return response()->json($response,201);
        //return "It works";
    }

So as shown before, it's simply used by using the validate method on the controller itself, so calling the built-in validate method here, and then passing the injected request object.

Next I create the associative array specifying the fields which should be validated, and the rules used for validation. Now when creating a new meeting I certainly want to have a title, but the only validation I want to run on this title is the required rule, to make sure that a title is provided. The very same is true for the description, which also should be required, and no other rules are applied on this field. Now on time field, it's a little bit more interesting. It should also be required, but I'll chain another rule here, the date_format rule, because I want to enforce a certain date format throughout this application. Now why is such a date format important? Because if your application stores this date in the database, you have to have a date format, which can be interpreted by that database, and therefore it's important to kind of standardized the input date so that you can, again, run a standardized transformation on that date to store it in a uniform format, hence, I set up a date format with which I came up here, and you may change that if you prefer another one, which is defined by having a four-digit year, then the month, then the day, then the hours, and the minutes, and finally the time zone for which this time should be valid, or in which this time lies. So the question is how do we make sure that these characters I entered here lead to the date I want, or to the date format I want it to lead? Laravel's built-in date format validator uses the date format's PHP's built-in date_create_from_format function(datetime create from format). And on this page, you'll find a table with a detailed explanation, which character describes which part of our date, or how the date may be encoded.

Finally, I finished this validation here with the user_id, which only should be required, nothing else right now.

Now as before, I will repeat this step for all other controller actions, and what I'll do is I will alter this validation appropriately to check the respective values each controller action needs, or the data each controller action needs to function correctly.

    public function update(Request $request, $id)
    {
        $this->validate($request, [
            'title' => 'required',
            'description' => 'required',
            'time' => 'required|date_format:YmdHie',
            'user_id' => 'required'
        ]);

Now to give a quick guide through all the validations I just created, the update method just looks like the create or the store method here, since it takes the same data to work correctly.

In the RegistrationController and the store controller action there, I just validate if a meeting and a user ID have been provided.

    public function store(Request $request)
    {
        $this->validate($request, [
            'meeting_id' => 'required',
            'user_id' => 'required'
        ]);

And in the AuthController I validate if name, email, and password are provided, but note that the email is also checked with the email rule, which makes sure that email is a valid email format, so with an @ sign, and .com, or anything like that at the end, and password is validated with the minimum rule as well, which just makes sure that it has at least five characters in this case, since I want to enforce a certain minimum length of passwords.

    public function store(Request $request){
        
        $this->validate($request, [
            'name' => 'required',
            'email' => 'required|email',
            'pasword' =>'required|min:5'
        ]);

Now for signing in I always do have the same validators, but for demo purposes I left out the min rule here for the password, because I don't really care how long the password here is, since I'm not creating a new user, I just want to sign a user in. So, if the email is found in the database, I'll just compare the passwords, and if they don't match the user is not signed in, if they match the user is signed in. It's not important how long the password is since this is already checked upon user creation in the sign up route.

Great, so now that validation is in place it's again time to test this with Postman, and send requests to the routes. I'll try this out with creating a new meeting, and let me show you what happens if I now delete the title and send the request.

{
	"time" : "201801301330IST",
	"description" : "Test",
	"user_id" :2
}	

As you can see I get the error message that the title should be provided.

{
    "message": "The given data was invalid.",
    "errors": {
        "title": [
            "The title field is required."
        ]
    }
}

So this works. What happens if I change the date format, and just leave out the minutes?

{
	"time" : "2018013013IST",
	"title" : "Test Meeting 2",
	"description" : "Test",
	"user_id" :2
}	

As you can see this is now also added to the error JSON object I get back, to tell me that the date format I specified is invalid.

{
    "message": "The given data was invalid.",
    "errors": {
        "time": [
            "The time does not match the format YmdHie."
        ]
    }
}

Let's also try this out for creating a new user. And let's say I want to create a user with a password shorter than five characters.

{
	"name" : "Adarsh",
	"email" : "contact@softhinkers.com",
	"password" : "te"
}

Of course I get back an error message that the password should be at least five characters long or may get The pasword field is required.

{
    "message": "The given data was invalid.",
    "errors": {
        "password": [
            "The password must be at least 5 characters."
        ]
    }
}

Now if I also delete the email address, or let's say I don't delete it, but I delete the @ sign to make it invalid, of course the error message is thrown that email is now no longer a valid email address.

{
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "The email field is required."
        ]
    }
}
{
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "The email must be a valid email address."
        ]
    }
}

Adjusting Validation Messages

So validation works, however, what has to be done to change the messages sent back upon the validation failures?

{
    "message": "The given data was invalid.",
    "errors": {
        "password": [
            "The password must be at least 5 characters."
        ]
    }
}

To change these messages, back in the project, go to the resources folder, and then the lang folder for language, and here in the en folder you'll see a file named validation.php. This file holds one single array, and this array specifies the rules as keys, and the appropriate error messages, which should be output as values. Now in these values variables can be used by prepending a colon at the beginning, for example, in the maximum rule to output the specified maximum number of characters which should be allowed. This is how error messages can be changed, and of course by providing other subfolders than en, you can also provide other languages too.

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Validation Language Lines
    |--------------------------------------------------------------------------
    |
    | The following language lines contain the default error messages used by
    | the validator class. Some of these rules have multiple versions such
    | as the size rules. Feel free to tweak each of these messages here.
    |
    */

    'accepted' => 'The :attribute must be accepted.',
    'active_url' => 'The :attribute is not a valid URL.',
    'after' => 'The :attribute must be a date after :date.',
    'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
    'alpha' => 'The :attribute may only contain letters.',
    'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.',
    'alpha_num' => 'The :attribute may only contain letters and numbers.',
    'array' => 'The :attribute must be an array.',
    'before' => 'The :attribute must be a date before :date.',
    'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
    'between' => [
        'numeric' => 'The :attribute must be between :min and :max.',
        'file' => 'The :attribute must be between :min and :max kilobytes.',
        'string' => 'The :attribute must be between :min and :max characters.',
        'array' => 'The :attribute must have between :min and :max items.',
    ],
    'boolean' => 'The :attribute field must be true or false.',
    'confirmed' => 'The :attribute confirmation does not match.',
    'date' => 'The :attribute is not a valid date.',
    'date_equals' => 'The :attribute must be a date equal to :date.',
    'date_format' => 'The :attribute does not match the format :format.',
    'different' => 'The :attribute and :other must be different.',
    'digits' => 'The :attribute must be :digits digits.',
    'digits_between' => 'The :attribute must be between :min and :max digits.',
    'dimensions' => 'The :attribute has invalid image dimensions.',
    'distinct' => 'The :attribute field has a duplicate value.',
    'email' => 'The :attribute must be a valid email address.',
    'exists' => 'The selected :attribute is invalid.',
    'file' => 'The :attribute must be a file.',
    'filled' => 'The :attribute field must have a value.',
    'gt' => [
        'numeric' => 'The :attribute must be greater than :value.',
        'file' => 'The :attribute must be greater than :value kilobytes.',
        'string' => 'The :attribute must be greater than :value characters.',
        'array' => 'The :attribute must have more than :value items.',
    ],
    'gte' => [
        'numeric' => 'The :attribute must be greater than or equal :value.',
        'file' => 'The :attribute must be greater than or equal :value kilobytes.',
        'string' => 'The :attribute must be greater than or equal :value characters.',
        'array' => 'The :attribute must have :value items or more.',
    ],
    'image' => 'The :attribute must be an image.',
    'in' => 'The selected :attribute is invalid.',
    'in_array' => 'The :attribute field does not exist in :other.',
    'integer' => 'The :attribute must be an integer.',
    'ip' => 'The :attribute must be a valid IP address.',
    'ipv4' => 'The :attribute must be a valid IPv4 address.',
    'ipv6' => 'The :attribute must be a valid IPv6 address.',
    'json' => 'The :attribute must be a valid JSON string.',
    'lt' => [
        'numeric' => 'The :attribute must be less than :value.',
        'file' => 'The :attribute must be less than :value kilobytes.',
        'string' => 'The :attribute must be less than :value characters.',
        'array' => 'The :attribute must have less than :value items.',
    ],
    'lte' => [
        'numeric' => 'The :attribute must be less than or equal :value.',
        'file' => 'The :attribute must be less than or equal :value kilobytes.',
        'string' => 'The :attribute must be less than or equal :value characters.',
        'array' => 'The :attribute must not have more than :value items.',
    ],
    'max' => [
        'numeric' => 'The :attribute may not be greater than :max.',
        'file' => 'The :attribute may not be greater than :max kilobytes.',
        'string' => 'The :attribute may not be greater than :max characters.',
        'array' => 'The :attribute may not have more than :max items.',
    ],
    'mimes' => 'The :attribute must be a file of type: :values.',
    'mimetypes' => 'The :attribute must be a file of type: :values.',
    'min' => [
        'numeric' => 'The :attribute must be at least :min.',
        'file' => 'The :attribute must be at least :min kilobytes.',
        'string' => 'The :attribute must be at least :min characters.',
        'array' => 'The :attribute must have at least :min items.',
    ],
    'not_in' => 'The selected :attribute is invalid.',
    'not_regex' => 'The :attribute format is invalid.',
    'numeric' => 'The :attribute must be a number.',
    'present' => 'The :attribute field must be present.',
    'regex' => 'The :attribute format is invalid.',
    'required' => 'The :attribute field is required.',
    'required_if' => 'The :attribute field is required when :other is :value.',
    'required_unless' => 'The :attribute field is required unless :other is in :values.',
    'required_with' => 'The :attribute field is required when :values is present.',
    'required_with_all' => 'The :attribute field is required when :values are present.',
    'required_without' => 'The :attribute field is required when :values is not present.',
    'required_without_all' => 'The :attribute field is required when none of :values are present.',
    'same' => 'The :attribute and :other must match.',
    'size' => [
        'numeric' => 'The :attribute must be :size.',
        'file' => 'The :attribute must be :size kilobytes.',
        'string' => 'The :attribute must be :size characters.',
        'array' => 'The :attribute must contain :size items.',
    ],
    'string' => 'The :attribute must be a string.',
    'timezone' => 'The :attribute must be a valid zone.',
    'unique' => 'The :attribute has already been taken.',
    'uploaded' => 'The :attribute failed to upload.',
    'url' => 'The :attribute format is invalid.',
    'uuid' => 'The :attribute must be a valid UUID.',

    /*
    |--------------------------------------------------------------------------
    | Custom Validation Language Lines
    |--------------------------------------------------------------------------
    |
    | Here you may specify custom validation messages for attributes using the
    | convention "attribute.rule" to name the lines. This makes it quick to
    | specify a specific custom language line for a given attribute rule.
    |
    */

    'custom' => [
        'attribute-name' => [
            'rule-name' => 'custom-message',
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Custom Validation Attributes
    |--------------------------------------------------------------------------
    |
    | The following language lines are used to swap our attribute placeholder
    | with something more reader friendly such as "E-Mail Address" instead
    | of "email". This simply helps us make our message more expressive.
    |
    */

    'attributes' => [],

];

Wrap Up

So in this chapter a lot of important things were covered for building a RESTful service. You learned how to work with data and how to extract it from a request, how to build appropriate responses, how to structure them and send them back, and of course, why validation is very important and how to implement validation in a Laravel application. With that, a lot has been achieved until now, and with all that it's really about time to add database operations in the next chapter, to make sure that you can really build powerful applications, and not only output the data sent to you, but also store it in the database and work with it.

Clone this wiki locally