inb4: copie y pegue de la documentación
La guía se enfoca en el despliegue rápido de un conjunto mínimo para el desarrollo completo de API de acuerdo con las mejores prácticas, tomado de la documentación de Laravel 5.7, reunido en un solo lugar. Escribí para mí y mis colegas como una hoja de trucos, espero que sea útil para otra persona.
Preestablecido
Ponemos un marco
composer create-project --prefer-dist laravel/laravel scaffold-api
Eliminar componentes innecesarios de la interfaz de usuario (vuejs, reaccionar)
php artisan preset none
Configurar conexión de base de datos
Vaya a la carpeta, edite el archivo .env:
DB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 DB_DATABASE=api-authentification DB_USERNAME=root DB_PASSWORD=
Comenzando a generar
Ejecutamos en la consola
php artisan make:model Game -mrc
Obtenemos el modelo, la migración y el controlador:
Model created successfully. Factory created successfully. Created Migration: 2019_02_27_105610_create_games_table Controller created successfully.
Crear columnas en la tabla de la base de datos.
Corregimos la migración agregando columnas a la tabla. Tipos más utilizados:
increments('id')
string('title')
text('description')
tinyInteger('complexity')
boolean('isActive')
softDeletes()
Para campos opcionales, no olvide agregar el valor predeterminado con ->default()
Aplicar migraciones ejecutando php artisan migrate
Generamos reglas de validación.
Ejecutando php artisan make:request GameRequest
Abra la App/Http/Requests/GameRequest.php
.
En el método authorize()
, establezca return true
hasta que agreguemos autorización.
La matriz que se devuelve en el método rules()
describe las reglas para todas las columnas que enumeramos en la migración. Reglas disponibles aquí
Para minimizar el código, usamos la construcción del interruptor para diferentes verbos http, en lugar de hacer 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' ]; } }
Opciones de descripción de error personalizado
Si necesita sus propios textos de error, redefinimos el método de mensajes (), que devuelve una matriz con traducciones de cada regla:
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', ]; }
Para garantizar que no solo los parámetros pasados en el cuerpo de la solicitud, sino también los parámetros pasados en la URL estén disponibles en las reglas de validación, redefinimos el método all (que generalmente se usa en el controlador en forma 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; }
Configuramos el controlador y describimos la lógica del negocio.
Abra Http\Controllers\GameController
. Eliminamos los métodos generados create(), edit()
destinados a representar formularios (ya que tenemos una API REST, no son necesarios).
Reemplace el use Illuminate\Http\Request;
estándar use Illuminate\Http\Request;
, en nuestro use App\Http\Requests\GameRequest;
A continuación, edite los métodos:
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); }
Si hay mucha lógica, entonces es mejor colocarla en una capa separada Servicio / Repositorio
Personaliza el modelo
Abra el modelo de la aplicación / Http / Game.php y agregue las propiedades:
protected $fillable = ['title', 'description', 'complexity', 'minPlayers', 'maxPlayers', 'isActive']; protected $hidden = ['created_at', 'updated_at', 'deleted_at'];
Configurar middleware
Para que nuestra aplicación siempre devuelva json independientemente de los encabezados pasados, creamos middleware:
php artisan make:middleware ForceJsonResponse
y agregue el código:
public function handle($request, Closure $next) { $request->headers->set('Accept', 'application/json'); return $next($request); }
Registre este middleware en la app/Http/Kernel.php
:
... 'api' => [ 'throttle:60,1', 'bindings', \App\Http\Middleware\ForceJsonResponse::class, ],
Configuramos enrutamiento
Abra routes/api.php
y agregue:
use Http\Controllers\GameController; Route::apiResource('/games', 'GameController');
El método estático Route :: apiResource, a diferencia del método de recursos, excluye los métodos de edición y creación, dejando solo indexar, mostrar, almacenar, actualizar, destruir.
Lo mismo se puede lograr con un registro más obvio:
Route::resource('/games', 'GameController')->only([ 'index', 'show', 'store', 'update', 'destroy' ]);
Ahora, puede mirar los caminos con la php artisan route:list
comando de php artisan route:list
y usarlo.
¡REST API está lista!
EpílogoEpílogo
Si necesita autorización, lo hará el Pasaporte Laravel estándar.
Configurar la autorización del pasaporte Laravel
composer require laravel/passport php artisan make:auth php artisan passport:install php artisan migrate
Agregue el Laravel\Passport\HasApiTokens
al modelo App\User
y llame a Passport::routesmethod
en la app/AuthServiceProvider
boot
app/AuthServiceProvider
:
public function boot() { $this->registerPolicies(); Passport::routes(); }
En el config/auth.php
cambie el controlador a pasaporte:
'api' => [ 'driver' => 'passport', 'provider' => 'users', ],
Cree un controlador para la autorización 'php artisan make: controller Api / AuthController.php`
Agrega el código allí
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); }
Después de eso, puede usar los métodos api/register, api/login, api/logout
para la autorización y bloquear el acceso a la api. Para hacer esto, necesitamos envolver el enrutamiento de nuestros controladores REST en middleware:
Route::middleware('auth:api')->group(function () { ... Route::get('/logout', 'Api\AuthController@logout')->name('logout'); });
EpílogoEpílogo:
Todavía habría que hacer pruebas funcionales y generación de documentación en swagger, pero esto está un poco más allá del alcance del tutorial de andamios, así que más sobre eso en otro momento