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

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