Diferencia entre revisiones de «Usuario:ManuelRomero/Laravel/API»
(4 revisiones intermedias por el mismo usuario no mostrado) | |||
Línea 191: | Línea 191: | ||
} | } | ||
</source> | </source> | ||
− | + | [[Imagen: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) | ||
+ | [[archivo: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 | ||
+ | <source lang=php> | ||
+ | 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; | ||
+ | } | ||
+ | </source> | ||
+ | *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 | ||
+ | <source lang=php> | ||
+ | 'required' => 'The :attribute field is required.', | ||
+ | ((Lo cambiamos por | ||
+ | 'required' => 'El :attribute es un campo obligatorio.', | ||
+ | |||
+ | </source> | ||
+ | *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í | ||
+ | <source lang=php> | ||
+ | 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; | ||
+ | } | ||
+ | |||
+ | </source> | ||
+ | |||
+ | *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 | ||
+ | [[Archivo:patch_posman.png]] | ||
+ | ;delete | ||
+ | *Este es un método muy sencillo de implementar y de invocar | ||
+ | |||
+ | <source lang=php> | ||
+ | public function destroy(Book $book) | ||
+ | { | ||
+ | // | ||
+ | $book->delete(); | ||
+ | //Retornamos una página con código 204 o página vacía | ||
+ | return response()->noContent(); | ||
+ | } | ||
+ | |||
+ | </source> | ||
+ | ===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) |
Última revisión de 05:22 2 dic 2022
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, ], ];
(: 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); } //
(: 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.
- 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"; }
- 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)
- 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
- 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)