In this basic tutorial, we will create an API to create read update and delete (CRUD) a post using Laravel Transformers.
Create Posts Table and User Controller
Create Posts Table
Create posts table through migration :
1 |
php artisan make:migration create_posts_table --create=posts |
Open the newly migration file and add some attributes :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreatePostsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->integer('user_id')->unsigned(); $table->string('content'); $table->timestamps(); $table->foreign('user_id')->references('id')->on('users'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('posts'); } } |
Run the migration :
1 |
php artisan migrate |
Create UserController
Create a Controller for User model :
1 |
php artisan make:controller UserController |
Create Users Method in UserController
Next step is to create users() method inside userController.
1 2 3 4 5 |
public function users(User $user){ $users = $user->all(); return response()->json($users); } |
Don’t forget to add the User model :
1 |
use App\User; |
Create API GET users Route
Create API Get Route
We now create an api route to get all users. Open routes/api.php and add the following line :
1 |
Route::get('/users','UserController@users'); |
Try the GET method from Postman
Open Postman and type the route on the url :
We get empty array as a response as we don’t have any users as of now.
Installing Fractal
Next we will install a library to transform data before using it in an API. The name of the library is fractal.
1 |
composer require spatie/laravel-fractal |
1 |
php artisan vendor:publish |
Create UserTransformer
Create UserTransformer :
1 |
php artisan make:transformer UserTransformer |
Inside UserTransformer.php create a new transform method as a response for the api calling.
1 2 3 4 5 6 7 8 |
public function transform(App\User $user) { return [ 'name' => $user->name, 'email' => $user->email, 'registered_at' => $user->created_at->diffForHumans() ]; } |
Next we’ll use fractal inside the UserController :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\User; use App\Transformers\UserTransformer; class UserController extends Controller { public function users(User $user){ $users = $user->all(); return fractal() ->collection($users) ->transformWith(new UserTransformer) ->toArray(); } } |
Try inside Postman :
Create User Registration POST /user Route
Add api_token attribute inside users table
api_token column will be added to our users table. Create the migration :
1 |
php artisan make:migration alter_users_add_api_token --table=users |
1 2 3 4 5 6 |
public function up() { Schema::table('users', function (Blueprint $table) { $table->string('api_token')->after('email'); }); } |
And run the migration :
1 |
php artisan migrate |
Next, modify user Model to add the api_token to fillable properties:
1 2 3 |
protected $fillable = [ 'name', 'email', 'password','api_token', ]; |
Create AuthController
AuthController is needed for us to register a user. Create one through artisan command:
1 |
php artisan make:controller AuthController |
Then create the register user method with the validation and fractal response :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\User; class AuthController extends Controller { public function register(Request $request, User $user){ $this->validate($request, [ 'name' => 'required', 'email' => 'required|email|unique:users', 'password' => 'required|min:6' ]); $user = $user->create([ 'name' => $request->name, 'email' => $request->email, 'password' => bcrypt($request->password), 'api_token' => bcrypt($request->email) ]); $response = fractal() ->item($user) ->transformWith(new UserTransformer) ->toArray(); return response()->json($response, 201); } } |
Create API Route
Create the route inside routes/api.php
1 |
Route::post('/auth/register','AuthController@register'); |
Open the POST Route inside Postman
Try to register a user :
Create User Login POST /user Route
We have made the register route. Subsequently, we will create the login route using the same POST method.
Add Login Method to AuthController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
use Auth; public function login(Request $request, User $user){ if(!Auth::attempt(['email'=>$request->email, 'password'=>$request->password])){ return response()->json(['error'=>'Wrong Credentials'], 401); } $user = $user->find(Auth::user()->id); return fractal() ->item($user) ->transformWith(new UserTransformer) ->toArray(); } |
Add Login Route to routes\api.php
1 |
Route::post('/auth/login','AuthController@login'); |
Add Meta API_token
Before we run the route to postman, let’s add a meta for the api_token inside authController :
1 2 3 4 5 6 7 |
return fractal() ->item($user) ->transformWith(new UserTransformer) ->addMeta([ 'token'=>$user->api_token ]) ->toArray(); |
Run the route login in Postman
Adding Middleware Token
Now we will create a middleware in order to prevent someone who doesn’t have an account in our application able to access the user information.
Add user/profile route inside routes/api with middleware
1 |
Route::get('/users/profile','UserController@profile')->middleware('auth:api'); |
Create profile method inside UserController
1 2 3 4 5 6 7 8 |
public function profile(User $user){ $user = $user->find(Auth::user()->id); return fractal() ->item($user) ->transformWith(new UserTransformer) ->toArray(); } |
Try in Postman
As we can see, if we don’t provide the api_token, the json response will be unauthenticated and we will not be able to see the users’ profile.
To see our profile, run the login method, copy the api_token meta and put it as body inside postman :
You also can put it inside the Authorization header :
Create Articles Table
Next up, we will create an article table. Each user is able to create more than one article.
Create Article Model and Migration
1 |
php artisan make:model Article -m |
1 2 3 4 5 6 7 8 9 10 |
public function up() { Schema::create('articles', function (Blueprint $table) { $table->increments('id'); $table->integer('user_id')->unsigned(); $table->foreign('user_id')->references('id')->on('users')->onDelete('restrict'); $table->text('content')->nullable(); $table->timestamps(); }); } |
Then run the migration : php artisan migrate
Article.php
1 2 3 4 5 6 7 8 9 10 |
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Article extends Model { protected $fillable = ['user_id','content']; } |
Create ArticleController.php
1 |
php artisan make:controller ArticleController |
Create ArticleTransformer.php
1 |
php artisan make:transformer ArticleTransformer |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php namespace App\Transformers; use League\Fractal\TransformerAbstract; use App\Article; class ArticleTransformer extends TransformerAbstract { /** * A Fractal transformer. * * @return array */ public function transform(Article $article) { return [ 'id' => $article->id, 'content' => $article->content, 'published' => $article->created_at->diffForHumans() ]; } } |
Create Add Article Method
Inside Article Controller, add a method to add an article :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public function add(Request $request, Article $article){ $this->validate($request,[ 'content' => 'required|min:5' ]); $article->create([ 'user_id' => Auth::user()->id, 'content' => $request->content ]); $response = fractal() ->item($article) ->transformWith(new ArticleTransformer) ->toArray(); return response()->json($response, 201); } |
Add the api route
1 |
Route::post('/articles','ArticleController@add')->middleware('auth:api'); |
Try in Postman
Users and Articles Relation
As I previously mentioned, each user is able to create more than one article. Therefore, we need to create the relation inside User and Article model :
User.php
1 2 3 |
public function articles(){ return $this->hasMany(Article::class); } |
Article.php
1 2 3 |
public function user(){ return $this->belongsTo(User::class); } |
Modify UserTransformer to add the includeArticle property :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php namespace App\Transformers; use League\Fractal\TransformerAbstract; use App\User; use App\Transformers\ArticleTransformer; class UserTransformer extends TransformerAbstract { protected $availableIncludes = [ 'articles' ]; /** * A Fractal transformer. * * @return array */ public function transform(User $user) { return [ 'id' => $user->id, 'name' => $user->name, 'email' => $user->email, 'registered_at' => $user->created_at->diffForHumans() ]; } public function includeArticles(User $user){ $articles = $user->articles; return $this->collection($articles, new ArticleTransformer); } } |
Then add the includeArticles inside UserController :
1 2 3 4 5 6 7 8 9 |
public function profile(User $user){ $user = $user->find(Auth::user()->id); return fractal() ->item($user) ->transformWith(new UserTransformer) ->includeArticles() ->toArray(); } |
Try in Postman
Order By Latest Article
Next up, we will order the articles by the latest published.
Modify Article.php model to add the latestFirst method :
1 2 3 |
public function scopeLatestFirst($query){ return $query->orderBy('id','DESC'); } |
Modify the UserTransformer :
1 2 3 4 5 |
public function includeArticles(User $user){ $articles = $user->articles()->latestFirst()->get(); return $this->collection($articles, new ArticleTransformer); } |
Try in Postman
Get User Profile By ID
Get the user profile by ID, let’s create the method inside UserController :
1 2 3 4 5 6 7 8 9 |
public function profileById(User $user, $id){ $user = $user->find($id); return fractal() ->item($user) ->transformWith(new UserTransformer) ->includeArticles() ->toArray(); } |
Create the route :
1 |
Route::get('users/{id}','UserController@profileById')->middleware('auth:api'); |
Try in Postman
Updating an Article
Next up we will create the route to update an Article using PUT. Create the route inside routes/api
1 |
Route::put('/article/{article}','ArticleController@update')->middleware('auth:api'); |
Create an update method inside ArticleController
1 2 3 4 5 6 7 8 9 10 11 |
public function update(Request $request, Article $article){ $article->content = $request->get('content',$article->content); $article->save(); $response = fractal() ->item($article) ->transformWith(new ArticleTransformer) ->toArray(); return response()->json($response, 200); } |
Try in POSTMAN
Article Update Policy
The previous method we create still have flaws. One of them is any person who has a user account inside our application still able to update another user’s artile. To prevent this, we need to create a policy. Only the author is able to change their own article.
Create Policy
1 |
php artisan make:policy ArticlePolicy |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php namespace App\Policies; use App\User; use Illuminate\Auth\Access\HandlesAuthorization; use App\Article; class ArticlePolicy { use HandlesAuthorization; /** * Create a new policy instance. * * @return void */ public function update(User $user, Article $article){ return $user->ownsArticle($article); } } |
Modify User.php model to add ownsArticle method :
1 2 3 |
public function ownsArticle(Article $article){ return auth()->id() == $article->user->id; } |
Modify ArticleController to add the authorization
1 2 3 4 5 6 7 8 9 10 11 12 |
public function update(Request $request, Article $article){ $this->authorize('update',$article); $article->content = $request->get('content',$article->content); $article->save(); $response = fractal() ->item($article) ->transformWith(new ArticleTransformer) ->toArray(); return response()->json($response, 200); } |
Add the policy to app/Providers/AuthServiceProvider.php
1 2 3 4 |
protected $policies = [ 'App\Model' => 'App\Policies\ModelPolicy', 'App\Article' => 'App\Policies\ArticlePolicy', ]; |
DELETE an Article Route
To delete an article is pretty straightforward. First, you need to create the DELETE route :
1 |
Route::delete('/article/{article}','ArticleController@delete')->middleware('auth:api'); |
Then add the delete method inside ArticleController
1 2 3 4 5 6 |
public function delete(Article $article){ $this->authorize('delete',$article); $article->delete(); return response()->json(['message' => 'Article has been deleted']); } |
Add the delete policy, similar to the update policy we’ve created a moment ago.
ArticlePolicy.php
1 2 3 |
public function delete(User $user, Article $article){ return $user->ownsArticle($article); } |
Try in POSTMAN
That’s all folks.