Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Criando APIs usando o micro-framework Respect

Criando APIs usando o micro-framework Respect

Avatar for Ivan Rosolen

Ivan Rosolen

July 02, 2013
Tweet

More Decks by Ivan Rosolen

Other Decks in Technology

Transcript

  1. Ivan Rosolen Graduado em sistemas de Informação Pós-graduado em Gerência

    de Projetos Desenvolvedor a 10+ anos Autor de vários PHPT (testes para o PHP) Gerente de Projetos na Arizona Tuesday, July 2, 13
  2. "[] conjunto de rotinas e padrões estabelecidos por um software

    para a utilização das suas funcionalidades por aplicativos que não pretendem envolver-se em detalhes da implementação do software, mas apenas usar seus serviços []" Wikipedia Tuesday, July 2, 13
  3. vhost (apache) ServerName "restbeer.local" DocumentRoot "/caminho_do_projeto/restBeer/" <Directory "/caminho_do_projeto/restBeer"> Options -Indexes

    FollowSymLinks AllowOverride All Order Allow,Deny Allow from all </Directory> CustomLog /caminho_dos_logs/restbeer-access_log combined ErrorLog /caminho_dos_logs/restbeer-error_log Tuesday, July 2, 13
  4. .htaccess RewriteEngine On # Redirect all requests not pointing at

    an actual file to index.php RewriteCond %{REQUEST_FILENAME} !-f RewriteRule . index.php [L] Tuesday, July 2, 13
  5. composer.json { "name": "RestBeer", "authors": [ { "name": "Ivan Rosolen",

    "email": "[email protected]" } ], "require": { "respect/rest": "0.5.x", "respect/relational": "0.5.x", "respect/config": "0.3.x", "respect/validation": "0.4.x" } } Tuesday, July 2, 13
  6. ‣ Apenas arquivos .INI ‣ Usa o mesmo parser nativo

    e rápido do php.ini ‣ Extende o arquivo .INI com seu próprio “dialeto” ‣ Implementa lazy loading para instâncias de objeto ‣ Arquivo config.ini: db_name = "restbeer.db" dsn = "sqlite:[db_name]" ‣ Utilização: use Respect\Config\Container; /** * Ler arquivo de configuração */ $config = new Container('config.ini'); echo $config->dsn; // sqlite:restbeer.db ‣ http://github.com/Respect/Config Tuesday, July 2, 13
  7. ‣ Quase zero de configuracão ‣ Fluent interface: $mapper->author[7]->fetch(); ‣

    Se adapta a diferentes databases ‣ Registros são tratados como Plain Data Object ‣ Dependência: Respect\Data (http://github.com/Respect/Data) ‣ Utilização: use Respect\Relational\Mapper; // Criar instância PDO com o SQLite // diretório precisa ter permissão de escrita também o.O $mapper = new Mapper(new PDO('sqlite:database.sq3')); // buscar todos os autores $authors = $mapper->author->fetchAll(); // gravar um autor $obj = new stdClass; $obj->name = 'Ivan Rosolen'; $mapper->author->persist($obj); $mapper->flush(); ‣ http://github.com/Respect/Relational Tuesday, July 2, 13
  8. ‣ Fluent/Chained interface: v::numeric()->positive()->between(1, 256)->validate($num) ‣ Mais de 30 validadores

    testados ‣ Possibilidade de utilizar validadores Zend/Symfony se instalados ‣ Utilização: use Respect\Validation\Validator as v; // validar número simples $number = 123; v::numeric()->validate($number); //true // validar em cadeia $v = v::arr() // validar se é array ->key('nome', $rule = v::alnum()->notEmpty()->noWhitespace()) // validar a key 'nome' ->key('estilo', $rule) // utilizando a mesma regra da key de cima ->validate($_POST['cerveja']); // zend validator $hostnameValidator = v::zend('Hostname')->assert('google.com'); // symfony validator $timeValidator = v::sf('Time')->assert('22:00:01'); ‣ https://github.com/Respect/Validation Tuesday, July 2, 13
  9. ‣ Thin and lightweight controller para aplicações RESTful e APIs

    ‣ Curva de aprendizado pequena ‣ Ótima documentação em português: http://www.cssexperts.net/respect-rest-docs-br/ ‣ Utilização: use Respect\Rest\Router; // Criar instância do router $router = new Router; // raiz http://example.com/ // instância para trabalhar em uma subpasta $router = new Router('/pasta'); // raiz http://example.com/pasta // Olá mundo $router->get('/', function() { return 'Hello World'; }); ‣ https://github.com/Respect/Rest Tuesday, July 2, 13
  10. // autoload do composer require 'vendor/autoload.php'; use Respect\Rest\Router; use Respect\Config\Container;

    use Respect\Validation\Validator as v; use Respect\Relational\Mapper; use Respect\Data\Collections\Collection; /** * Ler arquivo de configuração */ $config = new Container('config.ini'); /** * Criar instância PDO com o SQLite usando as configs */ // diretório precisa ter permissão de escrita também $mapper = new Mapper(new PDO($config->dsn)); // Criar instância do router $router = new Router(); /** * Rota para qualquer tipo de request (any) */ $router->any('/', function () { return 'RestBeer!'; }); Tuesday, July 2, 13
  11. $router->get('/cervejas/*', function ($nome) use ($mapper) { if ( !isset($nome) )

    { $cervejas = $mapper->cervejas->fetchAll(); header('HTTP/1.1 200 Ok'); return $cervejas; } $nome = filter_var( $nome, FILTER_SANITIZE_FULL_SPECIAL_CHARS ); if ( v::not(v::alnum()->notEmpty())->validate($nome) ) { header('HTTP/1.1 404 Not Found'); return 'Não encontrada'; } $cerveja = $mapper->cervejas(array( 'nome' => $nome ))->fetch(); // BONUS - podemos buscar por id também // $cerveja = $mapper->cervejas[$id]->fetch(); if ( !$cerveja ) { header('HTTP/1.1 404 Not Found'); return 'Não encontrada'; } header('HTTP/1.1 200 Ok'); return $cerveja; }); Tuesday, July 2, 13
  12. $router->post('/cervejas', function () use ($mapper,$cervejas) { if ( !isset($_POST) ||

    !isset($_POST['cerveja']) || v::not(v::arr())->validate($_POST['cerveja']) ) { header('HTTP/1.1 400 Bad Request'); return 'Faltam parâmetros'; } $validation = v::arr() ->key('nome', $rule = v::alnum()->notEmpty()->noWhitespace()) ->key('estilo', $rule) ->validate($_POST['cerveja']); if ( !$validation ) { header('HTTP/1.1 400 Bad Request'); return 'Faltam parâmetros'; } $cerveja = new stdClass(); $cerveja->nome = filter_var($_POST['cerveja']['nome'], FILTER_SANITIZE_FULL_SPECIAL_CHARS); $cerveja->estilo = filter_var($_POST['cerveja']['estilo'], FILTER_SANITIZE_FULL_SPECIAL_CHARS); $check = $mapper->cervejas(array( 'nome' => $cerveja->nome ))->fetch(); if ( $check ) { header('HTTP/1.1 409 Conflict'); return 'Cerveja já existe no sistema'; } $mapper->cervejas->persist($cerveja); $mapper->flush(); if ( !isset($cerveja->id) || empty($cerveja->id) ) { header('HTTP/1.1 500 Internal Server Error'); return 'Erro ao inserir cerveja'; } header('HTTP/1.1 201 Created'); return 'Cerveja criada'; }); Tuesday, July 2, 13
  13. $router->put('/cervejas/*', function ($nome) use ($mapper) { parse_str(file_get_contents('php://input'), $data); if (

    !isset($data) || !isset($data['cerveja']) || v::not(v::arr())->validate($data['cerveja']) ) { header('HTTP/1.1 400 Bad Request'); return 'Faltam parâmetros'; } $validation = v::arr()->key('nome',$rule = v::alnum()->notEmpty()->noWhitespace()) ->key('estilo', $rule) ->validate($data['cerveja']); if ( !$validation ) { header('HTTP/1.1 400 Bad Request'); return 'Faltam parâmetros'; } $cerveja = $mapper->cervejas(array( 'nome' => $nome ))->fetch(); if ( !$cerveja ) { header('HTTP/1.1 404 Not Found'); return 'Não encontrada'; } $newNome = filter_var( $data['cerveja']['nome'], FILTER_SANITIZE_FULL_SPECIAL_CHARS ); $newEstilo = filter_var( $data['cerveja']['estilo'], FILTER_SANITIZE_FULL_SPECIAL_CHARS ); $cerveja->nome = $newNome; $cerveja->estilo = $newEstilo; $mapper->cervejas->persist($cerveja); $mapper->flush(); header('HTTP/1.1 200 Ok'); return 'Cerveja atualizada'; }); // removidas algumas verificações para ficar melhor no slide (http://github.com/ivanrosolen/RestBeer) Tuesday, July 2, 13
  14. $router->delete('/cervejas/*', function ($nome) use ($mapper) { $nome = filter_var( $nome,

    FILTER_SANITIZE_FULL_SPECIAL_CHARS ); if ( !isset($nome) || v::not(v::alnum()->notEmpty())->validate($nome) ) { header('HTTP/1.1 400 Bad Request'); return 'Faltam parâmetros'; } $cerveja = $mapper->cervejas(array( 'nome' => $nome ))->fetch(); if ( !$cerveja ) { header('HTTP/1.1 404 Not Found'); return 'Não encontrada'; } $mapper->cervejas->remove($cerveja); $mapper->flush(); header('HTTP/1.1 200 Ok'); return 'Cerveja removida'; }); Tuesday, July 2, 13
  15. $jsonRender = function ($data) { header('Content-Type: application/json'); if ( v::string()->validate($data)

    ) { $data = array($data); } return json_encode($data,true); }; $router->always('Accept', array('application/json' => $jsonRender)); Tuesday, July 2, 13
  16. // do not use this! function checkLogin($user, $pass) { return

    $user === 'admin' && $pass === 'admin'; } $router->get('/admin', function () { return 'RestBeer Admin Protected!'; })->authBasic('Secret Area', function ($user, $pass) { return checkLogin($user, $pass); }); Tuesday, July 2, 13