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();
    }

Realizando test

  • Una vez que se ha programado cada método, hay que implementar los correspondientes test para validar su funcionamiento
  • Primero vamos a ver la estructura de test que viene con laravel
  • Tenemos dos tipos de test
Feacture
Test de características donde se testean diferentes componenetes interactuando entre ellos
Unit
Test unitarios donde se testean piezas específicas de código, como por ejemplo una clase o una función
  • Vamos a utilizar los feacture test para esta aplicación MMR ??? (44:02)