BurgerPedia: A Complete Laravel 5 and AngularJS Tutorial with Bootstrap to Make it Pretty – Part 7

in AJAX/HTML/CSS/jQuery/Javascript/Laravel/PHP/Tutorials & Samples/Web Development

Part 7 – Basic Validations

Now that we have the basic CRUD operators setup, it’s time to add some business logic through model validation. This is usually done at the validation level of a model. In the case of Burgerpedia, here are the validation rules that we will be adding:

  • Every hamburger must have a unique name
  • Every hamburger name cannot be longer than 255 characters
  • Every hamburger must also have an author and overview
  • Every hamburger description must actually have a description (i.e. no empty descriptions), an author and a title.
  • Every hamburger description must also have a title and author

So let’s start with the hamburger validation by going to the hamburger controller. Inside the create() method, we can call the validate() method which takes 2 parameters, the first is the request object and the second is an array of rules. So in the case of the hamburger, we want the name to be required and that it must be unique. We also want the user to provide an author name and an overview of the hamburger. The store() method for the hamburgers controller now looks like so:

public function store(Request $request){
	
	// validate our input burger
	$this->validate($request, [	'name' => 'required|unique:hamburgers|max:255' ]);
	$this->validate($request, [	'author' => 'required' ]);
	$this->validate($request, [	'overview' => 'required' ]);

	
	// we have a hamburger with a valid name
	$hamburger = Hamburger::create([
		'name' => $request->input('name'),
		'author' => $request->input('author'),
		'overview' => $request->input('overview')
	]);
		
	return $hamburger;
}

As you can see, we are passing in the ‘required’ validation constraint, the ‘unique’ constraint and the ‘max’ constraint to the name validator. The ‘unique’ constraint requires us to pass the table that we need to check the uniqueness constraint on. So, I have passed the hamburgers table as a parameter to the ‘unique’ constraint. Finally the ‘max’ constraint needs to know the maximum number of characters so I have passed 255. The validation for the author and overview fields are just that they cannot be empty so i have simply passed the ‘required’ constraint. Here is an example of me entering the same hamburger twice: “The EvilBurger”. It passes the first time but fails when we try to enter it again.

hamburger-store

when we try to enter the same hamburger again, we will now get the duplicate entry error seen below:

hamburger-duplicate

We will also get a validation fail if we do not provide required information such as the author for the hamburger. Here is a screenshot where I forget to enter the author of the Evilburber:

hamburger-validation-fail

Now, we will do the same for the hamburger description by requiring that it’s description, title or author cannot be empty. Here is what the store() method now looks like:

public function store(Request $request, $hamburgerId){
	
	// validate our input description
	$this->validate($request, [	'description' => 'required' , 'author' => 'required', 'title' => 'required']);
	
	$hamburger = Hamburger::findOrFail($hamburgerId);
	$hamburger->descriptions()->save(new Description([
		'title' => $request->input('title'),
		'description' => $request->input('description'),
		'author' => $request->input('author')
	]));
		
	return $hamburger->descriptions;
}

Although we have covered the validation for our store() methods, we want to make sure that it also carries over to our update() methods. So the update method of the hamburger and hamburger description controllers must also be updated. First let’s look at the update method for the hamburger:

public function update(Request $request, $id ){
	
	$hamburger = Hamburger::findOrFail($id);
	
	if($request->input('author') == $hamburger['author']){
		// validate our input burger
		$this->validate($request, [	'name' => 'required|unique:hamburgers|max:255' ]);
		$this->validate($request, [	'overview' => 'required' ]);
	
		$hamburger->update([
			'name' => $request->input('name'),
			'overview' => $request->input('overview'),
			'author' => $request->input('author')
		]);
		return $hamburger;
	}else{
		// Unprocessable entity
		return response()->json(['name' => 'Failure! You are not the author of this hamburger'], 422);

	}
}

what the above code does is try to find the hamburger with the provided id. If the hamburger does not exist, we send a failure message. If the hamburger exists, we then verify the author field to make sure that only the original author of the hamburger can update its name or overview. If this fails, then we throw an error and send response code 422. As a side note, The 422 (Unprocessable Entity) status code means the server understands the content type of the request entity, and the syntax of the request entity is correct, but was unable to process the contained instructions. If the author check passes, then we validate the name of the hamburger and the hamburger overview. If the validation passes, then we update the hamburger. Here is a screenshot of what happens when we attempt to update a hamburger when we are not its owner:

hamburger-upate-not-owner

Here is a screenshot of what happens when we attempt to update a hamburger and we are the owner:

hamburger-upate-owner

The same type of validation must be done for hamburger descriptions. The final code for the udpate() method for a hamburger description is provided below:

public function update(Request $request, $hamburgerId, $descriptionId){
	
	$description = Description::ofHamburger($hamburgerId)->findOrFail($descriptionId);
	
	if($request->input('author') == $description['author']){
		$description->update([
			'title' => $request->input('title'),
			'description' => $request->input('description'),
			'author' => $request->input('author')
		]);
		return $description;
	}else{
		// Unprocessable entity
		return response()->json(['name' => 'Failure! You are not the author of this description'], 422);

	}
}

Notice the only difference is that we have an extra parameter for the hamburger description: the Hamburger id. What we are doing is using the findOrFail function to find a description with the provided ID but this description must belong to the hamburger with the provided id. And with that we now have proper validation for our models.

Previous Step: Creating the Controllers
Next Step: Integrating AngularJS and Bootstrap

Tutorial Contents
Tutorial Resources

Mifty Yusuf is a Montreal-based software developer who enjoys playing with new web technologies as well as comic books and illustrations. He beleives that, no matter what the question is, the answer is always Batman!

5 Comments

  1. Hi,

    Thanks for these tutorials, it’s a good start to learn laravel!

    I have 2 questions about this part of the tutorial

    When we are updating an hamburger, we validate that the name must be unique, but as the name already exists for the hamburger i got the “The name has already been taken.” error.

    How to deal with that in Laravel ?

    Also when i create an hamburger using postman, values as passed as form-data. This doesn’t work for update. I have to pass data as x-www-form-urlencoded instead. Is there a way to fix that on laravel side ?

    Thanks!

    Alain

    • Hi Alain,
      The update method should not cause “the name is already been taken error”. The only way this is happening is because you already have the same name twice in your DB. (Most likely, you entered it before you added the validation rule. And now it is failing once the validation checks the DB). I just reverified the code and it is behaving pretty much as expected for the update.
      You are right about the Postman issue. on my head, I have added in the header that this is an AJAX request by passing the key “X-Requested-With” with value “XMLHttpRequest” inside the header of my request.

      • Hi,

        in my code it doesn’t works and it seems normal. The Request object does not contain an id and how can the validator guess that we are currently updating the hamburger #5 (for example) ?

        Following the doc on https://laravel.com/docs/5.3/validation#rule-unique I’ve updated the validator to $this->validate($request, [‘name’ => ‘required|max:255|unique:hamburgers,name,’ . $id]); and now it’s working 🙂

        Alain

        • Hi Alain,
          The original code should be working right out of the box. The only way it will fail is if you already have a hamburger with the name already in your database. In other words, you had a duplicate hamburger before adding the validation rules. I’ve just tested with Postman in various tests and it is behaving as expected. The only time you should be getting the error of “The name has already been taken.” is when there is already another hamburger in your DB with the same name. Meaning that the validation rule actually did its job 🙂

          1) If you use the follwoing command:

          php artisan route:list

          you will see that the put method for a hamburger is PUT api/hamburgers/{hamburger} So we do have the ID for the hamburger in our request already.
          2) what you are doing when you pass the ID field in your unique validator is forcing the validation rule to ignore a specific . You can read about it at http://stackoverflow.com/a/22406205 So, of course this works, but if your database already had another hamburger with the same name, your validator would not catch it since you are Forcing the Unique Rule To Ignore the given ID.

          To be sure, I would recommend that you clean your database, create one hamburger and try to update that hamburger. The code should work out of the box. Please let me know if this works. I’m really curious 😉

          • Hi,

            In fact I’m not making an hamburger api but a beer api, good way to have a good meal 🙂 But Yes I’ve checked and the name is unique.

            To be sure I’ve:
            * downloaded your zip file
            * installed the project by running make:auth and migrate artisan command
            * created a user
            * created a burger
            * edited the burger

            But I still have the error.

            1) I know we have the id via the route but as we don’t give it to the validator, how does he know it ?

            2) As I understand we ignore this only means that the validation won’t fail if we don’t update the name, if the name exists for an other ID, the validation will fail.

            As it’s the first time I use Laravel, I’m curious and your help is very appreciated 🙂

Leave a Reply

Your email address will not be published.

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Latest from AJAX

Go to Top