Lesson 1 - Laravel API CRUD Best Practice
Lesson 1 - Laravel API CRUD Best Practice
LARAVEL TUTORIAL
In the tutorial we use laravel as API only with features (notice that the Laravel version is 8.*):
Handle exception
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 '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
$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 });
Middleware
Generate middleware to handle api request by command (optional)
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 }
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 ];
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 }
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 }
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 }
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 }
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
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