Skip to content

Readme

API Client Service

Objetivo: Crear una API Client Service con relación NM. Características:

  • Relacionamos los dos modelos
  • Usaremos Middleware mediante grupo
  • Usaremos apiResource en rutas en lugar de resource (No añade create/update)
  • Opcional: Documentación con Swagger

  • composer create-project laravel/laravel client/service

  • Instalar API: php artisan install:api
  • Crear AuthController: php artisan make:controller AuthController

api.php

Dejamos el archivo de rutas:

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;
use App\Http\Controllers\ClientController;
use App\Http\Controllers\ServiceController;

Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);

Route::middleware('auth:sanctum')->group(function () {
    Route::post('/logout', [AuthController::class, 'logout']);
    Route::apiResource('clients', ClientController::class);
    Route::apiResource('services', ServiceController::class);
});

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');

API Rutas para probar sin autenticación

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;
use App\Http\Controllers\ClientController;
use App\Http\Controllers\ServiceController;

Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);

/*
Route::middleware('auth:sanctum')->group(function () {
    Route::post('/logout', [AuthController::class, 'logout']);
    Route::apiResource('clients', ClientController::class);
    Route::apiResource('services', ServiceController::class);
});

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');
*/

Route::apiResource('clients', ClientController::class);
Route::apiResource('services', ServiceController::class);

//rutas para introducir un servicio a un cliente
//de uno en uno
Route::post('/clients/{client}/services/{service}', [ClientController::class, 'addService']);

//de varios a la vez
Route::post('/clients/{client}/services', [ClientController::class, 'attachServices']);

classAuthController

<?php

// AuthController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Auth;
use Laravel\Sanctum\Sanctum;

class AuthController extends Controller {
    public function register(Request $request) {
        $request->validate([
            'name' => 'required',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:6',
        ]);
        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);
        return response()->json(['token' => $user->createToken('api-token')->plainTextToken, 'user' => $user]);
    }
    public function login(Request $request) {
        if (!Auth::attempt($request->only('email', 'password'))) {
            return response()->json(['message' => 'Unauthorized'], 401);
        }
        return response()->json(['token' => Auth::user()->createToken('api-token')->plainTextToken, 'user' => Auth::user()]);
    }
    public function logout(Request $request) {
        $request->user()->tokens()->delete();
        return response()->json(['message' => 'Logged out']);
    }
}

Al poner el anterior código, nos falla el modelo User al no tener el ApiToken, lo incorporamos:

Modelo User

Incluimos librería y HasApiToken

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasApiTokens, HasFactory, Notifiable;
... RESTO IGUAL

Modelo Client

Creamos el modelo cliente con migración, modelo y controlador

php artisan make:model Client -mcr

Modelo Service

Creamos el modelo Service conmigración, modelo y controlador

php artisan make:model Service -mcr

Migraciones

Create_client_service_table

php artisan make:migration create_client_service_table

 public function up(): void
    {
        Schema::create('client_service', function (Blueprint $table) {
            $table->id();
            $table->foreignId('client_id')->constrained()->onDelete('cascade');
            $table->foreignId('service_id')->constrained()->onDelete('cascade');
            $table->timestamps();
        });
    }

Migración Client

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up() {
        Schema::create('clients', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->string('phone');
            $table->timestamps();
        });
    }
    public function down() {
        Schema::dropIfExists('clients');
    }
};

a

Migración Service

// Migration for services table
return new class extends Migration {
    public function up() {
        Schema::create('services', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->text('description');
            $table->decimal('price', 10, 2);
            $table->timestamps();
        });
    }
    public function down() {
        Schema::dropIfExists('services');
    }
};

Migramos todo.

php artisan migrate

1738685527095

OPCIONAL Poblamos

Si se construye el seeder

php artisan db:seed

RouteList

1738686460897

Peticiones API Cliente

A continuación dejo el verbo, la ruta y el contenido del body para la solicitud

Client Index

GET

http://localhost:8000/api/clients

Client Store

POST

http://localhost:8000/api/clients

{
   "name": "name...",
   "email": "test@test.com",
   "phone": "123456789"
 }

Service Store

post

http://localhost:8000/api/services

{
   "name": "Analyst",
   "description": "Analyst",
   "price": "100"
 }

Tabla Pivote (2 opciones)

Client-Services-STORE (Tabla Pivote con varios servicios a la vez)

POST http://localhost:8000/api/clients/1/services

 {
   "services": [1, 2]
 }

Client-1Service-STORE (Tabla Pivote con un servicio en cada petición)

POST http://localhost:8000/api/clients/1/services/2

Nada en el body, va en la ruta.

1738687162495

Mejora.

Ampliarlo y probarlo con la autenticación y el login a través de Sanctum

Referencias

Más videos sobre esta temática que pueden ayudar: