Routing
Catatan: Ingin memahami lebih lanjut tentang routing? Periksa halaman "mengapa sebuah framework?" untuk penjelasan yang lebih mendalam.
Routing dasar di Flight dilakukan dengan mencocokkan pola URL dengan fungsi callback atau sebuah array dari sebuah kelas dan metode.
Flight::route('/', function(){
echo 'hello world!';
});
Rute dicocokkan dalam urutan mereka didefinisikan. Rute pertama yang mencocokkan permintaan akan dipanggil.
Callback/Fungsi
Callback dapat berupa objek apa pun yang dapat dipanggil. Jadi Anda dapat menggunakan fungsi biasa:
function hello() {
echo 'hello world!';
}
Flight::route('/', 'hello');
Kelas
Anda juga dapat menggunakan metode statis dari sebuah kelas:
class Greeting {
public static function hello() {
echo 'hello world!';
}
}
Flight::route('/', [ 'Greeting','hello' ]);
Atau dengan membuat objek terlebih dahulu dan kemudian memanggil metode:
// Greeting.php
class Greeting
{
public function __construct() {
$this->name = 'John Doe';
}
public function hello() {
echo "Hello, {$this->name}!";
}
}
// index.php
$greeting = new Greeting();
Flight::route('/', [ $greeting, 'hello' ]);
// Anda juga dapat melakukan ini tanpa membuat objek terlebih dahulu
// Catatan: Tidak ada argumen yang akan disuntikkan ke konstruktor
Flight::route('/', [ 'Greeting', 'hello' ]);
// Selain itu, Anda dapat menggunakan sintaks lebih pendek ini
Flight::route('/', 'Greeting->hello');
// atau
Flight::route('/', Greeting::class.'->hello');
Dependency Injection melalui DIC (Dependency Injection Container)
Jika Anda ingin menggunakan dependency injection melalui sebuah container (PSR-11, PHP-DI, Dice, dll), satu-satunya jenis rute yang tersedia adalah langsung membuat objek sendiri dan menggunakan container untuk membuat objek Anda atau Anda dapat menggunakan string untuk mendefinisikan kelas dan metode yang akan dipanggil. Anda dapat pergi ke halaman Dependency Injection untuk informasi lebih lanjut.
Berikut adalah contoh cepat:
use flight\database\PdoWrapper;
// Greeting.php
class Greeting
{
protected PdoWrapper $pdoWrapper;
public function __construct(PdoWrapper $pdoWrapper) {
$this->pdoWrapper = $pdoWrapper;
}
public function hello(int $id) {
// lakukan sesuatu dengan $this->pdoWrapper
$name = $this->pdoWrapper->fetchField("SELECT name FROM users WHERE id = ?", [ $id ]);
echo "Hello, world! Nama saya adalah {$name}!";
}
}
// index.php
// Siapkan container dengan parameter apa pun yang Anda butuhkan
// Lihat halaman Dependency Injection untuk informasi lebih lanjut tentang PSR-11
$dice = new \Dice\Dice();
// Jangan lupa untuk menetapkan kembali variabel dengan '$dice = '!!!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
'shared' => true,
'constructParams' => [
'mysql:host=localhost;dbname=test',
'root',
'password'
]
]);
// Daftarkan pengendali container
Flight::registerContainerHandler(function($class, $params) use ($dice) {
return $dice->create($class, $params);
});
// Rute seperti biasa
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// atau
Flight::route('/hello/@id', 'Greeting->hello');
// atau
Flight::route('/hello/@id', 'Greeting::hello');
Flight::start();
Metode Routing
Secara default, pola rute dicocokkan dengan semua metode permintaan. Anda dapat merespons metode tertentu dengan menempatkan pengenal sebelum URL.
Flight::route('GET /', function () {
echo 'Saya menerima permintaan GET.';
});
Flight::route('POST /', function () {
echo 'Saya menerima permintaan POST.';
});
// Anda tidak dapat menggunakan Flight::get() untuk rute karena itu adalah metode
// untuk mendapatkan variabel, tidak membuat rute.
// Flight::post('/', function() { /* kode */ });
// Flight::patch('/', function() { /* kode */ });
// Flight::put('/', function() { /* kode */ });
// Flight::delete('/', function() { /* kode */ });
Anda juga dapat memetakan beberapa metode ke satu callback dengan menggunakan pemisah |
:
Flight::route('GET|POST /', function () {
echo 'Saya menerima baik permintaan GET atau POST.';
});
Selain itu, Anda dapat mengambil objek Router yang memiliki beberapa metode pembantu untuk Anda gunakan:
$router = Flight::router();
// memetakan semua metode
$router->map('/', function() {
echo 'hello world!';
});
// permintaan GET
$router->get('/users', function() {
echo 'users';
});
// $router->post();
// $router->put();
// $router->delete();
// $router->patch();
Ekspresi Reguler
Anda dapat menggunakan ekspresi reguler dalam rute Anda:
Flight::route('/user/[0-9]+', function () {
// Ini akan mencocokkan /user/1234
});
Meskipun metode ini tersedia, disarankan untuk menggunakan parameter bernama, atau parameter bernama dengan ekspresi reguler, karena lebih mudah dibaca dan lebih mudah untuk dipelihara.
Parameter Bernama
Anda dapat menentukan parameter bernama dalam rute Anda yang akan diteruskan ke fungsi callback Anda. Ini lebih untuk keterbacaan rute daripada yang lain . Silakan lihat bagian di bawah tentang caveat penting.
Flight::route('/@name/@id', function (string $name, string $id) {
echo "hello, $name ($id)!";
});
Anda juga dapat menyertakan ekspresi reguler dengan parameter bernama Anda menggunakan
pemisah :
:
Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
// Ini akan mencocokkan /bob/123
// Tetapi tidak akan mencocokkan /bob/12345
});
Catatan: Mencocokkan grup regex
()
dengan parameter posisi tidak didukung. :'(
Caveat Penting
Meskipun dalam contoh di atas, tampaknya @name
terikat langsung pada variabel $name
, itu tidak. Urutan parameter dalam fungsi callback yang menentukan apa yang diteruskan ke dalamnya. Jadi jika Anda membalik urutan parameter dalam fungsi callback, variabel juga akan dibalik. Berikut adalah contohnya:
Flight::route('/@name/@id', function (string $id, string $name) {
echo "hello, $name ($id)!";
});
Dan jika Anda mengunjungi URL berikut: /bob/123
, hasilnya akan menjadi hello, 123 (bob)!
.
Silakan berhati-hati saat Anda menetapkan rute dan fungsi callback Anda.
Parameter Opsional
Anda dapat menentukan parameter bernama yang bersifat opsional untuk pencocokan dengan membungkus segmen dalam tanda kurung.
Flight::route(
'/blog(/@year(/@month(/@day)))',
function(?string $year, ?string $month, ?string $day) {
// Ini akan mencocokkan URL berikut:
// /blog/2012/12/10
// /blog/2012/12
// /blog/2012
// /blog
}
);
Parameter opsional yang tidak dicocokkan akan diteruskan sebagai NULL
.
Wildcards
Pencocokan hanya dilakukan pada segmen URL individu. Jika Anda ingin mencocokkan beberapa
segmen Anda dapat menggunakan wildcard *
.
Flight::route('/blog/*', function () {
// Ini akan mencocokkan /blog/2000/02/01
});
Untuk merutekan semua permintaan ke satu callback, Anda dapat melakukan:
Flight::route('*', function () {
// Lakukan sesuatu
});
Menyampaikan
Anda dapat meneruskan eksekusi ke rute berikutnya yang cocok dengan mengembalikan true
dari
fungsi callback Anda.
Flight::route('/user/@name', function (string $name) {
// Periksa beberapa kondisi
if ($name !== "Bob") {
// Lanjutkan ke rute berikutnya
return true;
}
});
Flight::route('/user/*', function () {
// Ini akan dipanggil
});
Aliasing Rute
Anda dapat menetapkan alias ke sebuah rute, sehingga URL dapat dibuat secara dinamis dilanjutkan dalam kode Anda (seperti template misalnya).
Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
// nanti dalam kode di suatu tempat
Flight::getUrl('user_view', [ 'id' => 5 ]); // akan mengembalikan '/users/5'
Ini sangat membantu jika URL Anda kebetulan berubah. Dalam contoh di atas, katakanlah bahwa pengguna dipindahkan ke /admin/users/@id
alih-alih.
Dengan aliasing di tempat, Anda tidak perlu mengubah di mana pun Anda merujuk alias karena alias sekarang akan mengembalikan /admin/users/5
seperti dalam
contoh di atas.
Aliasing rute masih berfungsi dalam grup juga:
Flight::group('/users', function() {
Flight::route('/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
});
// nanti dalam kode di suatu tempat
Flight::getUrl('user_view', [ 'id' => 5 ]); // akan mengembalikan '/users/5'
Informasi Rute
Jika Anda ingin memeriksa informasi rute yang cocok, Anda dapat meminta objek rute
untuk diteruskan ke fungsi callback Anda dengan meneruskan true
sebagai parameter ketiga di metode rute. Objek rute akan selalu menjadi parameter terakhir yang diteruskan ke fungsi callback Anda.
Flight::route('/', function(\flight\net\Route $route) {
// Array metode HTTP yang dicocokkan
$route->methods;
// Array parameter bernama
$route->params;
// Ekspresi reguler yang cocok
$route->regex;
// Berisi konten dari setiap '*' yang digunakan dalam pola URL
$route->splat;
// Menunjukkan jalur url....jika Anda benar-benar membutuhkannya
$route->pattern;
// Menunjukkan middleware apa yang ditugaskan untuk ini
$route->middleware;
// Menunjukkan alias yang ditugaskan untuk rute ini
$route->alias;
}, true);
Pengelompokan Rute
Mungkin ada waktu ketika Anda ingin mengelompokkan rute-rute terkait bersama (seperti /api/v1
).
Anda dapat melakukan ini dengan menggunakan metode group
:
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Mencocokkan /api/v1/users
});
Flight::route('/posts', function () {
// Mencocokkan /api/v1/posts
});
});
Anda bahkan dapat menelurkan grup dari grup:
Flight::group('/api', function () {
Flight::group('/v1', function () {
// Flight::get() mendapatkan variabel, itu tidak menetapkan rute! Lihat konteks objek di bawah
Flight::route('GET /users', function () {
// Mencocokkan GET /api/v1/users
});
Flight::post('/posts', function () {
// Mencocokkan POST /api/v1/posts
});
Flight::put('/posts/1', function () {
// Mencocokkan PUT /api/v1/posts
});
});
Flight::group('/v2', function () {
// Flight::get() mendapatkan variabel, itu tidak menetapkan rute! Lihat konteks objek di bawah
Flight::route('GET /users', function () {
// Mencocokkan GET /api/v2/users
});
});
});
Pengelompokan dengan Konteks Objek
Anda masih dapat menggunakan pengelompokan rute dengan objek Engine
dengan cara berikut:
$app = new \flight\Engine();
$app->group('/api/v1', function (Router $router) {
// gunakan variabel $router
$router->get('/users', function () {
// Mencocokkan GET /api/v1/users
});
$router->post('/posts', function () {
// Mencocokkan POST /api/v1/posts
});
});
Resource Routing
Anda dapat membuat serangkaian rute untuk sebuah sumber daya menggunakan metode resource
. Ini akan membuat
serangkaian rute untuk sumber daya yang mengikuti konvensi RESTful.
Untuk membuat sumber daya, lakukan hal berikut:
Flight::resource('/users', UsersController::class);
Dan yang akan terjadi di latar belakang adalah ini akan membuat rute berikut:
[
'index' => 'GET ',
'create' => 'GET /create',
'store' => 'POST ',
'show' => 'GET /@id',
'edit' => 'GET /@id/edit',
'update' => 'PUT /@id',
'destroy' => 'DELETE /@id'
]
Dan controller Anda akan terlihat seperti ini:
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
{
}
}
Catatan: Anda dapat melihat rute yang baru ditambahkan dengan
runway
dengan menjalankanphp runway routes
.
Menyesuaikan Rute Sumber Daya
Ada beberapa opsi untuk mengonfigurasi rute sumber daya.
Alias Basis
Anda dapat mengonfigurasi aliasBase
. Secara default, alias adalah bagian terakhir dari URL yang ditentukan.
Misalnya /users/
akan menghasilkan aliasBase
menjadi users
. Ketika rute ini dibuat,
alias adalah users.index
, users.create
, dst. Jika Anda ingin mengubah alias, atur aliasBase
ke nilai yang Anda inginkan.
Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);
Hanya dan Kecuali
Anda juga dapat menentukan rute mana yang ingin Anda buat dengan menggunakan opsi only
dan except
.
Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);
Ini pada dasarnya adalah opsi daftar putih dan daftar hitam sehingga Anda dapat menentukan rute mana yang ingin Anda buat.
Middleware
Anda juga dapat menentukan middleware yang akan dijalankan di setiap rute yang dibuat oleh metode resource
.
Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);
Streaming
Anda sekarang dapat melakukan streaming respons ke klien menggunakan metode streamWithHeaders()
.
Ini berguna untuk mengirim file besar, proses yang memakan waktu lama, atau menghasilkan respons besar.
Streaming rute ditangani sedikit berbeda dari rute biasa.
Catatan: Streaming respons hanya tersedia jika Anda memiliki
flight.v2.output_buffering
disetel ke false.
Stream dengan Header Manual
Anda dapat melakukan streaming respons ke klien dengan menggunakan metode stream()
pada sebuah rute. Jika Anda
melakukan ini, Anda harus menetapkan semua metode secara manual sebelum Anda mengeluarkan apapun kepada klien.
Ini dilakukan dengan fungsi header()
php atau metode Flight::response()->setRealHeader()
.
Flight::route('/@filename', function($filename) {
// jelas Anda akan menyaring jalur dan lain-lain.
$fileNameSafe = basename($filename);
// Jika Anda memiliki header tambahan untuk diatur di sini setelah rute dieksekusi
// Anda harus mendefinisikannya sebelum ada yang di-echo keluar.
// Mereka semua harus merupakan panggilan mentah ke fungsi header()
// atau panggilan ke Flight::response()->setRealHeader()
header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
// atau
Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="'.$fileNameSafe.'"');
$fileData = file_get_contents('/some/path/to/files/'.$fileNameSafe);
// Penanganan kesalahan dan lain-lain
if(empty($fileData)) {
Flight::halt(404, 'File tidak ditemukan');
}
// set panjang konten secara manual jika Anda suka
header('Content-Length: '.filesize($filename));
// Streaming data ke klien
echo $fileData;
// Ini adalah baris ajaib di sini
})->stream();
Stream dengan Header
Anda juga dapat menggunakan metode streamWithHeaders()
untuk mengatur header sebelum Anda mulai streaming.
Flight::route('/stream-users', function() {
// Anda dapat menambahkan header tambahan apa pun yang Anda inginkan di sini
// Anda hanya harus menggunakan header() atau Flight::response()->setRealHeader()
// namun cara Anda menarik data Anda, hanya sebagai contoh...
$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 ',';
}
// Ini diperlukan untuk mengirim data ke klien
ob_flush();
}
echo '}';
// Ini adalah bagaimana Anda mengatur header sebelum Anda mulai streaming.
})->streamWithHeaders([
'Content-Type' => 'application/json',
'Content-Disposition' => 'attachment; filename="users.json"',
// kode status opsional, default ke 200
'status' => 200
]);