inb4: copiar e colar da documentação
O guia concentra-se na rápida implantação de um conjunto mínimo para o desenvolvimento completo da API, de acordo com as melhores práticas, retiradas da documentação do Laravel 5.7, coletadas em um único local. Escrevi para mim e meus colegas como uma folha de dicas, espero que seja útil para outra pessoa.
Predefinição
Nós colocamos uma estrutura
composer create-project --prefer-dist laravel/laravel scaffold-api
Remova componentes desnecessários da interface do usuário (vuejs, reaja)
php artisan preset none
Configurar conexão com o banco de dados
Vá para a pasta, edite o arquivo .env:
DB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 DB_DATABASE=api-authentification DB_USERNAME=root DB_PASSWORD=
Introdução à geração
Executamos no console
php artisan make:model Game -mrc
Temos o modelo, migração e controlador:
Model created successfully. Factory created successfully. Created Migration: 2019_02_27_105610_create_games_table Controller created successfully.
Crie colunas na tabela do banco de dados
Corrigimos a migração adicionando colunas à tabela. Tipos mais usados:
increments('id')
string('title')
text('description')
tinyInteger('complexity')
boolean('isActive')
softDeletes()
Para campos opcionais, não esqueça de adicionar o valor padrão com ->default()
Aplique migrações executando o php artisan migrate
Geramos regras de validação
Executando o php artisan make:request GameRequest
Abra App/Http/Requests/GameRequest.php
.
No método authorize()
, defina return true
até adicionarmos autorização.
A matriz retornada no método rules()
descreve as regras para todas as colunas listadas na migração. Regras disponíveis aqui
Para minimizar o código, usamos a construção switch para diferentes verbos http, em vez de fazer 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' ]; } }
Opções de descrição de erro personalizadas
Se você precisar de seus próprios textos de erro, redefinimos o método messages (), que retorna uma matriz com traduções de cada regra:
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 garantir que não apenas os parâmetros passados no corpo da solicitação, mas também os parâmetros passados na URL estejam disponíveis nas regras de validação, redefinimos o método all (normalmente usado no controlador na 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 o controlador e descrevemos a lógica de negócios
Abra Http\Controllers\GameController
. Removemos os métodos gerados create(), edit()
destinados a renderizar formulários (já que temos uma API REST, eles não são necessários).
Substitua o use Illuminate\Http\Request;
padrão use Illuminate\Http\Request;
, em nosso use App\Http\Requests\GameRequest;
Em seguida, edite os 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); }
Se houver muita lógica, é melhor colocá-la em uma camada separada Serviço / Repositório
Personalize o modelo
Abra o modelo app / Http / Game.php e adicione as propriedades:
protected $fillable = ['title', 'description', 'complexity', 'minPlayers', 'maxPlayers', 'isActive']; protected $hidden = ['created_at', 'updated_at', 'deleted_at'];
Configurar middleware
Para que nosso aplicativo sempre retorne json, independentemente dos cabeçalhos passados, criamos o middleware:
php artisan make:middleware ForceJsonResponse
e adicione o código a ele:
public function handle($request, Closure $next) { $request->headers->set('Accept', 'application/json'); return $next($request); }
Registre este middleware em app/Http/Kernel.php
:
... 'api' => [ 'throttle:60,1', 'bindings', \App\Http\Middleware\ForceJsonResponse::class, ],
Nós configuramos o roteamento
Abra routes/api.php
e adicione:
use Http\Controllers\GameController; Route::apiResource('/games', 'GameController');
O método estático Route :: apiResource, diferentemente do método de recurso, exclui os métodos de edição e criação, deixando apenas o índice, mostra, armazena, atualiza e destrói.
O mesmo pode ser alcançado com um registro mais óbvio:
Route::resource('/games', 'GameController')->only([ 'index', 'show', 'store', 'update', 'destroy' ]);
Agora, você pode ver o caminho com o comando php artisan route:list
e usá-lo.
A API REST está pronta!
PosfácioPosfácio
Se você precisar de autorização, o Laravel Passport padrão o fará.
Configurar autorização do Laravel Passport
composer require laravel/passport php artisan make:auth php artisan passport:install php artisan migrate
Adicione a característica Laravel\Passport\HasApiTokens
ao modelo App\User
e chame Passport::routesmethod
no app/AuthServiceProvider
boot
app/AuthServiceProvider
:
public function boot() { $this->registerPolicies(); Passport::routes(); }
No config/auth.php
altere o driver para passaporte:
'api' => [ 'driver' => 'passport', 'provider' => 'users', ],
Crie um controlador para autorização 'php artisan make: controller Api / AuthController.php`
Adicione o código lá
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); }
Depois disso, você pode usar os métodos api/register, api/login, api/logout
para autorização e bloquear o acesso à API. Para fazer isso, precisamos agrupar o roteamento de nossos controladores REST no middleware:
Route::middleware('auth:api')->group(function () { ... Route::get('/logout', 'Api\AuthController@logout')->name('logout'); });
PosfácioPosfácio:
Ainda haveria testes funcionais e geração de documentação com arrogância, mas isso está um pouco além do escopo do tutorial do andaime, e mais sobre isso outra vez