PHP Servizos Web

De MediaWiki
Saltar a: navegación, buscar

Introdución

  • Neste manual non aplica ben as regras para que sexa Rest (os código de erro de resposta HTTP).


Servizo REST: Características

Información obtida deste enlace.
  • REST é o acrónimo de Representational State Transfer.


  • Para que sexa considerado REST ten que cumprir estas características:
  • Baseado nun protocolo de cliente/servidor sen estado (protocolo Http)
  • Cacheable
  • Escalable
  • Cunhas operacións ben definidas no que os recursos estean identificados de forma única por URIs.


  • Ao utilizar o protocolo HTTP, xa vimos que non garda o estado polo que deberemos de enviar os datos necesarios para a autenticación en cada petición que fagamos.


  • Cada petición que fagamos levará asociada o uso dun verbo que indicará un tipo de acción a realizar:
  • GET: Utilízase para acceder a un recurso
  • POST: Envía datos para crear un recurso. Os datos non van na URI, se non no corpo da mensaxe.
  • PUT: Utilizado para editar un recurso.
  • DELETE: Elimina un recurso
  • PATCH: Utilízase para modificar parcialmente un recurso. Normalmente faise con PUT.


  • Cando fagamos algunha das operacións anteriores deberemos de informar a quen fixo a petición do éxito ou fracaso da operación. Non existe un estándar que nos obrigue a utilizar uns códigos ou outros, pero deberemos de aproveitar' os código HTML para facilitar o 'consumo' do noso servizo REST:
  • 200 → OK : Petición recibida e procesada de forma correcta
  • 201 → Created : Petición completada. Creouse un novo recurso
  • 204 → No Content: Petición procesada correctamente, pero a resposta non ten ningún contido
  • 401 → Unauthorized: A información de autenticación non é válida
  • 404 → Not found: O recurso non se atopou.
Temos neste enlace os código de resposta do protocolo HTTP.
Temos neste enlace a sintaxe da resposta que debe dar un servizo e cando se deben enviar cada un dos códigos de resposta do protocolo HTTP.


  • Cacheable:
Isto quere dicir que ten que ter a posibilidade de poder 'cachear' os resultados para ter un mellor desempeño.
As peticións deben indicar se o resultado pode ou non ser cacheado.


  • Escalable:
O servidor encargado de recibir e procesar as respostas debe ser capaz de ser dividido en capas (cada capa se encargará de procesar as peticións de diferentes recursos). Desta forma podemos ter diferentes políticas de seguridade.


  • Identificación de recursos mediante URIs:
Normalmente un servizo REST vai permitir realizar operacións de consulta, actualización, engadir e borrado sobre diferentes elementos de informacións. Es estes elementos denomínanse recursos (por exemplo, engadir un novo libro se xestionamos unha librería, obter o listado de clientes, obter o prezo de todas as pantallas de 14 se xestionamos unha tenda de computadores,...).
Cando fagamos unha operación sobre un destes recursos, temos que utilizar unha URI que deberá cumprir as seguintes propiedades:
  • Deben ser únicas, non pode existir máis dunha URI para identificar o mesmo recurso.
  • Deben ser independentes do formato no que queiramos consultar el recurso (a URI debe ser a mesma se devolvemos a información en JSON ou XML, por exemplo)
  • Deben manter una xerarquía na ruta do recurso
  • No deben indicar accións, polo que non debemos usar verbos á hora de definir unha URI (como viñamos facendo nas aplicacións WEB)


Por exemplo, se queremos xestionar un recurso 'libros':


Nota: Normalmente o recurso estará en minúsculas e en plural a no ser que o recurso sexa único (por exemplo a configuración).
En caso de querer filtrar os recursos faremos uso da URI e enviaremos a información de filtrado usando parámetros da forma: ?param1=valor1&param2=valor.
Por exemplo, se queremos obter a lista de libros de informática: http://meusitio.es/libros?tematica=informatica.
Temos que ter coidado coa xerarquía de recursos e acceder atendendo a dita xerarquía.
Así se xestiono varias tendas de libros, o acceso a un libro dunha tenda debería ser: http://meusitio.es/tendas/1/libros/5 e non ao revés (http://meusitio.es/libros/5/tendas/1).


Outras características que deberían cumprir as URI's:
  • Utilizar minúsculas y guións ou guións baixos (snake-case) no canto de maiúsculas y minúsculas (CamelCase)
  • Non utilizar caracteres que necesiten codificación URL como por exemplo espazos en branco, comillas, etc.
  • Non utilizar parámetros de consulta (?tipo=1) en peticións que no sexan de consulta.


  • Formato de resposta:
Normalmente o formato vai ser XML ou JSON.
Como comentamos anteriormente na URI non debe ir ningunha información sobre o tipo de formato da resposta.
O tipo de formato da resposta será indicado na cabeceira da petición.

Creación dun servizo web

Base de datos

  • Partimos da seguinte base de datos de nome BELLEZA composta polas seguintes táboas:

Táboa MARCAS:

  1. CREATE TABLE `BELLEZA`.`MARCAS` (
  2.   `id_marca` INT NOT NULL AUTO_INCREMENT,
  3.   `descripcion` VARCHAR(100) NOT NULL,
  4.   PRIMARY KEY (`id_marca`));

Táboa PERFUMES:

  1. CREATE TABLE `PERFUMES` (
  2.   `id_perfume` int(11) NOT NULL AUTO_INCREMENT,
  3.   `descripcion` varchar(45) COLLATE utf8_spanish2_ci NOT NULL,
  4.   `prezo` decimal(8,2) NOT NULL,
  5.   `data_compra` date DEFAULT NULL,
  6.   `marca_id` int(11) NOT NULL,
  7.   PRIMARY KEY (`id_perfume`),
  8.   KEY `fk_PERFUMES_1_idx` (`id_perfume`),
  9.   KEY `fk_PERFUMES_MARCA_idx` (`marca_id`),
  10.   CONSTRAINT `fk_PERFUMES_MARCA` FOREIGN KEY (`marca_id`) REFERENCES `MARCAS` (`id_marca`) ON DELETE NO ACTION ON UPDATE CASCADE
  11. ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_spanish2_ci;


  • Algúns datos de exemplo:
  1. INSERT INTO `MARCAS` VALUES (1,'CHANNEL'),(2,'ADIDAS'),(3,'ARAMIS');
  1. INSERT INTO `PERFUMES` VALUES (1,'Channel 1',46.22,'2017-02-01',1),(2,'Adidas',1.33,NULL,2),(18,'Best Aramis',54.32,'0000-00-00',3);

Nota: Indicar que Laravel como xa comentamos anteriormente permite a creación de táboas usando migracións e enchendo de datos con seeders.


  • Agora en Laravel creamos os modelos correspondentes a cada táboa:
Arquivo /app/Marca.php
  1. <?php
  2.  
  3. namespace App;
  4.  
  5. use Illuminate\Database\Eloquent\Model;
  6.  
  7. class Marca extends Model
  8. {
  9.     protected $table ='MARCAS';
  10.     protected $primaryKey='id_marca';
  11.    
  12.     public $timestamps = false;
  13.    
  14.     protected $fillable =   [
  15.                                 'descipcion'
  16.                             ];
  17.     // Se queremos que certos campos non sexan enviados en formato json os poñemos na propiedade $hidden
  18.     // protected $hidden = ['password'];
  19.  
  20.    
  21.     public function perfumes(){
  22.         return $this->hasMany('App\Perfume','marca_id','id_marca');
  23.     }
  24. }


Arquivo /app/Perfume.php
  1. <?php
  2.  
  3. namespace App;
  4.  
  5. use Illuminate\Database\Eloquent\Model;
  6.  
  7. class Perfume extends Model
  8. {
  9.     protected $table ='PERFUMES';
  10.     protected $primaryKey='id_perfume';
  11.    
  12.     public $timestamps = false;
  13.    
  14.     protected $dates = ['data_compra'];
  15.     //protected $dateFormat = 'd-m-Y';
  16.  
  17.     protected $fillable =   [
  18.                                 'descipcion',
  19.                                 'prezo',
  20.                                 'data_compra',
  21.                                 'marca_id'
  22.                             ];
  23.     // Se queremos que certos campos non sexan enviados en formato json os poñemos na propiedade $hidden
  24.     // protected $hidden = ['password'];
  25.  
  26.     public function marca(){
  27.         return $this->belongsTo('App\Marca','marca_id','id_marca');
  28.     }
  29. }
  • Liña 15: Se devolvemos datos en json podemos facer uso do atributo $dateFormat para que os campos de tipo Date teñan un formato específico. O problema desta solución é que despois da un erro cando engadimos datos....???

Rutas

  • Nota: Lembrar que nun servizo non existen formularios de entrada de datos, polo que as rutas/funcións que dan acceso a ditos formularios sobran.
  • Partimos da idea de que un perfume sempre vai pertencer a unha marca e polo tanto non existe se non existe a marca.


  • As rutas que imos ter van ser:



  • Imos ter polo tanto tres controladores asociados a estas rutas:
  • MarcasController
  • MarcasPerfumesController
  • PerfumesController
Os comandos para crear ditos controladores serán:
  • Versión 5.1 ou anteriores:
  1. php artisan make:controller MarcaController
  2. php artisan make:controller PerfumeController
  3. php artisan make:controller MarcaPerfumeController
  • Versión 5.2 ou posteriores:
  1. php artisan make:controller MarcaController --resource
  2. php artisan make:controller PerfumeController --resource
  3. php artisan make:controller MarcaPerfumeController --resource


  • Editamos o arquivo /app/Http/routes.php
  1. Route::resource('marcas','MarcasController',
  2.                 ['only' => ['store', 'show', 'update', 'destroy','index']]);
  3.  
  4. Route::resource('marcas.perfumes','MarcasPerfumesController',
  5.                 ['only' => ['store', 'update', 'index', 'destroy','']]);
  6.  
  7. Route::resource('perfumes','PerfumesController',
  8.                 ['only' => ['index', 'show']]);


Nota: Información sobre as rutas neste enlace.


Importante: A partires de Laravel 5.3 as rutas do servizo REST deberían gardarse no arquivo routes/api.php e as rutas 'normais' no arquivo routes/web.php.
Ao facelo desta forma, se optimizan os recursos empregados por Laravel para atender as peticións (nas peticións normais se están aplicando filtros para inicializar a sesión, as cookies, os bindings e a protección CSRF).
Se queremos acceder a unha ruta que se atopa definida en api.php, teremos que empregar a palabra api na ruta desta forma: http://meusitio.es/api/marcas/{marcas}.


  • Unha vez creadas as rutas podemos comprobalas có comando: php artisan route:list
Php laravel rest 1.jpg


Probando as rutas

  • Podemos facer que dende os controladores amosen unha cadea de texto para comprobar que as rutas funcionan correctamente.
Para comprobalas podemos facer uso:


  • Importante: Por defecto Laravel espera recibir un token dos formularios para evitar ataques CSRF. No caso dos servizos, estes non empregan formularios polo que desactivaremos o envío do token no arquivo /app/Http/kernel.php
  1.    ...........
  2.     protected $middleware = [
  3.         \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
  4.         \App\Http\Middleware\EncryptCookies::class,
  5.         \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
  6.         \Illuminate\Session\Middleware\StartSession::class,
  7.         \Illuminate\View\Middleware\ShareErrorsFromSession::class,
  8.        // \App\Http\Middleware\VerifyCsrfToken::class,
  9.     ];



Arquivo: /app/Http/Controller/MarcaController.php
  1. <?php
  2.  
  3. namespace App\Http\Controllers;
  4.  
  5. use Illuminate\Http\Request;
  6.  
  7. use App\Http\Requests;
  8. use App\Http\Controllers\Controller;
  9.  
  10. class MarcasController extends Controller
  11. {
  12.     /**
  13.      * Display a listing of the resource.
  14.      *
  15.      * @return \Illuminate\Http\Response
  16.      */
  17.     public function index()
  18.     {
  19.         return 'Amosamos as marcas';
  20.     }
  21.  
  22.  
  23.     /**
  24.      * Store a newly created resource in storage.
  25.      *
  26.      * @param  \Illuminate\Http\Request  $request
  27.      * @return \Illuminate\Http\Response
  28.      */
  29.     public function store(Request $request)
  30.     {
  31.         return "Creando unha nova marca";
  32.     }
  33.  
  34.     /**
  35.      * Display the specified resource.
  36.      *
  37.      * @param  int  $id
  38.      * @return \Illuminate\Http\Response
  39.      */
  40.     public function show($id)
  41.     {
  42.         return "Amosando datos da marca $id";
  43.     }
  44.  
  45.     /**
  46.      * Update the specified resource in storage.
  47.      *
  48.      * @param  \Illuminate\Http\Request  $request
  49.      * @param  int  $id
  50.      * @return \Illuminate\Http\Response
  51.      */
  52.     public function update(Request $request, $id)
  53.     {
  54.         return "Actualizando os datos da marca $id";
  55.     }
  56.  
  57.     /**
  58.      * Remove the specified resource from storage.
  59.      *
  60.      * @param  int  $id
  61.      * @return \Illuminate\Http\Response
  62.      */
  63.     public function destroy($id)
  64.     {
  65.         return "Eliminando marca $id";
  66.     }
  67. }


Arquivo: /app/Http/Controller/MarcaPerfumeController.php

  1. <?php
  2.  
  3. namespace App\Http\Controllers;
  4.  
  5. use Illuminate\Http\Request;
  6.  
  7. use App\Http\Requests;
  8. use App\Http\Controllers\Controller;
  9.  
  10. class MarcasPerfumesController extends Controller
  11. {
  12.     /**
  13.      * Display a listing of the resource.
  14.      *
  15.      * @return \Illuminate\Http\Response
  16.      */
  17.     public function index($idMarca)
  18.     {
  19.         return "Amosando os perfumes da marca $idMarca";
  20.     }
  21.  
  22.  
  23.     /**
  24.      * Store a newly created resource in storage.
  25.      *
  26.      * @param  \Illuminate\Http\Request  $request
  27.      * @return \Illuminate\Http\Response
  28.      */
  29.     public function store(Request $request,$idMarca)
  30.     {
  31.         return "Se crea un novo perfume da marca $idMarca";
  32.     }
  33.  
  34.  
  35.  
  36.     /**
  37.      * Update the specified resource in storage.
  38.      *
  39.      * @param  \Illuminate\Http\Request  $request
  40.      * @param  int  $id
  41.      * @return \Illuminate\Http\Response
  42.      */
  43.     public function update(Request $request, $idMarca,$idPerfume)
  44.     {
  45.         return "Se actualizan os datos do perfume $idPerfume da marca $idMarca";
  46.     }
  47.  
  48.     /**
  49.      * Remove the specified resource from storage.
  50.      *
  51.      * @param  int  $id
  52.      * @return \Illuminate\Http\Response
  53.      */
  54.     public function destroy($idMarca,$idPerfume)
  55.     {
  56.         return "Se elimina perfume $idPerfume da marca $idMarca";
  57.     }
  58. }


Arquivo: /app/Http/Controller/PerfumeController.php

  1. <?php
  2.  
  3. namespace App\Http\Controllers;
  4.  
  5. use Illuminate\Http\Request;
  6.  
  7. use App\Http\Requests;
  8. use App\Http\Controllers\Controller;
  9.  
  10. class PerfumesController extends Controller
  11. {
  12.     /**
  13.      * Display a listing of the resource.
  14.      *
  15.      * @return \Illuminate\Http\Response
  16.      */
  17.     public function index()
  18.     {
  19.         return "Amósanse todos os perfumes";
  20.     }
  21.  
  22.  
  23.     /**
  24.      * Display the specified resource.
  25.      *
  26.      * @param  int  $id
  27.      * @return \Illuminate\Http\Response
  28.      */
  29.     public function show($idPerfume)
  30.     {
  31.         return "Amosa os datos do perfume $idPerfume";
  32.     }
  33.  
  34. }


  • Exemplos para probar as rutas:
Comando:
  1. curl -i "http://localhost:8000/marcas"
Resultado:
  1. HTTP/1.1 200 OK
  2. Host: localhost:8000
  3. Connection: close
  4. X-Powered-By: PHP/5.5.9-1ubuntu4.19
  5. Cache-Control: no-cache
  6. Date: Mon, 17 Apr 2017 09:25:34 GMT
  7. Content-Type: text/html; charset=UTF-8
  8. Set-Cookie: laravel_session=eyJpdiI6IjIrbzRrS08yaU51bjVwdk1WY2VrcGc9PSIsInZhbHVlIjoiQmhUVU9SdVhKVzBVckNqUitJV0JlR2tSNkkydWFmY2hGSWdmWUV0Q053MlV1QTVyb21FcG1tMDFveGREaDZmTk55Nnk1VDNkclBCQng5OExXM1ZnQmc9PSIsIm1hYyI6Ijc2YWQ5YTY0MmZjOWRkZjRmNTU4ZTllNWNjZTA0NjRkZGUwMmEzNTIyNjhiMmJkMTBiZWMzYzczYTQ0Zjk2ZGEifQ%3D%3D; expires=Mon, 17-Apr-2017 11:25:34 GMT; Max-Age=7200; path=/; httponly
  9.  
  10. Amosamos as marcas


Comando:
  1. curl -i -H "Accept: application/json" -X POST http://localhost:8000/marcas
Resultado:
  1. HTTP/1.1 200 OK
  2. Host: localhost:8000
  3. Connection: close
  4. X-Powered-By: PHP/5.5.9-1ubuntu4.19
  5. Cache-Control: no-cache
  6. Date: Mon, 17 Apr 2017 09:26:43 GMT
  7. Content-Type: text/html; charset=UTF-8
  8. Set-Cookie: laravel_session=eyJpdiI6InYrZXU0Y1BsbGM0NGdWR212aUNlQXc9PSIsInZhbHVlIjoiNG1QM0lEaG02QmFkS2p1XC9UZzZ6R2dCT2FxUFwvbURSNWNJN21ibUpvZzFvWVE1b3JBaFB4a0F3ZjFIc0NVZzNPVWxOSmhsT2wwdnFIdTJhck5PeHBCdz09IiwibWFjIjoiODg0YTNjOTQ2NjAyOTNhNTI0OGI5MjZkYjIwZWU0YmJiMTM1ODFmMDkzNTU3MmZjMzg5OGY3NWE0NzNjZDlkZiJ9; expires=Mon, 17-Apr-2017 11:26:43 GMT; Max-Age=7200; path=/; httponly
  9.  
  10. Creando unha nova marca

Devolvendo datos json

  • Agora é o momento de implementar o código dos controladores para que devolvan en formato json os datos a quen faga uso da API REST que estamos a implementar.
Soamente temos que chamar á función toJson para converter os datos a dito modelo da forma:
  1. $jsonPerfumes = Perfumes::all()->toJson();
Unha vez convertido temos que enviar a resposta ao cliente.
Isto o faremos có obxecto response da forma: response()->json(['variable'=>'valor']);
Desta forma convirte a json.
Poderíamos converter os datos primeiro a json da forma:
$marcasjson= Marca::all()->toJson();
Pero iso xa o fai o response...
Se ademais queremos devolver un código de resposta HTTP o poñeremos ao final: response()->json(['variable'=>'valor','status'=>'ok'],200);
Máis información neste enlace.
Todas as respostas e formatos deste exercicio están seguindo as normas indicadas neste enlace.


  • Arquivo app/Http/Controller/MarcasController.php
  1. <?php
  2.  
  3. namespace App\Http\Controllers;
  4.  
  5. use Illuminate\Http\Request;
  6.  
  7. use App\Http\Requests;
  8. use App\Http\Controllers\Controller;
  9.  
  10. use App\Marca;
  11. use Validator;
  12.  
  13. class MarcasController extends Controller
  14. {
  15.     /**
  16.      * Display a listing of the resource.
  17.      *
  18.      * @return \Illuminate\Http\Response
  19.      */
  20.     public function index()
  21.     {
  22.        
  23.         return response()->json(['status'=>'ok','data'=>Marca::all()],200);
  24.     }
  25.  
  26.  
  27.     /**
  28.      * Store a newly created resource in storage.
  29.      *
  30.      * @param  \Illuminate\Http\Request  $request
  31.      * @return \Illuminate\Http\Response
  32.      */
  33.     public function store(Request $request)
  34.     {
  35.         if(!$request->input('descripcion')){
  36.             return response()->json(['errors'=>['status'=>'422','title'=>'Datos incompletos','detail'=>'Falta a descripcion da marca']],400);            
  37.         }
  38.         $novaMarca = new Marca;
  39.         $novaMarca->descripcion=$request->input("descripcion");
  40.         $novaMarca->save();
  41.  
  42.         return response()->json(['status'=>'ok','data'=>$novaMarca],201)->header('Location', 'http://www.dominio.es/marcas/'.$novaMarca->id_marca)->header('Content-Type', 'application/json');
  43.     }
  44.  
  45.     /**
  46.      * Display the specified resource.
  47.      *
  48.      * @param  int  $id
  49.      * @return \Illuminate\Http\Response
  50.      */
  51.     public function show($idMarca)
  52.     {
  53.         $marca = Marca::find($idMarca);
  54.         if (!$marca){
  55.             return response()->json(['errors'=>['status'=>'404','title'=>'Marca non atopada']],404);
  56.         }
  57.        
  58.         return response()->json(['status'=>'ok','data'=>$marca],200);
  59.     }
  60.  
  61.     /**
  62.      * Update the specified resource in storage.
  63.      *
  64.      * @param  \Illuminate\Http\Request  $request
  65.      * @param  int  $id
  66.      * @return \Illuminate\Http\Response
  67.      */
  68.     public function update(Request $request, $idMarca)
  69.     {
  70.         // Validamos os datos que nos chegan:
  71.         $validator = Validator::make($request->all(),[
  72.             'descripcion' => 'required|min:1|max:100'
  73.         ]);
  74.         if ($validator->fails()){
  75.            return response()->json(['errors'=>['status'=>'422','title'=>'Datos incompletos','detail'=>'Falta a descripcion da marca ou ten mais de 100 caracteres']],400);            
  76.         }
  77.        
  78.         // Pode vir polo método PUT ou polo método PATCH
  79.         // Non imos facer distinción.
  80.         $modificado=false;
  81.         $marcaModificar = Marca::find($idMarca);
  82.         if (!$marcaModificar){
  83.             return response()->json(['errors'=>['status'=>'404','title'=>'Marca non atopada']],404);
  84.         }
  85.         if($request->has('descripcion')){
  86.             $modificado=true;
  87.             $marcaModificar->descripcion=$request->input('descripcion');
  88.         }
  89.        
  90.         // Non se modificou nada. Neste caso non pode darse o caso xa que obrigamos a enviar a descripcion no caso de modificación. Pero se tivésemos máis campos, teríamos que quitar o atribute required.
  91.         if(!$modificado) {
  92.             return response()->json(['errors'=>['status'=>'304','title'=>'Marca non modificada']],304);
  93.         }
  94.        
  95.         $marcaModificar->save();
  96.         return response()->json(['status'=>'ok'],200);
  97.        
  98.     }
  99.  
  100.     /**
  101.      * Remove the specified resource from storage.
  102.      *
  103.      * @param  int  $id
  104.      * @return \Illuminate\Http\Response
  105.      */
  106.     public function destroy($idMarca)
  107.     {
  108.         $marcaBorrar = Marca::find($idMarca);
  109.         if (!$marcaBorrar){
  110.             return response()->json(['errors'=>['status'=>'404','title'=>'Marca non atopada']],404);
  111.         }
  112.        
  113.         // Comprobamos se temos perfumes asociados á marca
  114.         if($marcaBorrar->perfumes && $marcaBorrar->perfumes->count()>0){
  115.             return response()->json(['errors'=>['status'=>'409','title'=>'Existen perfumes asociados']],409);
  116.         }
  117.        
  118.         $marcaBorrar->delete();
  119.         return response()->json('',204);
  120.        
  121.        
  122.     }
  123. }


  • Arquivo app/Http/Controller/PerfumesController.php
  1. <?php
  2.  
  3. namespace App\Http\Controllers;
  4.  
  5. use Illuminate\Http\Request;
  6.  
  7. use App\Http\Requests;
  8. use App\Http\Controllers\Controller;
  9. use App\Perfume;
  10.  
  11. class PerfumesController extends Controller
  12. {
  13.     /**
  14.      * Display a listing of the resource.
  15.      *
  16.      * @return \Illuminate\Http\Response
  17.      */
  18.     public function index()
  19.     {
  20.         return response()->json(['status'=>'ok','data'=>Perfumes::all()],200);    }
  21.  
  22.  
  23.     /**
  24.      * Display the specified resource.
  25.      *
  26.      * @param  int  $id
  27.      * @return \Illuminate\Http\Response
  28.      */
  29.     public function show($idPerfume)
  30.     {
  31.         $perfume=Perfume::find($idPerfume);
  32.         if (!$perfume){
  33.             return response()->json(['errors'=>['status'=>'404','title'=>'Perfume non atopado']],404);
  34.         }
  35.        
  36.         return response()->json(['status'=>'ok','data'=>$perfume],200);
  37.     }
  38.  
  39. }



  • Arquivo app/Http/Controller/MarcasPerfumesController.php
  1. <?php
  2.  
  3. namespace App\Http\Controllers;
  4.  
  5. use Illuminate\Http\Request;
  6.  
  7. use App\Http\Requests;
  8. use App\Http\Controllers\Controller;
  9.  
  10. use App\Marca;
  11. use App\Perfume;
  12. use Validator;
  13.  
  14. class MarcasPerfumesController extends Controller
  15. {
  16.     // Formato dd/mm/YYYY => devuelve YYYY-mm-dd
  17.     function cambiaf_a_mysql($fecha){
  18.        
  19.         if (empty($fecha)) return null;
  20.        
  21.         $numeroDia = substr($fecha,0,2);
  22.         $mes = substr($fecha,3,2);
  23.         $anio = substr($fecha,6,4);
  24.         return $anio . '-' . $mes . '-' . $numeroDia;
  25.     }
  26.    
  27.    
  28.     /**
  29.      * Display a listing of the resource.
  30.      *
  31.      * @return \Illuminate\Http\Response
  32.      */
  33.     public function index($idMarca)
  34.     {
  35.         $marca = Marca::find($idMarca);
  36.         if (!$marca){
  37.             return response()->json(['errors'=>['status'=>'404','title'=>'Marca non atopada']],404);
  38.         }
  39.        
  40.         $perfumes = $marca->perfumes()->get();
  41.         return response()->json(['status'=>'ok','data'=>$perfumes],200);
  42.     }
  43.  
  44.  
  45.     /**
  46.      * Store a newly created resource in storage.
  47.      *
  48.      * @param  \Illuminate\Http\Request  $request
  49.      * @return \Illuminate\Http\Response
  50.      */
  51.     public function store(Request $request,$idMarca)
  52.     {
  53.         // Validamos os datos que nos chegan:
  54.         $validator = Validator::make($request->all(),[
  55.             'descripcion' => 'required|max:45',
  56.             'prezo' => 'required|regex:/[0-9]{1,3},[0-9]{2}/',
  57.             'data_compra' => 'date_format:"d/m/Y"'
  58.         ]);
  59.         if ($validator->fails()){
  60.            return response()->json(['errors'=>['status'=>'422','title'=>'Datos incompletos','detail'=>'Faltan datos ou con formato incorrecto: descripcion, prezo, data_compra']],400);            
  61.         }
  62.  
  63.         // Comprobamos se a marca existe
  64.         $marca = Marca::find($idMarca);
  65.         if (!$marca){
  66.             return response()->json(['errors'=>['status'=>'404','title'=>'Marca non atopada']],404);
  67.         }
  68.  
  69.         //SE NON TIVISEMOS QUE FORMATEAR OS DATOS PODERÍAMOS FACELO ASÍ:
  70.         //   $marca->perfumes()->create($request->all());
  71.         //  pero desta forma teríamos que enviar o id da marca tamén como dato e comprobar que sexa o mesmo que $idMarca
  72.         $novoPerfume=new Perfume;
  73.         $novoPerfume->descripcion=$request->input('descripcion');
  74.         $novoPerfume->prezo=str_replace(',','.',$request->input('prezo'));
  75.         $novoPerfume->data_compra=$this->cambiaf_a_mysql($request->input('data_compra'));
  76.         $novoPerfume->marca_id=$idMarca;
  77.         $novoPerfume->save();
  78.        
  79.         return response()->json(['status'=>'ok','data'=>$novoPerfume],201)->header('Location', 'http://www.dominio.es/perfumes/' . $novoPerfume->id_perfume)->header('Content-Type', 'application/json');
  80.  
  81.     }
  82.  
  83.  
  84.  
  85.     /**
  86.      * Update the specified resource in storage.
  87.      *
  88.      * @param  \Illuminate\Http\Request  $request
  89.      * @param  int  $id
  90.      * @return \Illuminate\Http\Response
  91.      */
  92.     public function update(Request $request, $idMarca,$idPerfume)
  93.     {
  94.         // Validamos os datos que nos chegan:
  95.         $validator = Validator::make($request->all(),[
  96.             'descripcion' => 'min:1|max:45',
  97.             'prezo' => 'regex:/[0-9]{1,3},[0-9]{2}/',
  98.             'data_compra' => 'date_format:"d/m/Y"',
  99.             'marca_id' => 'integer'
  100.         ]);
  101.         if ($validator->fails()){
  102.            return response()->json(['errors'=>['status'=>'422','title'=>'Datos incompletos','detail'=>'Faltan datos ou con formato incorrecto: descripcion, prezo, data_compra']],400);            
  103.         }
  104.         if($request->has('marca_id')){
  105.             // Comprobamos se a marca nova existe
  106.             $marcadestino = Marca::find($request->input('marca_id'));
  107.             if (!$marcadestino){
  108.                 return response()->json(['errors'=>['status'=>'422','title'=>'Datos incompletos','detail'=>'A marca non existe']],400);            
  109.             }
  110.         }
  111.  
  112.        
  113.         // Comprobamos se a marca existe
  114.         $marca = Marca::find($idMarca);
  115.         if (!$marca){
  116.             return response()->json(['errors'=>['status'=>'404','title'=>'Marca non atopada']],404);
  117.         }
  118.         // Comprobamos se o perfume existe
  119.         $perfume=$marca->perfumes()->find($idPerfume);
  120.         if (!$perfume){
  121.             return response()->json(['errors'=>['status'=>'404','title'=>'Perfume non atopado']],404);
  122.         }
  123.  
  124.         $modificado=false;
  125.         if($request->has('descripcion')){
  126.             $perfume->descripcion=$request->input('descripcion');
  127.             $modificado=true;
  128.         }
  129.         if($request->has('prezo')){
  130.             $perfume->prezp=str_replace(',','.',$request->input('prezo'));
  131.             $modificado=true;
  132.         }
  133.         if($request->has('data_compra')){
  134.             $perfume->data_compra=$this->cambiaf_a_mysql($request->input('data_compra'));
  135.             $modificado=true;
  136.         }
  137.         if($request->has('marca_id')){
  138.             $perfume->marca_id=$request->input('marca_id');
  139.             $modificado=true;
  140.         }
  141.  
  142.         if(!$modificado) {
  143.             return response()->json(['errors'=>['status'=>'304','title'=>'Marca non modificada']],304);
  144.         }
  145.        
  146.         $perfume->save();
  147.         return response()->json(['status'=>'ok','data'=>$perfume],200);      
  148.     }
  149.  
  150.     /**
  151.      * Remove the specified resource from storage.
  152.      *
  153.      * @param  int  $id
  154.      * @return \Illuminate\Http\Response
  155.      */
  156.     public function destroy($idMarca,$idPerfume)
  157.     {
  158.         $marca = Marca::find($idMarca);
  159.         if (!$marca){
  160.             return response()->json(['errors'=>['status'=>'404','title'=>'Marca non atopada']],404);
  161.         }
  162.  
  163.         // Comprobamos se o perfume existe
  164.         $perfumeBorrar=$marca->perfumes()->find($idPerfume);
  165.         if (!$perfumeBorrar){
  166.             return response()->json(['errors'=>['status'=>'404','title'=>'Perfume non atopado']],404);
  167.         }
  168.        
  169.        
  170.         //$perfumeBorrar->delete();
  171.         return response()->json('',204);
  172.     }
  173. }


Autenticar

Usar un servizo externo

* Comando para instalación: composer require guzzlehttp/guzzle


Exemplo de código

  • Neste código imos amosar como facer unha chamada a unha API Externa.
  1. ........
  2. use \GuzzleHttp\Client;
  3.  
  4. class ApiExternaController extends Controller
  5. {
  6.  
  7.     public function index()
  8.     {
  9.         $client = new \GuzzleHttp\Client();
  10.         $res = $client->get('https://XXXX.XXXXX.XX/api/XXXXXXXX/XXXXXX/json?key=XXXXXXXXXXXXXX&query=parametro&language=es');
  11.         echo $res->getStatusCode(); // 200
  12.         $datos = json_decode($res->getBody(),true);
  13.         var_dump($datos);
  14.     }
  15.     ..................
  • Liña 3: Temos que facer uso da clase Client de GuzzleHttp.
  • Liña 10: Facemos a petición a API Externa. Fixarse que normalmente teredes que enviar algún tipo de autenticación. Neste caso enviamos unha API Key xa que o servizo pertence a Google.
Dita API espera recibir na URL o formato de saída (xml ou json), e tamén pode levar parámetros como uha cadea de busca ou a linguaxe do resultado.
  • Liña 11: Podemos obter o código de erro devolto pola API (200 é correcto como xa vimos anteriormente).
  • Liña 12: Utilizamos a función json_decode có parámetro a true para que devolva os datos como un array asociativo.


  • O var_dump dará como resultado:
  1. array (size=4)
  2.   'html_attributions' =>
  3.     array (size=0)
  4.       empty
  5.   'next_page_token' => string 'CvQB5QAAAB-Q718qEtEfzZnccxK0THwIjfCrwGbkVsvEoQaiGyMWBj6Z3-DW1_9UWJaKANfpAahtoMP7AEjcb1JzLzRvJa9gBIueMXhxGzSPhHwKXxiI6Gp2NVQUHRcr0wW2VgAmVHKCGYid01wI8C_r0SaD3iHIB-0Fr5h2IRy4DsmjN6L9oJMaOQTBtAgCN7sCH6imRmMd2lq5XZW9pWHA3JeOAJt4HhKb34X8fjNNKgiIvOi0XPPcRPoIYJ-NcphGShTgA_Fhnl02vyqnn8mbzGMA9xU4yBObOx-cfd5qMKHYLdb5p0L6ctNa_SIV5YM_YUSFwRIQthy2TYyPTmUNwbBfFDMP5xoUPNoKN8AtwpDHFVgOb44DjIBGffU' (length=383)
  6.   'results' =>
  7.     array (size=20)
  8.       0 =>
  9.         array (size=10)
  10.           'formatted_address' => string 'Rúa María, 2, 15402 Ferrol, A Coruña, España' (length=48)
  11.           'geometry' =>
  12.             array (size=2)
  13.               ...
  14.           'icon' => string 'https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png' (length=67)
  15.           'id' => string '7c7088e7c654ba30efa1e384e0881bebee8cf065' (length=40)
  16.           'name' => string 'Asador Gavia' (length=12)
  17.           'photos' =>
  18.             array (size=1)
  19.               ...
  20.           'place_id' => string 'ChIJ22OacwV2Lg0RLObpSmq3D6k' (length=27)
  21.           'rating' => float 4.2
  22.           'reference' => string 'CmRSAAAAG-LM_2yc9TTbC8l4HoczU2OPnFPs6eYHzoLwJBwGeSiFT9XvalSDcOb0bLZXHDxp3tSAswl57eXpcZGvAIDiYfckbnl7eIxhLl722qHdn4qv-py6_umtqt2pvzoWGC3EEhA8MgKa1gD9lEzOD_EGSqlgGhT8YjPOdYP3d9C9oB9IdHEr92yvEQ' (length=190)
  23.           'types' =>
  24.             array (size=4)
  25.               ...
  26.       1 =>
  27.         array (size=10)
  28.           'formatted_address' => string 'Lugar Bosque, 73 - Bajo, 15405 Ferrol, A Coruña, España' (length=57)
  29.           'geometry' =>
  30.             array (size=2)
  31.               ...
  32.           'icon' => string 'https://maps.gstatic.com/mapfiles/place_api/icons/restaurant-71.png' (length=67)
  33.           'id' => string 'd0d3a7fb96efe558e00268ba18bf7c9f7e003d67' (length=40)
  34.           'name' => string 'Medulio' (length=7)
  35.           'photos' =>
  36.             array (size=1)
  37.               ...
  38.           'place_id' => string 'ChIJI7bO3KPYLQ0R5CVGKcTigTg' (length=27)
  39.           'rating' => float 4.2
  40.           'reference' => string 'CmRRAAAA_pB5EDe4puf3vqpGI2ACBvg5C5AchtIyS306-m7j_TBzbYsN9KNz8zU-W2u7qhPh1WptaIC5La2JXJxOeGfIlqekxmtKF27PQwz8q503Nbizc7sNH7OKcIhbKm_rssA1EhC9ikBsyUqi3ruykyou0NHGGhTpsMBsUvdbBYkcutfmME-AuOwzWA' (length=190)
  41.           'types' =>
  42.             array (size


  • Liña 6: A nos interésanos a clave 'results', polo tanto: $datos['results'] (que é un array asociativo) accederemos ao conxunto de resultados. Soamente teremos que percorrer dito array e por cada elemento acceder ás propiedades que nos interesen ($dato['name'] ou $dato['formatted_address'] por exemplo).
Esta información sería gardada nun array que será enviado á vista para que amose o resultado.



-- Ángel D. Fernández González -- (2017).