📜 ⬆️ ⬇️

REST API on Laravel in 100 lines of code

inb4: copy-paste from documentation


The guide focuses on the rapid deployment of a minimum set for a full-fledged API development in accordance with best practice, taken from the Laravel 5.7 documentation collected in one place. I wrote for myself and my colleagues as a cheat sheet, I hope it will be useful to someone else.


Presetting


We put the framework


composer create-project --prefer-dist laravel/laravel scaffold-api


Remove unnecessary UI components (vuejs, react)


php artisan preset none


Configuring the connection to the database


Go to the folder, edit the .env file:


 DB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 DB_DATABASE=api-authentification DB_USERNAME=root DB_PASSWORD= 

We start generation


Perform in console
php artisan make:model Game -mrc


We get the model, migration and controller:


 Model created successfully. Factory created successfully. Created Migration: 2019_02_27_105610_create_games_table Controller created successfully. 

Create columns in the database table


Rule migration by adding columns to the table. The most commonly used types are:



For optional fields, do not forget to add a default value using ->default()


Apply migrations by performing php artisan migrate


We generate validation rules


php artisan make:request GameRequest


Open the App/Http/Requests/GameRequest.php .
In the authorize() method, set return true until we add authorization.
The array that is returned in the rules() method describes the rules for all the columns we listed in the migration. Available rules here


To minimize the code, we use the switch construct for different http verbs, instead of making separate StoreGameRequest, UpdateGameRequest, etc.


 public function rules(Request $request) { $rules = [ 'title' => 'required|string|unique:games,title', 'description' => '', 'complexity' => 'required|min:1|max:10', 'minPlayers' => 'required|min:1|max:10', 'maxPlayers' => 'required|min:1|max:10', 'isActive' => 'required|boolean' ]; switch ($this->getMethod()) { case 'POST': return $rules; case 'PUT': return [ 'game_id' => 'required|integer|exists:games,id', // .   : unique:games,id,' . $this->route('game'), 'title' => [ 'required', Rule::unique('games')->ignore($this->title, 'title') //  ,     ] ] + $rules; //      // case 'PATCH': case 'DELETE': return [ 'game_id' => 'required|integer|exists:games,id' ]; } } 

Own options for describing errors


If you need your own error texts, override the messages () method, which returns an array with translations of each rule:


 public function messages() { return [ 'date.required' => 'A date is required', 'date.date_format' => 'A date must be in format: Ym-d', 'date.unique' => 'This date is already taken', 'date.after_or_equal' => 'A date must be after or equal today', 'date.exists' => 'This date doesn\'t exists', ]; } 

To ensure that not only the parameters passed in the request body, but also the parameters passed to the URL are available in the validation rules, we override the all method (which is usually used in the controller as $ request-> all ()):


 public function all($keys = null) { // return $this->all(); $data = parent::all($keys); switch ($this->getMethod()) { // case 'PUT': // case 'PATCH': case 'DELETE': $data['date'] = $this->route('day'); } return $data; } 

We configure the controller and describe the business logic


Open Http\Controllers\GameController . We delete the generated create(), edit() methods for rendering forms (since we have REST API, they are not needed).


Replace standard use Illuminate\Http\Request; on our use App\Http\Requests\GameRequest;


Next, we rule the methods:


 public function index() { return Game::all(); } 

 public function store(GameRequest $request) { $day = Game::create($request->validated()); return $day; } 

 public function show(Game $game) { return $game = Game::findOrFail($game); } 

 public function update(GameRequest $request, $id) { $game = Game::findOrFail($id); $game->fill($request->except(['game_id'])); $game->save(); return response()->json($game); } 

 public function destroy(GameRequest $request, $id) { $game = Game::findOrFail($id); if($game->delete()) return response(null, 204); } 

If there is a lot of logic, then it is better to put it in a separate layer Service / Repository


Customize the model


Open the app / Http / Game.php model and add properties:


 protected $fillable = ['title', 'description', 'complexity', 'minPlayers', 'maxPlayers', 'isActive']; protected $hidden = ['created_at', 'updated_at', 'deleted_at']; 

Configuring middleware


So that our application always returns json regardless of the transferred headers, create middleware:


 php artisan make:middleware ForceJsonResponse 

and add the code to it:


 public function handle($request, Closure $next) { $request->headers->set('Accept', 'application/json'); return $next($request); } 

Register this middleware in app/Http/Kernel.php :


 ... 'api' => [ 'throttle:60,1', 'bindings', \App\Http\Middleware\ForceJsonResponse::class, ], 

We configure routing


Open routes/api.php and add:


 use Http\Controllers\GameController; Route::apiResource('/games', 'GameController'); 

The static method Route :: apiResource, unlike the resource method, excludes the edit and create methods, leaving only the index, show, store, update, destroy.


The same can be achieved with a more obvious record:


 Route::resource('/games', 'GameController')->only([ 'index', 'show', 'store', 'update', 'destroy' ]); 

Now, you can look at the path with the command php artisan route:list and use.


REST API is ready!


Afterword

Afterword


If authorization is required, then the standard Laravel Passport will do.


Configuring authorization Laravel Passport


 composer require laravel/passport php artisan make:auth php artisan passport:install php artisan migrate 

Add the Laravel\Passport\HasApiTokens to the App\User model and call Passport::routesmethod to the boot app/AuthServiceProvider :


 public function boot() { $this->registerPolicies(); Passport::routes(); } 

In the config/auth.php change the driver to passport:


 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], 

Create a controller for authorization 'php artisan make: controller Api / AuthController.php`


We add there the code


 use App\User; use Illuminate\Support\Facades\Validator; 

 public function register (Request $request) { $validator = Validator::make($request->all(), [ 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:6|confirmed', ]); if ($validator->fails()) { return response(['errors'=>$validator->errors()->all()], 422); } $request['password']=Hash::make($request['password']); $user = User::create($request->toArray()); $token = $user->createToken('Laravel Password Grant Client')->accessToken; $response = ['token' => $token]; return response($response, 200); } public function login (Request $request) { $user = User::where('email', $request->email)->first(); if ($user) { if (Hash::check($request->password, $user->password)) { $token = $user->createToken('Laravel Password Grant Client')->accessToken; $response = ['token' => $token]; return response($response, 200); } else { $response = "Password missmatch"; return response($response, 422); } } else { $response = 'User does not exist'; return response($response, 422); } } public function logout (Request $request) { $token = $request->user()->token(); $token->revoke(); $response = 'You have been succesfully logged out!'; return response($response, 200); } 

After that, you can use the methods api/register, api/login, api/logout for authorization, and close access to api. To do this, you need to wrap the routing of our REST controllers in middleware:


 Route::middleware('auth:api')->group(function () { ... Route::get('/logout', 'Api\AuthController@logout')->name('logout'); }); 

Afterword

Afterword:


There would be more functional tests and documentation generation in the swagger, but this is a bit beyond the scope of the scaffold-tutorial, so about this another time



')

Source: https://habr.com/ru/post/441946/


All Articles