0% found this document useful (0 votes)
19 views

Lesson 1 - Laravel API CRUD Best Practice

Belakar

Uploaded by

Antonius Ajalah
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
19 views

Lesson 1 - Laravel API CRUD Best Practice

Belakar

Uploaded by

Antonius Ajalah
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

 English 

LARAVEL TUTORIAL

Lesson 1: Laravel API CRUD best practice


POSTED ON 16 MARCH, 2021 BY FREELANCERVIET.NET

In the tutorial we use laravel as API only with features (notice that the Laravel version is 8.*):

Register, login, logout user

Authentication by Sanctum package

CRUD api example

Handle exception

Bind middleware for api request

Handle upload file

Fetch output response by API Resource

Relation between Model (Video belong to User)

Using database transaction

For authentication use sanctum package

1 composer require laravel/sanctum

Next, you should publish the Sanctum configuration and migration files using the vendor:publish Artisan command. The sanctum
configuration file will be placed in your application’s config directory:

1 php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

Change config to default is api at /config/auth.php

1 'defaults' => [
2 'guard' => 'api',
3 'passwords' => 'users',
4 ],

For api prefix should be version so add each version in one file. For example v1 is routes/api.php and v2 is routes/apiv2.php
Register api prefix in /app/Providers/RouteServiceProvider.php

public function boot()


{
$this->configureRateLimiting();

$this->routes(function () {
Route::prefix('v1')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));

Route::prefix('v2')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/apiv2.php'));

Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
});
}
Route for v1 /routes/api.php

Use Route::apiResource instead of default Route::resource to exclude routes that present HTML templates such as English
create 
and edit
Use middleware auth:sanctum for authentication

1 Route::post('/login', 'App\Http\Controllers\API\WordpressAuthController@login');
2 Route::post('/register', 'App\Http\Controllers\API\WordpressAuthController@register');
3 Route::middleware(['auth:sanctum'])->group(function(){
4 Route::get('/user', 'App\Http\Controllers\API\WordpressAuthController@currentUser');
5 Route::post('/logout', 'App\Http\Controllers\API\WordpressAuthController@logout');
6 Route::apiResource('/video', 'App\Http\Controllers\API');
7 });

The routes will like below

VERB URI ACTION ROUTE NAME

POST /v1/register register

POST /v1/login login

GET /v1/user currentUser

POST /v1/logout logout

GET /v1/videos index videos.index

POST /v1/videos store videos.store

GET /v1/videos/{video} show videos.show

PUT/PATCH /v1/videos/{video} update videos.update

DELETE /v1/videos/{video} destroy videos.destroy

Middleware
Generate middleware to handle api request by command (optional)

1 php artisan make:middleware Api

Change handle function of middleware /app/Middleware/Api.php

1 <?php
2
3 namespace App\Http\Middleware;
4
5 use Closure;
6 use Illuminate\Auth\Access\Gate;
7 use Illuminate\Http\Request;
8 use Illuminate\Support\Facades\Auth;
9 use Illuminate\Support\Facades\Response;
10
11 class Api
12 {
13 /**
14 * Handle an incoming request.
15 *
16 * @param \Illuminate\Http\Request $request
17 * @param \Closure $next
18 * @return mixed
19 */
20 public function handle($request, Closure $next, $role = "view")
21 {
22 $request->headers->set('Accept', 'application/json');
23 return $next($request);
24 }
25 }

Add the middleware to all routes in $middlewareGroup inside /app/Http/Kernel.php


1 /**
 23 * The application's route middleware groups.
*
English 

4 * @var array
5 */
6 protected $middlewareGroups = [
7 'web' => [
8 \App\Http\Middleware\EncryptCookies::class,
9 \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
10 \Illuminate\Session\Middleware\StartSession::class,
11 \Laravel\Jetstream\Http\Middleware\AuthenticateSession::class,
12 \Illuminate\View\Middleware\ShareErrorsFromSession::class,
13 \App\Http\Middleware\VerifyCsrfToken::class,
14 \Illuminate\Routing\Middleware\SubstituteBindings::class,
15 \App\Http\Middleware\HandleInertiaRequests::class,
16 ],
17
18 'api' => [
19 // 'throttle:api',
20 App\Http\Middleware\Api::class,
21 \Illuminate\Routing\Middleware\SubstituteBindings::class,
22 ],
23 ];

Controller and Model


Generate auth controller by command

1 php artisan make:controller /API/AuthController

Controller Api Auth


1 <?php
2 English 
3 namespace App\Http\Controllers\API;
4
5 use App\Http\Controllers\Controller;
6 use Illuminate\Http\Request;
7 use App\Models\User;
8 use Carbon\Carbon;
9 use Illuminate\Support\Facades\Hash;
0
1 class AuthController extends Controller
2 {
3 public function currentUser(Request $request){
4 $user = $request->user();
5 return $user;
6 }
7
8 public function register(Request $request)
9 {
0 $validatedData = $request->validate([
1 'email' => 'email|required|unique:wp_users',
2 'password' => 'required',
3 ]);
4
5 $validatedData['password'] = Hash::make($validatedData['user_password']);
6 $user = User::create($validatedData);
7
8 $accessToken = $user->createToken('authToken')->plainTextToken;
9
0 return response([ 'user' => $user, 'access_token' => $accessToken]);
1 }
2
3 public function login(Request $request)
4 {
5 $loginData = $request->validate([
6 'email' => 'email|required',
7 'password' => 'required'
8 ]);
9 $user = User::where('email', $request->email)->first();
0
1 if (! $user || ! Hash::check($request->password, $user->password)) {
2 return response(['message' => 'Invalid password' ],400);
3 }
4 $accessToken = $user->createToken('authToken')->plainTextToken;
5
6 return response(['user' => $user, 'access_token' => $accessToken]);
7
8 }
9
0 public function logout(Request $request){
1 $request->user()->currentAccessToken()->delete();
2 return response(['message' => 'Logout Success']);
3 }
4 }

To Add meta for User , we create new table user_meta


Run command create user_meta model and migration

php artisan make:model UserMeta


php artisan make:migration create_user_meta

Modify /app/Models/UserMeta.php
<?php
2 English 
3 namespace App\Models;
4
5 use Illuminate\Database\Eloquent\Factories\HasFactory;
6 use Illuminate\Database\Eloquent\Model;
7 use App\Models\User;
8
9 class UserMeta extends Model
0 {
use HasFactory;
2 protected $table = 'user_meta';
3 public $timestamps = false;
4
5 protected $fillable = [
6 'user_id','photo_path','is_verified'
7 ];
8
9 public function user()
0 {
return $this->belongsTo(User::class,'user_id','id');
2 }
3 }

migration user_meta table

1 public function up()


2 {
3 Schema::create('user_meta', function (Blueprint $table) {
4 $table->id('user_id');
5 $table->string('photo_path',500);
6 $table->string('photo_verify',500);
7 $table->boolean('is_verified');
8 });
9 }
10
11 /**
12 * Reverse the migrations.
13 *
14 * @return void
15 */
16 public function down()
17 {
18 Schema::dropIfExists('api_usermeta');
19 }

Now to make sure user table is created with meta in same time we use transaction . Modify
/app/Http/Controllers/AuthController.php function register

1 use Illuminate\Support\Facades\DB;
2 use App\Models\UserMeta;
3 public function register(Request $request)
4 {
5 $validatedData = $request->validate([
6 'email' => 'email|required|unique:wp_users',
7 'password' => 'required',
8 ]);
9
10 $validatedData['password'] = Hash::make($validatedData['user_password']);
11 $user = DB::transaction(function () use ($validatedData) {
12 $user = User::create($validatedData);
13
14 UserMeta::create([
15 'user_id' => $user->ID,
16 'is_verified' => false,
17 'photo_path' => ''
18 ]);
19 return $user;
20 }, 5);
21 //5 is time transaction rerun if exception happen
22
23 $accessToken = $user->createToken('authToken')->plainTextToken;
24
25 return response([ 'user' => $user, 'access_token' => $accessToken]);
26 }

Before generate VideoController we need to know that we need to response path of video as url that can be show on browser so we need
to fetch path as url for each video. This case we need API Resource
Generate VideoResource
1 php artisan make:resource VideoResource
 English 
Change /app/Http/VideoResource.php

1 <?php
2
3 namespace App\Http\Resources;
4
5 use Illuminate\Http\Resources\Json\JsonResource;
6 use Illuminate\Support\Facades\Storage;
7
8 class VideoResource extends JsonResource
9 {
10 /**
11 * Transform the resource into an array.
12 *
13 * @param \Illuminate\Http\Request $request
14 * @return array
15 */
16 public function toArray($request)
17 {
18 return [
19 'id' => $this->id,
20 'user' => $this->user,
21 'title' => $this->title,
22 'description' => $this->description,
23 'path' => Storage::url($this->path)
24 ];
25 }
26 }

Generate video controller and model by command

1 php artisan make:controller /API/VideoController --resource --api --model=Video

Model /app/Models/Video.php

1 <?php
2
3 namespace App\Models;
4
5 use Illuminate\Database\Eloquent\Factories\HasFactory;
6 use Illuminate\Database\Eloquent\Model;
7
8 class Video extends Model
9 {
10 use HasFactory;
11 protected $table = 'api_videos';
12 public $timestamps = true;
13
14 protected $casts = [
15 'cost' => 'float'
16 ];
17
18 protected $fillable = [
19 'title',
20 'description',
21 'path'
22 ];
23
24 public function user(){
25 return $this->belongsTo(User::class,'user_id','ID');
26 }
27 }

Run command to generate migration table

1 php artisan make:migration add_video --create=api_videos

After that change migration file


1 <?php
 23 use Illuminate\Database\Migrations\Migration;
English 

4 use Illuminate\Database\Schema\Blueprint;
5 use Illuminate\Support\Facades\Schema;
6
7 class AddVideo extends Migration
8 {
9 /**
10 * Run the migrations.
11 *
12 * @return void
13 */
14 public function up()
15 {
16 Schema::create('api_videos', function (Blueprint $table) {
17 $table->id();
18 $table->string('title',255);
19 $table->string('description',2000);
20 $table->string('path',500);
21 $table->timestamps();
22 });
23 }
24
25 /**
26 * Reverse the migrations.
27 *
28 * @return void
29 */
30 public function down()
31 {
32 Schema::dropIfExists('api_videos');
33 }
34 }

resource: create controller with full function for route:resource


api: exclude function that generate html view
model: generate model and inject to the controller

Controller app/http/Controller/API/VideoController.php
1 <?php
 23 namespace App\Http\Controllers\API;
English 

4
5 use App\Http\Controllers\Controller;
6 use App\Http\Resources\VideoResource;
7 use App\Models\Video;
8 use Illuminate\Http\Request;
9 use Illuminate\Support\Facades\Storage;
10
11 class VideoController extends Controller
12 {
13 /**
14 * Display a listing of the resource.
15 *
16 * @return \Illuminate\Http\Response
17 */
18 public function index()
19 {
20 //
21 $videos = VideoResource::collection(Video::with('user')->latest()->paginate(5));
22
23 return response(['data'=>$videos]);
24 }
25
26 /**
27 * Store a newly created resource in storage.
28 *
29 * @param \Illuminate\Http\Request $request
30 * @return \Illuminate\Http\Response
31 */
32 public function store(Request $request)
33 {
34 $validData = $request->validate([
35 'title' => 'required',
36 'description' => 'required',
37 'path' => 'required|mimes:mp4|max:100000',
38 ]);
39 $user = $request->user();
40 $path = Storage::disk()->put($user->ID, $request->path);
41 $validData['user_id'] = $request->user()->ID;
42 $validData['path'] = $path;
43 $result = new VideoResource(Video::create($validData));
44 return response(['data'=>$result, 'message'=> 'Video is created']);
45 }
46
47
48 /**
49 * Display the specified resource.
50 *
51 * @param \App\Models\Video $video
52 * @return \Illuminate\Http\Response
53 */
54 public function show(Video $video)
55 {
56 //
57 return response(['data'=>$video]);
58 }
59
60 /**
61 * Update the specified resource in storage.
62 *
63 * @param \Illuminate\Http\Request $request
64 * @param \App\Models\Video $video
65 * @return \Illuminate\Http\Response
66 */
67 public function update(Request $request, Video $video)
68 {
69 $request->validate([
70 'title' => 'required',
71 'description' => 'required',
72 ]);
73
74 $result = $video->update($request->all());
75 return response(['data'=>$result, 'message'=> 'Video is updated']);
76 }
77
78 /**
79 * Remove the specified resource from storage.
80 *
81 * @param \App\Models\Video $video
82 * @return \Illuminate\Http\Response
83 */
84 public function destroy(Request $request, Video $video)
85 {
86 if($video->user_id != $request->user()->id){
87 return response()->json(['message' => 'You can only delete your own video.'], 403);
88 }
English 
89 Storage::delete($video->path);
90 $video->delete();
91
92 return response(['message'=> 'Video is deleted']);
93 }
94 }
More custom for handle error and response as json
Response when call GET /v1/video/

1 {
2 "data": [
3 {
4 "id": 12,
5 "user": {
6 "id": 5,
7 "email": "[email protected]",
8 "user_status": 0,
9 "display_name": "Vuong anh duong"
10 },
11 "title": "test title",
12 "description": "test description",
13 "path": "http://localhost:8084/store/5/hnDd4kTE8qICp0RiZQ6pe5tEC1AJX8pKojrDq3GF.mp4"
14 },
15 {
16 "id": 11,
17 "user": {
18 "id": 5,
19 "email": "[email protected]",
20 "user_status": 0,
21 "display_name": "Vuong anh duong"
22 },
23 "title": "test title",
24 "description": "test description",
25 "path": "http://localhost:8084/store/5/4qD7Hb9C35vHDToQXkj6FvW5WDa4aqTn321FtWKZ.mp4"
26 }
27
28 ]
29 }

It is optional because MiddleApi have passed Accept: application/json to all request then laravel will response as json
Add Trait for Rest api \app\Exceptions\Traits\RestExceptionHandlerTrait.php
1 <?php
 23 namespace App\Exceptions\Traits;
English 

4
5 use Exception;
6 use Illuminate\Http\Request;
7 use Illuminate\Database\Eloquent\ModelNotFoundException;
8 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
9
10 trait RestExceptionHandlerTrait
11 {
12
13 /**
14 * Creates a new JSON response based on exception type.
15 *
16 * @param Request $request
17 * @param Exception $e
18 * @return \Illuminate\Http\JsonResponse
19 */
20 protected function getJsonResponseForException(Request $request, Exception $e)
21 {
22 if($this->isModelNotFoundException($e))
23 return $this->modelNotFound('Item not found');
24 if($this->isNotFoundException($e))
25 return $this->modelNotFound('Not found');
26
27 return parent::render($request, $e);
28 }
29
30 /**
31 * Returns json response for generic bad request.
32 *
33 * @param string $message
34 * @param int $statusCode
35 * @return \Illuminate\Http\JsonResponse
36 */
37 protected function badRequest($message = 'Bad request', $statusCode = 400)
38 {
39 return $this->jsonResponse(['error' => $message], $statusCode);
40 }
41
42 /**
43 * Returns json response for Eloquent model not found exception.
44 *
45 * @param string $message
46 * @param int $statusCode
47 * @return \Illuminate\Http\JsonResponse
48 */
49 protected function modelNotFound($message = 'Record not found', $statusCode = 404)
50 {
51 return $this->jsonResponse(['error' => $message], $statusCode);
52 }
53
54 /**
55 * Returns json response.
56 *
57 * @param array|null $payload
58 * @param int $statusCode
59 * @return \Illuminate\Http\JsonResponse
60 */
61 protected function jsonResponse(array $payload = null, $statusCode = 404)
62 {
63 $payload = $payload ?: [];
64
65 return response()->json($payload, $statusCode);
66 }
67
68 /**
69 * Determines if the given exception is an Eloquent model not found.
70 *
71 * @param Exception $e
72 * @return bool
73 */
74 protected function isModelNotFoundException(Exception $e)
75 {
76 return $e instanceof ModelNotFoundException;
77 }
78
79 protected function isNotFoundException(Exception $e){
80 return $e instanceof NotFoundHttpException;
81 }
82
83 protected function isApiCall(Request $request)
84 {
85 return strpos($request->getUri(), '/v1/') !== false;
86 }
87 }
 English 
Change /app/Exception/Handler.php

1 <?php
2
3 namespace App\Exceptions;
4
5 use App\Exceptions\Traits\RestExceptionHandlerTrait;
6 use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
7 use Throwable;
8
9 class Handler extends ExceptionHandler
10 {
11 use RestExceptionHandlerTrait;
12 /**
13 * A list of the exception types that are not reported.
14 *
15 * @var array
16 */
17 protected $dontReport = [
18 //
19 ];
20
21 /**
22 * A list of the inputs that are never flashed for validation exceptions.
23 *
24 * @var array
25 */
26 protected $dontFlash = [
27 'password',
28 'password_confirmation',
29 ];
30
31 /**
32 * Register the exception handling callbacks for the application.
33 *
34 * @return void
35 */
36 public function register()
37 {
38 $this->reportable(function (Throwable $e) {
39 //
40 });
41 }
42
43 public function render($request, Throwable $e)
44 {
45 if(!$this->isApiCall($request)) {
46 $retval = parent::render($request, $e);
47 } else {
48 $retval = $this->getJsonResponseForException($request, $e);
49 }
50
51 return $retval;
52 }
53 }

   

 China order management system Install Let’s Encrypt SSL on Linux with auto renew

Leave a Reply
Your email address will not be published. Required fields are marked *
Comment
Name *

 English 

Email *

Website

POST COMMENT

>> Lesson 1: Laravel API CRUD best practice

Lesson 2: Using Enum on laravel

Lesson 3: Simple CRUD using Vue JS

Lesson 4: Laravel api auth with wordpress

ABOUT
Contact
FAQ
About us
Security policies

CONTACT INFO
Email: [email protected]
Skype: vuonganhduong812
Phone: +84 3522 98233
Zalo: 03522 98233
Whatsapp: +84 3522 98233

   

https://www.facebook.com/freelancerviet.net

Freelancerviet.net
Ikuti Halaman 66 pengikut

Copyright 2023 © Freelancerviet.net

You might also like