Aplicación API de librería que usa la operación CRUD de Laravel 8
En este artículo, implementaremos una aplicación de librería Laravel 8 CRUD
. Después de leer este artículo, podrá:
- Aprender a crear tablas en la base de datos
MySQL
con relaciones deOne-to-Many
y deMany-to-Many
. - Implementar los modelos y las funciones a las que sirven.
- Saber cómo implementar CRUD con Laravel Resouce Controller
- Familiarízate con los métodos
belongsTo
ybelongsToMany
Una definición simple del proyecto.
Cada libro pertenece a una editorial, por lo que podrá asignar su libro a una editorial específica o, si no puede encontrarlo, puede almacenar una nueva editorial en la base de datos.
Además de eso, puede almacenar autores en la tabla de autores e incluir uno de ellos o varios de ellos en su libro como el nombre del autor. Si no desea leer este texto completo, puede omitirlo y encontrar el código completo en el repositorio en el que inserto el enlace al final del artículo.
Echemos un vistazo a cómo está diseñada nuestra base de datos, luego profundizaremos en el código.
Relación uno a muchos
Existe una relación de uno a muchos entre libros y editores. Un elemento de los editores puede estar vinculado a muchos elementos de los libros, pero un miembro de los libros está vinculado a un solo elemento de los editores.
Relación de muchos a muchos
Existe una relación de muchos a muchos entre libros y autores.
Cada libro ha pertenecido a muchos autores y cada autor ha escrito muchos libros. Entonces, en esta situación, usaremos una tabla como tabla dinámica para almacenar relaciones. Nuestra tabla dinámica es book_authors.
Crear tablas en la base de datos
Después de crear el proyecto con el compositor, es hora de crear nuestras tablas y migrarlas a la base de datos. En Laravel, usaremos el siguiente comando para crear la tabla.
php artisan make:migration create_publishers_table
En primer lugar, crearemos la tabla de editores que tendrá la siguiente estructura:
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreatePublishersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('publishers', function (Blueprint $table) { $table->id(); $table->string('identifier')->unique(); $table->string('fname'); $table->string('lname'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('publishers'); } }
Y aquí está el resto de las tablas que debe crear en orden:
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateBooksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('books', function (Blueprint $table) { $table->id(); $table->string('isbn')->unique(); $table->string('name'); $table->string('year'); $table->integer('page'); $table->foreignId('publisher_id')->constrained('publishers'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('books'); } }
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateAuthorsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('authors', function (Blueprint $table) { $table->id(); $table->string('identifier')->uniqu(); $table->string('fname'); $table->string('lname'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('authors'); } }
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateBookAuthorsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('book_authors', function (Blueprint $table) { $table->id(); $table->foreignId('book_id')->constrained('books'); $table->foreignId('author_id')->constrained('authors'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('book_authors'); } }
Crear modelos
En esta sección, hablaremos de los modelos y las funciones que cumplen. Primero, crearemos el modelo Publisher con el comando php artisan make:model Publisher
y, como puede ver en la estructura de la base de datos, existe una relación de uno a muchos entre libros y editores, y esto se define mediante la definición de un método en el modelo Eloquent.
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Publisher extends Model { use HasFactory; /** * @var array */ protected $fillable = [ 'identifier', 'fname', 'lname', ]; /** * publisher's books * * @return void */ public function books(){ return $this->hasMany(Book::class,'publisher_id'); } }
Es hora de crear otros modelos y definir la relación de varios a varios entre libros y autores. así que crearemos el modelo BookAuthor como un pivote Eloquent. Para hacer esto, asegúrese de que su modelo se extienda Illuminate\Database\Eloquent\Relations\Pivot
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\Pivot; class BookAuthor extends Pivot { use HasFactory; protected $table = 'book_authors'; }
Y ahora crearemos el modelo Author, que contendrá la función con el nombre de los libros que usa el método belongsToMany
y el modelo BookAuthor
como modelo Pivot
para obtener la lista de todos los libros del objeto autor.
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Author extends Model { use HasFactory; /** * @var array */ protected $fillable = [ 'identifier', 'fname', 'lname', ]; /** * authors's books * * @return void */ public function books(){ return $this->belongsToMany(Author::class,'book_authors')->using(BookAuthor::class); } }
Finalmente, tendremos el modelo de libro que usa el método belongsTo
para obtener el editor relacionado en la función con el nombre del editor y utiliza el método belongsToMany
y el modelo BookAuthor
como modelo dinámico para obtener la lista de todos los autores del objeto libro.
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Book extends Model { use HasFactory; /** * @var array */ protected $fillable = [ 'isbn', 'name', 'year', 'page', 'publisher_id', ]; /** * book's publisher * * @return void */ public function publisher() { return $this->belongsTo(Publisher::class) ->withDefault([ 'identifier' => 'WITHOUT ID', 'fname' => 'NOT FOUND', 'lname' => 'NOT FOUND', ]); } /** * book's authors * * @return void */ public function authors() { return $this->belongsToMany(Author::class, 'book_authors') ->using(BookAuthor::class); } }
Crear controladores
- En este proyecto, cada controlador contendrá las siguientes funciones:
- La función de índice se utilizará para obtener una lista completa de todos los registros.
- En la función de tienda después de validar las solicitudes, se creará el nuevo elemento.
- La función show se usa para obtener el elemento específico pasando ID a la función.
- Pasamos el ID a la función de destrucción y luego detectamos elementos con el ID dado y luego lo borramos.
- La función de actualización se utiliza para actualizar el elemento dado en función de las solicitudes dadas.
<?php namespace App\Http\Controllers; use App\Models\Publisher; use Exception; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Http\Request; class PublisherController extends Controller { /** * @var Model */ protected $model; /** * __construct * * @param Publisher $publisher * @return void */ public function __construct(Publisher $publisher) { $this->model = $publisher; } /** * @return Collection */ public function index() { $items = $this->model->with('books')->get(); return response(['data' => $items, 'status' => 200]); } /** * @param Request $request * @return Collection */ public function store(Request $request) { $request->validate([ 'identifier' => 'required|unique:publishers|min:3', 'fname' => 'required', 'lname' => 'required', ]); $this->model->create($request->all()); return $this->index(); } /** * @param mixed $id * @return Collection */ public function destroy($id) { try { $item = $this->model->with('books')->findOrFail($id); $item->delete(); return $this->index(); } catch (ModelNotFoundException $e) { return response(['message' => 'Item Not Found!', 'status' => 404]); } } /** * @param mixed $id * @return Model */ public function show($id) { try { $item = $this->model->with('books')->findOrFail($id); return response(['data' => $item, 'status' => 200]); } catch (ModelNotFoundException $e) { return response(['message' => 'Item Not Found!', 'status' => 404]); } } /** * @param mixed $id * @param mixed $request * @return Collection */ public function update($id, Request $request) { try { $item = $this->model->with('books')->findOrFail($id); $item->update($request->all()); return response(['data' => $item, 'status' => 200]); } catch (ModelNotFoundException $e) { return response(['message' => 'Item Not Found!', 'status' => 404]); } } }
En la función de actualización y almacenamiento de BookController, verá el código $item->authors()->sync($requeest->get('authors'))
. sync
se puede utilizar para sincronizar ambos lados de una relación belongsToMany
. De forma predeterminada, descarta todas las relaciones y solo mantiene las proporcionadas como argumentos. De manera similar, si desea eliminar una determinada relación de entidad de la tabla dinámica, puede usar el método. Por ejemplo, si desea eliminar a los autores de un libro, puede hacerlo con el código $item->authors()->detach()
.
<?php namespace App\Http\Controllers; use App\Models\Book; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Http\Request; class BookController extends Controller { /** * @var Model */ protected $model; /** * @param Model $book * @return void */ public function __construct(Book $book) { $this->model = $book; } /** * @return Collection */ public function index() { $items = $this->model->with('authors', 'publisher')->get(); return response(['data' => $items, 'status' => 200]); } /** * @param Request $request * @return Collection */ public function store(Request $request) { $request->validate([ 'isbn' => 'required|digits:13|integer|unique:books,isbn', 'name' => 'required|min:3', 'year' => 'required|integer|digits:4', 'page' => 'required|integer', 'publisher_id' => 'exists:publishers,id', 'authors' => 'array', 'authors.*' => 'exists:authors,id' ]); $item = $this->model->create($request->all()); $authors = $request->get('authors'); $item->authors()->sync($authors); return $this->index(); } /** * @param mixed $id * @return Collection */ public function destroy($id) { try { $item = $this->model->with('authors', 'publisher')->findOrFail($id); $item->authors()->detach(); $item->delete(); return $this->index(); } catch (ModelNotFoundException $e) { return response(['message' => 'Item Not Found!', 'status' => 404]); } } /** * @param mixed $id * @return Model */ public function show($id) { try { $item = $this->model->with('authors', 'publisher')->findOrFail($id); return response(['data' => $item, 'status' => 200]); } catch (ModelNotFoundException $e) { return response(['message' => 'Item Not Found!', 'status' => 404]); } } /** * @param mixed $id * @param mixed $request * @return Collection */ public function update($id, Request $request) { try { $item = $this->model->with('authors', 'publisher')->findOrFail($id); $item->update($request->all()); $authors = $request->get('authors'); $item->authors()->sync($authors); return response(['data' => $item, 'status' => 200]); } catch (ModelNotFoundException $e) { return response(['message' => 'Item Not Found!', 'status' => 404]); } } }
<?php namespace App\Http\Controllers; use App\Models\Author; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Http\Request; class AuthorController extends Controller { /** * @var Model */ protected $model; /** * @param Model $author * @return void */ public function __construct(Author $author) { $this->model = $author; } /** * @return Collection */ public function index() { $items = $this->model->with('books')->get(); return response(['data' => $items], 200); } /** * @param Request $request * @return Collection */ public function store(Request $request) { $request->validate([ 'identifier' => 'required|unique:authors|min:3', 'fname' => 'required', 'lname' => 'required', ]); $this->model->create($request->all()); return $this->index(); } /** * @param mixed $id * @return Collection */ public function destroy($id) { try { $item = $this->model->with('books')->findOrFail($id); $item->books()->detach(); $item->delete(); return $this->index(); } catch (ModelNotFoundException $e) { return response(['message' => 'Item Not Found!', 'status' => 404]); } } /** * @param mixed $id * @return Model */ public function show($id) { try { $item = $this->model->with('books')->findOrFail($id); return response(['data' => $item, 'status' => 200]); } catch (ModelNotFoundException $e) { return response(['message' => 'Item Not Found!', 'status' => 404]); } } /** * @param mixed $id * @param mixed $request * @return Collection */ public function update($id, Request $request) { try { $item = $this->model->with('books')->findOrFail($id); $item->update($request->all()); return response(['data' => $item, 'status' => 200]); } catch (ModelNotFoundException $e) { return response(['message' => 'Item Not Found!', 'status' => 404]); } } }
Crear rutas
Finalmente, definiremos nuestras rutas en el directorio routes/api.php
. Usando Route:apiResource
configura algunas rutas predeterminadas. Y puede ver la lista de todas las rutas ejecutando el comandode Artisan route:list
.
<?php use App\Http\Controllers\AuthorController; use App\Http\Controllers\BookController; use App\Http\Controllers\PublisherController; use Illuminate\Support\Facades\Route; Route::apiResource('authors', AuthorController::class); Route::apiResource('publishers', PublisherController::class); Route::apiResource('books',BookController::class);
Ahora tienes una API de librería. El código completo se encuentra en el siguiente repositorio.
https://github.com/hanieas/Tutorial-Book-Store-API
Espero que este curso lo ayude a familiarizarse con el diseño de bases de datos, las relaciones de uno a muchos y las relaciones de muchos-a-muchos en Laravel.