JSON APIs with Laravel: Part 4 - Controller Methods and Data Validation
Oliver Sarfas • August 19, 2019
programming laravelThis a part of my "JSON APIs with Laravel" series. If you've missed any of the previous episodes you can find them at the bottom of this article
Recap
We've got some Models, populated by factories, which are triggered by seeders. The Models have data which is exposed in our Controller methods and available on our endpoints wired up by our Routing.
TL;DR We've got stuff, showing in some places.
So far so good.
Now let's handle the EAD in our BREAD, and get some more endpoints populated.
Edit, Add, Delete
First up, we'll do Delete. Sounds weird to start at the end, but it's by far the easiest to implement.
DELETE Endpoint
Go to the destroy
method in ContactController
and you'll find the following;
public function destroy( Contact $contact )
{
}
We need to add the business logic to this. Put simply, all we want to do is delete the contact given, so let's add that now;
public function destroy( Contact $contact )
{
$contact->delete();
}
Now what do we return to the user? All the contacts? An empty response? 404 as the resource no longer exists?
Personally, I'll return an empty response, with a HTTP 200 code. However you may find that returning your index()
function is preferred in your application - very helpful for SPAs.
We're going to return a HTTP 200 empty response here.
public function destroy( Contact $contact )
{
$contact->delete();
return response()->json([]);
}
ADD Endpoint
The ADD endpoint can be found in the store
method of the controller;
public function store(Request $request)
{
}
Right now, all it's doing is taking the request, and doing nothing with it. What we want to have (in terms of flow) is the following;
- Take request
- Validate it's contents
- Create a new resource given the validated data
- Return the newly created resource to the requester
A couple of things to consider however are;
- idempotency
- authentication
Idempotency
What do we do if someone submits the same details twice, in the space of a minute? Or the same details for a contact we already have?
Create a new one? Return the existing? Well, that's entirely up to you.
By the "rules" of REST, your POST endpoint for creating a resource should be idempotent, however you'll rarely find that this is the case in the wild.
Personally, I try to stick to this wherever possible, but sometimes the business doesn't want this, so you have to "go rogue" and build without.
Authentication
Currently our API is open, as intended. If we wanted to restrict users to certain actions, or even lock down endpoints to locations/ ip addresses, etc. we'd need some form of authentication.
In API terms, this is usually done with tokens, or username & passwords. We'll pick this up in a future episode, however for now we'll leave it open
Actual Code now..!
Ok, so our store method needs to validate our request before handling the data. Luckily Laravel makes this really easy and has some commands that will help. run the php artisan make:request CreateContactRequest
in your console, and you'll get the file app\Http\Requests\CreateContactRequest.php
file.
The full documentation of this Request concept can be found on the laravel website
<?php
namespace Http\Requests;
class CreateContactRequest extends FormRequest
{
public function authorize()
{
return false;
}
public function rules()
{
return [
];
}
}
We want this request to be authorised, and have some rules, let's populate those;
<?php
namespace Http\Requests;
class CreateContactRequest extends FormRequest
{
public function authorize()
{
return true; // public endpoint, allow all
}
public function rules()
{
return [
'name' => 'required|max:100', // Your name can be up to 100 characters in length
'email' => 'required|email|unique:contacts,email', // Email must be valid and unique to the database
'contact_number' => 'required|string', // Contact number must be supplied, and a string
'address' => 'required|string', // Address must be supplied, and a string
];
}
}
So we've got a request, how do we use it? Well, its as simple as swapping out the class in the method!
<?php
public function store ( Request $request)
{
}
# Becomes
public function store ( CreateContactRequest $request )
{
}
Don't forget to add
use Http\Requests\CreateContactRequest;
to the top of the Controller file to ensure that the class is autoloaded from the correct namespace.
Let's flesh out the functionality now.
We're going to take our validated data, and create a new Contact - given that one doesn't already exist.
<?php
public function store( CreateContactRequest $request )
{
$contact = Contact::firstOrCreate( $request->validated ()); // create or retrieve record
return response()->json($contact); // return record
}
That's it. 2 lines. It's so elegant and simple to do. Easy to modify, read, and maintain.
EDIT Endpoint
Edit runs very much the same as Add, so I won't go into as much detail - in fear of repeating myself needlessly.
The edit endpoing is update
in the controller, which can be found starting with;
<?php
public function update(Request $request, Contact $contact)
{
}
For this, create a new request (so that you can validate the data, call it UpdateContactRequest
), type hint that against the method, and then just update the resource as necessary.
You'll end out with the following;
<?php
public function update(UpdateContactRequest $request, Contact $contact)
{
$contact->update($request->validated);
return response()->json($contact);
}
Next Up
Next time, we'll look at Authentication, and ensuring that only certain users / keys can do certain actions. There are some great packages out there for this, and we'll be exploring those.