Nota: Quer entender mais sobre roteamento? Confira a página "por que um framework?" para uma explicação mais detalhada.
O roteamento básico no Flight é feito combinando um padrão de URL com uma função de retorno de chamada ou um array de uma classe e método.
Flight::route('/', function(){ echo 'olá mundo!'; });
As rotas são combinadas na ordem em que são definidas. A primeira rota que corresponder a uma solicitação será invocada.
O retorno de chamada pode ser qualquer objeto que seja chamável. Então você pode usar uma função regular:
function hello() { echo 'olá mundo!'; } Flight::route('/', 'hello');
Você também pode usar um método estático de uma classe:
class Greeting { public static function hello() { echo 'olá mundo!'; } } Flight::route('/', [ 'Greeting','hello' ]);
Ou criando um objeto primeiro e depois chamando o método:
// Greeting.php class Greeting { public function __construct() { $this->name = 'John Doe'; } public function hello() { echo "Olá, {$this->name}!"; } } // index.php $greeting = new Greeting(); Flight::route('/', [ $greeting, 'hello' ]); // Você também pode fazer isso sem criar o objeto primeiro // Nota: Nenhum argumento será injetado no construtor Flight::route('/', [ 'Greeting', 'hello' ]); // Além disso, você pode usar esta sintaxe mais curta Flight::route('/', 'Greeting->hello'); // ou Flight::route('/', Greeting::class.'->hello');
Se você quiser usar injeção de dependência via um container (PSR-11, PHP-DI, Dice, etc), o único tipo de rotas onde isso está disponível é criar o objeto você mesmo e usar o container para criar seu objeto ou você pode usar strings para definir a classe e método a serem chamados. Você pode acessar a página Injeção de Dependência para mais informações.
Aqui está um exemplo rápido:
use flight\database\PdoWrapper; // Greeting.php class Greeting { protected PdoWrapper $pdoWrapper; public function __construct(PdoWrapper $pdoWrapper) { $this->pdoWrapper = $pdoWrapper; } public function hello(int $id) { // faça algo com $this->pdoWrapper $name = $this->pdoWrapper->fetchField("SELECT name FROM users WHERE id = ?", [ $id ]); echo "Olá, mundo! Meu nome é {$name}!"; } } // index.php // Configure o container com quaisquer parâmetros que você precisar // Consulte a página de Injeção de Dependência para mais informações sobre PSR-11 $dice = new \Dice\Dice(); // Não se esqueça de reatribuir a variável com '$dice = '!!!!! $dice = $dice->addRule('flight\database\PdoWrapper', [ 'shared' => true, 'constructParams' => [ 'mysql:host=localhost;dbname=test', 'root', 'senha' ] ]); // Registre o manipulador do container Flight::registerContainerHandler(function($class, $params) use ($dice) { return $dice->create($class, $params); }); // Rotas como normal Flight::route('/hello/@id', [ 'Greeting', 'hello' ]); // ou Flight::route('/hello/@id', 'Greeting->hello'); // ou Flight::route('/hello/@id', 'Greeting::hello'); Flight::start();
Por padrão, os padrões de rota são combinados com todos os métodos de solicitação. Você pode responder a métodos específicos colocando um identificador antes da URL.
Flight::route('GET /', function () { echo 'Recebi uma solicitação GET.'; }); Flight::route('POST /', function () { echo 'Recebi uma solicitação POST.'; }); // Você não pode usar Flight::get() para rotas, pois esse é um método // para obter variáveis, não criar uma rota. // Flight::post('/', function() { /* código */ }); // Flight::patch('/', function() { /* código */ }); // Flight::put('/', function() { /* código */ }); // Flight::delete('/', function() { /* código */ });
Você também pode mapear múltiplos métodos para um único retorno de chamada usando um delimitador |:
|
Flight::route('GET|POST /', function () { echo 'Recebi uma solicitação de GET ou POST.'; });
Além disso, você pode obter o objeto Router que possui alguns métodos auxiliares para você usar:
$router = Flight::router(); // mapeia todos os métodos $router->map('/', function() { echo 'olá mundo!'; }); // solicitação GET $router->get('/users', function() { echo 'usuários'; }); // $router->post(); // $router->put(); // $router->delete(); // $router->patch();
Você pode usar expressões regulares em suas rotas:
Flight::route('/user/[0-9]+', function () { // Isso corresponderá a /user/1234 });
Embora esse método esteja disponível, é recomendado usar parâmetros nomeados, ou parâmetros nomeados com expressões regulares, pois são mais legíveis e mais fáceis de manter.
Você pode especificar parâmetros nomeados em suas rotas que serão passados para sua função de retorno de chamada. Isso é mais para a legibilidade da rota do que qualquer outra coisa. Por favor, veja a seção abaixo sobre a importante caveat.
Flight::route('/@name/@id', function (string $name, string $id) { echo "olá, $name ($id)!"; });
Você também pode incluir expressões regulares com seus parâmetros nomeados usando o delimitador ::
:
Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) { // Isso corresponderá a /bob/123 // Mas não corresponderá a /bob/12345 });
Nota: Grupos regex correspondentes () com parâmetros posicionais não são suportados. :'(
()
Embora no exemplo acima, pareça que @name está diretamente ligado à variável $name, não está. A ordem dos parâmetros na função de retorno de chamada é o que determina o que é passado para ela. Portanto, se você mudar a ordem dos parâmetros na função de retorno de chamada, as variáveis também serão trocadas. Aqui está um exemplo:
@name
$name
Flight::route('/@name/@id', function (string $id, string $name) { echo "olá, $name ($id)!"; });
E se você fosse para a seguinte URL: /bob/123, a saída seria olá, 123 (bob)!. Por favor, tenha cuidado ao configurar suas rotas e suas funções de retorno de chamada.
/bob/123
olá, 123 (bob)!
Você pode especificar parâmetros nomeados que são opcionais para correspondência envolvendo segmentos entre parênteses.
Flight::route( '/blog(/@year(/@month(/@day)))', function(?string $year, ?string $month, ?string $day) { // Isso corresponderá às seguintes URLs: // /blog/2012/12/10 // /blog/2012/12 // /blog/2012 // /blog } );
Quaisquer parâmetros opcionais que não corresponderem serão passados como NULL.
NULL
A correspondência é feita apenas em segmentos de URL individuais. Se você quiser combinar múltiplos segmentos, pode usar o coringa *.
*
Flight::route('/blog/*', function () { // Isso corresponderá a /blog/2000/02/01 });
Para direcionar todas as solicitações para um único retorno de chamada, você pode fazer:
Flight::route('*', function () { // Faça algo });
Você pode passar a execução para a próxima rota correspondente retornando true de sua função de retorno de chamada.
true
Flight::route('/user/@name', function (string $name) { // Verifique alguma condição if ($name !== "Bob") { // Continue para a próxima rota return true; } }); Flight::route('/user/*', function () { // Isso será chamado });
Você pode atribuir um alias a uma rota, para que a URL possa ser gerada dinamicamente mais tarde em seu código (como um modelo, por exemplo).
Flight::route('/users/@id', function($id) { echo 'usuário:'.$id; }, false, 'user_view'); // depois em algum lugar no código Flight::getUrl('user_view', [ 'id' => 5 ]); // retornará '/users/5'
Isso é especialmente útil se sua URL mudar. No exemplo acima, suponha que os usuários foram movidos para /admin/users/@id em vez disso. Com o alias em prática, você não precisa mudar em nenhum lugar onde você referenciar o alias porque o alias agora retornará /admin/users/5, como no exemplo acima.
/admin/users/@id
/admin/users/5
O alias da rota ainda funciona em grupos também:
Flight::group('/users', function() { Flight::route('/@id', function($id) { echo 'usuário:'.$id; }, false, 'user_view'); }); // depois em algum lugar no código Flight::getUrl('user_view', [ 'id' => 5 ]); // retornará '/users/5'
Se você quiser inspecionar as informações da rota correspondente, pode solicitar que o objeto de rota seja passado para seu retorno de chamada passando true como o terceiro parâmetro no método de rota. O objeto de rota sempre será o último parâmetro passado para sua função de retorno de chamada.
Flight::route('/', function(\flight\net\Route $route) { // Array de métodos HTTP correspondentes $route->methods; // Array de parâmetros nomeados $route->params; // Expressão regular correspondente $route->regex; // Contém o conteúdo de qualquer '*' usado no padrão de URL $route->splat; // Mostra o caminho da URL....se você realmente precisar $route->pattern; // Mostra qual middleware está atribuído a isso $route->middleware; // Mostra o alias atribuído a esta rota $route->alias; }, true);
Pode haver momentos em que você queira agrupar rotas relacionadas (como /api/v1). Você pode fazer isso usando o método group:
/api/v1
group
Flight::group('/api/v1', function () { Flight::route('/users', function () { // Corresponde a /api/v1/users }); Flight::route('/posts', function () { // Corresponde a /api/v1/posts }); });
Você pode até aninhar grupos de grupos:
Flight::group('/api', function () { Flight::group('/v1', function () { // Flight::get() obtém variáveis, não define uma rota! Veja o contexto do objeto abaixo Flight::route('GET /users', function () { // Corresponde a GET /api/v1/users }); Flight::post('/posts', function () { // Corresponde a POST /api/v1/posts }); Flight::put('/posts/1', function () { // Corresponde a PUT /api/v1/posts }); }); Flight::group('/v2', function () { // Flight::get() obtém variáveis, não define uma rota! Veja o contexto do objeto abaixo Flight::route('GET /users', function () { // Corresponde a GET /api/v2/users }); }); });
Você ainda pode usar o agrupamento de rotas com o objeto Engine da seguinte forma:
Engine
$app = new \flight\Engine(); $app->group('/api/v1', function (Router $router) { // use a variável $router $router->get('/users', function () { // Corresponde a GET /api/v1/users }); $router->post('/posts', function () { // Corresponde a POST /api/v1/posts }); });
Você pode criar um conjunto de rotas para um recurso usando o método resource. Isso criará um conjunto de rotas para um recurso que segue as convenções RESTful.
resource
Para criar um recurso, faça o seguinte:
Flight::resource('/users', UsersController::class);
E o que acontecerá nos bastidores é que ele criará as seguintes rotas:
[ 'index' => 'GET ', 'create' => 'GET /create', 'store' => 'POST ', 'show' => 'GET /@id', 'edit' => 'GET /@id/edit', 'update' => 'PUT /@id', 'destroy' => 'DELETE /@id' ]
E seu controlador ficará assim:
class UsersController { public function index(): void { } public function show(string $id): void { } public function create(): void { } public function store(): void { } public function edit(string $id): void { } public function update(string $id): void { } public function destroy(string $id): void { } }
Nota: Você pode visualizar as novas rotas adicionadas com runway executando php runway routes.
runway
php runway routes
Existem algumas opções para configurar as rotas de recursos.
Você pode configurar o aliasBase. Por padrão, o alias é a última parte da URL especificada. Por exemplo, /users/ resultaria em um aliasBase de users. Quando essas rotas são criadas, os aliases são users.index, users.create, etc. Se você quiser mudar o alias, defina o aliasBase para o valor que deseja.
aliasBase
/users/
users
users.index
users.create
Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);
Você também pode especificar quais rotas deseja criar usando as opções only e except.
only
except
Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);
Essas são basicamente opções de listagem e blacklist para que você possa especificar quais rotas deseja criar.
Você também pode especificar middleware para ser executado em cada uma das rotas criadas pelo método resource.
Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);
Agora você pode transmitir respostas para o cliente usando o método streamWithHeaders(). Isso é útil para enviar arquivos grandes, processos longos em execução ou gerar grandes respostas. Transmitir uma rota é tratado de forma um pouco diferente de uma rota regular.
streamWithHeaders()
Nota: Transmissões de respostas só estão disponíveis se você tiver flight.v2.output_buffering definido como falso.
flight.v2.output_buffering
Você pode transmitir uma resposta para o cliente usando o método stream() em uma rota. Se você fizer isso, você deve definir todos os métodos à mão antes de emitir qualquer coisa para o cliente. Isso é feito com a função header() do php ou o método Flight::response()->setRealHeader().
stream()
header()
Flight::response()->setRealHeader()
Flight::route('/@filename', function($filename) { // obviamente você sanitizaria o caminho e outras coisas. $fileNameSafe = basename($filename); // Se você tiver cabeçalhos adicionais para definir aqui após a execução da rota // você deve defini-los antes que qualquer coisa seja ecoada. // Todos devem ser uma chamada bruta à função header() ou // uma chamada para Flight::response()->setRealHeader() header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"'); // ou Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="'.$fileNameSafe.'"'); $fileData = file_get_contents('/some/path/to/files/'.$fileNameSafe); // Captura de erros e outras coisas if(empty($fileData)) { Flight::halt(404, 'Arquivo não encontrado'); } // defina manualmente o comprimento do conteúdo, se desejar header('Content-Length: '.filesize($filename)); // Transmita os dados para o cliente echo $fileData; // Esta é a linha mágica aqui })->stream();
Você também pode usar o método streamWithHeaders() para definir os cabeçalhos antes de começar a transmitir.
Flight::route('/stream-users', function() { // você pode adicionar quaisquer cabeçalhos adicionais que desejar aqui // você deve usar header() ou Flight::response()->setRealHeader() // como você obtém seus dados, apenas como exemplo... $users_stmt = Flight::db()->query("SELECT id, first_name, last_name FROM users"); echo '{'; $user_count = count($users); while($user = $users_stmt->fetch(PDO::FETCH_ASSOC)) { echo json_encode($user); if(--$user_count > 0) { echo ','; } // Isso é exigido para enviar os dados ao cliente ob_flush(); } echo '}'; // Esta é a forma como você definirá os cabeçalhos antes de começar a transmitir. })->streamWithHeaders([ 'Content-Type' => 'application/json', 'Content-Disposition' => 'attachment; filename="users.json"', // status code opcional, padrão é 200 'status' => 200 ]);