inb4: copier coller depuis la documentation
Le guide se concentre sur le déploiement rapide d'un ensemble minimal pour un développement d'API à part entière conformément aux meilleures pratiques, tiré de la documentation Laravel 5.7, collecté en un seul endroit. J'ai écrit pour moi et mes collègues comme une feuille de triche, j'espère que cela sera utile à quelqu'un d'autre.
Preset
Nous mettons un cadre
composer create-project --prefer-dist laravel/laravel scaffold-api
Supprimez les composants inutiles de l'interface utilisateur (vuejs, react)
php artisan preset none
Configurer la connexion à la base de données
Accédez au dossier, modifiez le fichier .env:
DB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 DB_DATABASE=api-authentification DB_USERNAME=root DB_PASSWORD=
Commencer à générer
Nous exécutons dans la console
php artisan make:model Game -mrc
Nous obtenons le modèle, la migration et le contrôleur:
Model created successfully. Factory created successfully. Created Migration: 2019_02_27_105610_create_games_table Controller created successfully.
Créer des colonnes dans la table de base de données
Nous corrigeons la migration en ajoutant des colonnes à la table. Types les plus couramment utilisés:
increments('id')
string('title')
text('description')
tinyInteger('complexity')
boolean('isActive')
softDeletes()
Pour les champs optionnels, n'oubliez pas d'ajouter la valeur par défaut avec ->default()
Appliquer les migrations en exécutant php artisan migrate
Nous générons des règles de validation
Exécution de php artisan make:request GameRequest
Ouvrez App/Http/Requests/GameRequest.php
.
Dans la méthode authorize()
, définissez return true
jusqu'à ce que nous ajoutions l'autorisation.
Le tableau renvoyé dans la méthode rules()
décrit les règles de toutes les colonnes que nous avons répertoriées dans la migration. Règles disponibles ici
Pour minimiser le code, nous utilisons la construction de commutateur pour différents verbes http, au lieu de faire StoreGameRequest, UpdateGameRequest séparés, 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' ]; } }
Options de description d'erreur personnalisées
Si vous avez besoin de vos propres textes d'erreur, nous redéfinissons la méthode messages (), qui retourne un tableau avec les traductions de chaque règle:
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', ]; }
Afin de garantir que non seulement les paramètres passés dans le corps de la demande, mais aussi les paramètres passés dans l'URL sont disponibles dans les règles de validation, nous redéfinissons la méthode all (qui est généralement utilisée dans le contrôleur sous la forme de $ 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; }
Nous configurons le contrôleur et décrivons la logique métier
Ouvrez Http\Controllers\GameController
. Nous supprimons les méthodes create(), edit()
générées pour le rendu des formulaires (puisque nous avons une API REST, elles ne sont pas nécessaires).
Remplacez l' use Illuminate\Http\Request;
standard use Illuminate\Http\Request;
, sur notre use App\Http\Requests\GameRequest;
Ensuite, modifiez les méthodes:
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); }
S'il y a beaucoup de logique, il est préférable de le placer dans un service / référentiel de couche séparé
Personnalisez le modèle
Ouvrez le modèle app / Http / Game.php et ajoutez les propriétés:
protected $fillable = ['title', 'description', 'complexity', 'minPlayers', 'maxPlayers', 'isActive']; protected $hidden = ['created_at', 'updated_at', 'deleted_at'];
Configurer le middleware
Pour que notre application retourne toujours json quels que soient les en-têtes passés, nous créons un middleware:
php artisan make:middleware ForceJsonResponse
et ajoutez-y le code:
public function handle($request, Closure $next) { $request->headers->set('Accept', 'application/json'); return $next($request); }
Enregistrez ce middleware dans app/Http/Kernel.php
:
... 'api' => [ 'throttle:60,1', 'bindings', \App\Http\Middleware\ForceJsonResponse::class, ],
Nous configurons le routage
Ouvrez routes/api.php
et ajoutez:
use Http\Controllers\GameController; Route::apiResource('/games', 'GameController');
La méthode statique Route :: apiResource, contrairement à la méthode de ressource, exclut les méthodes d'édition et de création, ne laissant que l'index, afficher, stocker, mettre à jour, détruire.
La même chose peut être obtenue avec un record plus évident:
Route::resource('/games', 'GameController')->only([ 'index', 'show', 'store', 'update', 'destroy' ]);
Maintenant, vous pouvez regarder les chemins avec la commande php artisan route:list
et l'utiliser.
L'API REST est prête!
PostfacePostface
Si vous avez besoin d'une autorisation, le passeport Laravel standard fera l'affaire.
Configurer l'autorisation Laravel Passport
composer require laravel/passport php artisan make:auth php artisan passport:install php artisan migrate
Ajoutez le Laravel\Passport\HasApiTokens
au modèle App\User
et appelez Passport::routesmethod
dans la app/AuthServiceProvider
boot
app/AuthServiceProvider
:
public function boot() { $this->registerPolicies(); Passport::routes(); }
Dans le config/auth.php
changez le pilote en passeport:
'api' => [ 'driver' => 'passport', 'provider' => 'users', ],
Créez un contrôleur pour l'autorisation «php artisan make: controller Api / AuthController.php»
Ajoutez-y le 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); }
Après cela, vous pouvez utiliser les méthodes api/register, api/login, api/logout
pour l'autorisation et bloquer l'accès à l'api. Pour ce faire, nous devons encapsuler le routage de nos contrôleurs REST dans un middleware:
Route::middleware('auth:api')->group(function () { ... Route::get('/logout', 'Api\AuthController@logout')->name('logout'); });
PostfacePostface:
Il y aurait encore à faire des tests fonctionnels et la génération de documentation dans swagger, mais c'est un peu au-delà de la portée du tutoriel d'échafaudage, donc plus à ce sujet une autre fois