Usuario:ManuelRomero/Laravel/API

De WikiEducator
Saltar a: navegación, buscar

Crear un aplicación

laravel new api --git
Fichero de rutas
  • Se va a realizar una api que va a ser una aplicación para ser consumida por otra o por nosotros usando la normativa/recomendación rest, que luego comentaremos
  • El fichero de rutas que se usa para una api, debería de ser el de /routes/api.php, en lugar de el de /routes/web.php
  • La principal diferencia entre este fichero de rutas api.php y web.php son sus middelware.

¨Los middelware están especificados en el fichero http/kernel.php y ver los middelware que usa cada ruta.

  • Ahí vemos los middelware aplicados al fichero web y al fichero api:
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
 
        'api' => [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            //thorttle es un límite de peticiones en un determinado tiempo
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

(Comment.gif: Los middelware aplicados a api son trhottle, es decir el límite de peticiones en un determinado tiempo para evitar ataques y que el servidor se quede bloqueado)


  • Si miramos las rutas creadas con
php artisan route:list --except-vendor

Vemos que aparece una ruta

  bookspai git:(master) ✗ php artisan  route:list --except-vendor
 
  GET|HEAD       / ........................................................... 
  GET|HEAD       api/user ....................................................
  • Observamos que la ruta user, definida en el fichero routes/api.php no tiene el prefijo
  • Este se pone en fichero RouteServiceProviders en el método boot se establece
  • Posibilidad de crear más prefijos y más ficheros, por ejemplo diferentes versiones (Pendiente probar)
  $this->routes(function () {
            Route::middleware('api')
                ->prefix('api/v1')
                ->group(base_path('routes/api.php'));
            Route::middleware('api')
                ->prefix('api/v2')
                ->group(base_path('routes/api2.php'));


Creando los modelos de nuestra api
php artisan make:model Book -mf --apo
  • -m migracion
  • -f factory
  • --api crea el controlador con los métodos tipo Rest
  • LA especificación Rest me dice cómo deberían de ser las solicitudes y los métodos asociados para atenderlos

Archivo:Rest api.png(21:50)

Podemos no utilizar la ruta de editar un libro o crear un libro, ya que no vamos a utilizarlo al ser una API, solo vamos a consumir datos.

Modificando el fichero de rutas api.php
  • Ahora especicamos las rutas rest, usando el métod ApiRoute de la facade Route
Route::apiResource("bookd", \App\Http\Controllers\BookController::class);
  • Podemos ver ahora las rutas
 bookspai git:(master) ✗ php artisan route:list --except-vendor
 
  GET|HEAD        / ......................................................................................................... 
  GET|HEAD        api/books .............................................................. books.index › BookController@index
  POST            api/books .............................................................. books.store › BookController@store
  GET|HEAD        api/books/{book} ......................................................... books.show › BookController@show
  PUT|PATCH       api/books/{book} ..................................................... books.update › BookController@update
  DELETE          api/books/{book} ................................................... books.destroy › BookController@destroy
  GET|HEAD        api/user .............................................................................................
Implementando los métodos
  • Primero debemos crear la base de datos
  • Para ello configuramos la conexión(fichero .env)

Idioma no válido.

Necesita especificar un idioma como esto: <source lang="html4strict">...</source>

lenguajes soportados para sintaxis remarcada:

4cs, 6502acme, 6502kickass, 6502tasm, 68000devpac, abap, actionscript, actionscript3, ada, algol68, apache, applescript, apt_sources, arm, asm, asp, asymptote, autoconf, autohotkey, autoit, avisynth, awk, bascomavr, bash, basic4gl, bf, bibtex, blitzbasic, bnf, boo, c, c_loadrunner, c_mac, caddcl, cadlisp, cfdg, cfm, chaiscript, cil, clojure, cmake, cobol, coffeescript, cpp, cpp-qt, csharp, css, cuesheet, d, dcl, dcpu16, dcs, delphi, diff, div, dos, dot, e, ecmascript, eiffel, email, epc, erlang, euphoria, f1, falcon, fo, fortran, freebasic, freeswitch, fsharp, gambas, gdb, genero, genie, gettext, glsl, gml, gnuplot, go, groovy, gwbasic, haskell, haxe, hicest, hq9plus, html4strict, html5, icon, idl, ini, inno, intercal, io, j, java, java5, javascript, jquery, kixtart, klonec, klonecpp, latex, lb, ldif, lisp, llvm, locobasic, logtalk, lolcode, lotusformulas, lotusscript, lscript, lsl2, lua, m68k, magiksf, make, mapbasic, matlab, mirc, mmix, modula2, modula3, mpasm, mxml, mysql, nagios, netrexx, newlisp, nsis, oberon2, objc, objeck, ocaml, ocaml-brief, octave, oobas, oorexx, oracle11, oracle8, oxygene, oz, parasail, parigp, pascal, pcre, per, perl, perl6, pf, php, php-brief, pic16, pike, pixelbender, pli, plsql, postgresql, povray, powerbuilder, powershell, proftpd, progress, prolog, properties, providex, purebasic, pycon, pys60, python, q, qbasic, rails, rebol, reg, rexx, robots, rpmspec, rsplus, ruby, sas, scala, scheme, scilab, sdlbasic, smalltalk, smarty, spark, sparql, sql, stonescript, systemverilog, tcl, teraterm, text, thinbasic, tsql, typoscript, unicon, upc, urbi, uscript, vala, vb, vbnet, vedit, verilog, vhdl, vim, visualfoxpro, visualprolog, whitespace, whois, winbatch, xbasic, xml, xorg_conf, xpp, yaml, z80, zxbasic


DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=books
DB_USERNAME=manuel
DB_PASSWORD=manuel12345
  • Modificamos el fichero de migracion para agregar un campo
public function up()
    {
        Schema::create('books', function (Blueprint $table) {
            $table->id();
            $table->string("title");
 
            $table->timestamps();
        });
    }
  • ejecutamos la migración
php artisan migrate
  • Añadimos un libro en la base de datos
  • Ahora agregamos el código en el método index que es el que se va a ejecutar cuando hagamos una solicitud get a api/books (ver rutas creadas)
 public function index()
    {
        //
        $books=Book::all();
        return $books;
 
 
    }
  • Por si hubiera miles de libros se suele usar el método paginate en lugar de all(), observa la salida
 public function index()
    {
        //
        $books=Book::paginate();
        return $books;
 
 
    }
  • En este método puedo especificar cuantos registros quiero por página
  • Solicitamos en el navegador (previo levantamos el servidor php artisan serve &) y nos aparecer un json con los libros
  • PAra verlo un poco parseado instalamos en el navegador algún plugin (por ejemplo json-formatter)
El método show
 public function show(Book $book)
    {
        //
        return $book; 
    }
Observa la salida en los siguientes casos y concluimos
 public function show($book)
    {
        //
        return $book; 
    }
 
//En este caso solo recibimos el identificador
 
public function show(Book $book)
    {
        //
        return Books::find($book); 
    }
//


(Comment.gif: Esto se conoce como Route Binding El Implicit Route Binding es una funcionalidad de Laravel que nos permite atar un modelo a una ruta de forma automática, de tal manera que podamos acceder a los registros de un modelo sin definir de forma explicita una función de búsqueda para dicho registro. )


  • Los métodos index y show se ejecutan con un verbo GET
  • Ahora necesitamos otros verbos (PATH POST y DELETE), por lo que para poderlos usar vamos al no tener un cliente creado, vamos a usar uan app llamada postman que nos va a permitir enviar solicitudes usando estos verbos.

https://aprendible.com/fullstack/backend?utm_source=newsletter&utm_medium=email&utm_campaign=Cu%C3%A9ntame%20%C2%BFQu%C3%A9%20te%20pareci%C3%B3%20el%20primer%20d%C3%ADa%20de%20entrenamiento%3F (33:18)

  • El uso de esta app postman es muy sencillo, debemos de especificar el verbo por el que solicitamos, y la url donde solicitamos
  • Se puede probar los diferentes verbos, retornando un texto en el médoto correspondiente
 public function store(Request $request)
    {
        //
        return "Estoy en store";
    }

Post posman.png

  • De esta forma podríamos probar todos los casos
store
  • En este método recibe un Request que contendrá todos los datos del libro que le pasamos por post
  • Para ello debemos pasar valores a través de posman y recogerlos en el método
  • Los recogemos y les damos de alta en la base de datos
  • Para enviar los datos en posman, vamos a fbody, y ahí, en orm-data escribimos el campo title y el valor que queremos asignar (si tuviera más datos mi modelo, lo tendríamos que insertar ahí.(ver imagen siguiente)

Post data posman.png

  • Si enviamos la solicitud sin aportar un valor en título, vemos que nos genera una excepción dando información del campo en concreto

(Si en posman en header escribo en key Accept y en value application/json, se ve en formato más legible) Archivo:Error null field.png

  • PAra que en lugar de esta excepción, nos retorne un error informando que el título no puede estar vacío validamos el requuest en el método store
  • De esta forma el método nos queda
  public function store(Request $request)
    {
        $request->validate([
            'title'=>['required']
        ]);
        $book = new Book();
        //Recogemos el nombre del libre, en esta caso solo tenemos ese atributo
        $book->title= $request->input('title');
        //también funcionaría $request->name;
        $book->save();
 
        //Ahora lo devolvemos para retornar al cliente el libro que acabamos de instanciar en la BD
        return $book;
    }
  • Vemos que el texto aparece en inglés. Si queremos establecer en español (cambiar el lang en la configuración de la aplicación
    • Luego duplicamos la carpetan lang/en a lang/es y vamos al fichero validation.php y modificamos el elemento require y así quedaría
'required' => 'The :attribute field is required.',
((Lo cambiamos por 
'required' => 'El :attribute  es un campo obligatorio.',
  • Hay un repositorio llamado laravel-lang donde tenemos muchas traducciones ya realizadas
https://laravel-lang.com/  
update

El método update es muy parecido al método store, lo que ocurre es que recibe el libro que queremos modificar como parámetro en la invocación

  • Entonces en lugar de crear un nuevo libro, tomamos el que recibimos y con los datos de request, lo actualizamos
  • El código quedaría así
    public function update(Request $request, Book $book)
    {
        $request->validate([
            'title'=>['required']
        ]);
 
        //Recogemos el nombre del libro acutalizado que viene en request y
        // se lo asignamos al libro que también recibimos (que queremos actualizar) 
        $book->title= $request->input('title');
        //también funcionaría $request->name;
        $book->save();
 
        //Ahora lo devolvemos para retornar al cliente el libro que acabamos de instanciar en la BD
        return $book;
    }
  • Para probarlo con posman, hay que tener en cuenta que el verbo pacht no funciona como post, y los parámetros nuevos del libro (lo que queremos modificar), han de ir en lugar de en form-data en x-wwww-form-urlencoded, como podemos ver en la imagen siguiente

Patch posman.png

delete
  • Este es un método muy sencillo de implementar y de invocar
    public function destroy(Book $book)
    {
        //
        $book->delete();
//Retornamos una página con código 204 o página vacía
        return response()->noContent();
    }