Tipe Primitif Controller Dan Closure Routes
Apa yang saya tidak pertimbangkan adalah jenis instruksi primitif untuk controller Laravel. PHP hanya memiliki empat jenis skala primitif:bool , int, float dan string. Untuk routes, string dan int adalah jenis yang paling penting. Namun, saya biasanya tidak menggunakan instruksi tipe untuk tipe skalar primitif di controller saya.
Saya baru-baru ini melihat masalah yang timbul dengan instruksi dari jenis controller yang disebabkan oleh TypeError. Jadi saya ingin menunjukkan beberapa contoh di mana Anda dapat dengan aman menggunakan controller dengan tipe int
Pertimbangkan tindakan routes berikut dan jenis $orderId akan menjadi apa saat dipanggil:
Route::get('/order/{order_id}', function ($orderId) {
return [
'type' => gettype($orderId),
'value' => $orderId,
];
});
pada saat menggunkan $orderId akan menjadi string ketika closure ini dipanggil. Jika Anda menulis tes cepat, itulah yang akan Anda lihat:
/**
* A basic test example.
*
* @return void
*/
public function test_example()
{
$response = $this->get('/order/123');
$response->assertJson([
'type' => 'string',
'value' => '123',
]);
}
sekarang ,katakanlah anda mengharapkan order id untuk tipe bilangan bulat, berikut adalah parameter yang digunakan:
Route::get('/order/{order_id}', function (int $orderId) {
return [
'type' => gettype($orderId),
'value' => $orderId,
];
});
Ketika saya kembali ke tes, gagal dengan output berikut:
--- Expected
+++ Actual
@@ @@
array (
- 'type' => 'string',
- 'value' => '123',
+ 'type' => 'integer',
+ 'value' => 123,
)
Secara teknis Anda meneruskan string 123 ke fungsi route, tetapi PHP menangani ini melalui penegakan tipe secara default. Artinya, PHP akan mencoba mengubah nilai dari string menjadi integer saat memanggil fungsi route.
Secara teknis, pendekatan ini berfungsi dan menjamin tipe integer, tetapi masih ada masalah lain yang mungkin Anda temukan. Bagaimana jika pengguna melewati sesuatu yang tidak dapat dikonversi dari string ke integer?
Ketika saya memperbarui pengujian saya sebagai berikut, saya mendapatkan TypeError:
public function test_example()
{
$this->withoutExceptionHandling();
$response = $this->get('/order/ABC-123');
$response->assertJson([
'type' => 'integer',
'value' => 123,
]);
}
Ketika saya menjalankan tes, saya mendapatkan kesalahan berikut:
TypeError: Illuminate\Routing\RouteFileRegistrar::{closure}():
Argument #1 ($orderId) must be of type int, string given, called in .../vendor/laravel/framework/src/Illuminate/Routing/Route.php on line 238
Jika Anda mengetikkan integer ke route, Anda perlu memastikan bahwa route memiliki batasan ekspresi reguler.
Route::get('/order/{order_id}', function (int $orderId) {
return [
'type' => gettype($orderId),
'value' => $orderId,
];
})->where('order_id', '[0-9]+');
Penambahan pembatasan pada pengaturan rute memastikan bahwa jenis eksekusi berfungsi seperti yang diharapkan karena hanya angka yang sesuai dengan rute. Tes-tes berikut akan lebih spesifik sehingga rute kontrol tidak dapat disamakan dengan pengaturan router non-numerik.
public function test_example()
{
$response = $this->get('/order/ABC-123');
$response->assertNotFound();
}
Anda sekarang dapat berasumsi bahwa jenis jadwal penutupan Anda adalah kekuatan yang aman untuk bilangan bulat jika Anda ingin menggunakan instruksi tipe. Meskipun penggunaan penggunaannya terbatas, saya pikir studi tentang nuansa ini dapat membantu orang mencoba mengetuk instruksi parameter router.
Apakah ini dipengaruhi oleh tipe yang tepat?
Saya ingin menunjukkan bahwa mendeklarasikan (strict_types = 1) . Kode panggilan ada dalam kerangka Laravel, yang tidak menggunakan deklarasi strict_types, sehingga tidak berpengaruh. Oleh karena itu, penegakan jenis terjadi.
- Laravel dari controller::callAction() untuk controller
- Laravel dari Route::runCallable() untuk berbasis penutupan Routes
Dalam dokumentasi deklarasi tipe PHP, pengetikan ketat memiliki peringatan berikut tentang cara kerja tipe ketat:
Pengetikan ketat berlaku untuk panggilan fungsi yang dibuat dari dalam file yang pengetikan ketat diaktifkan, bukan untuk fungsi yang dideklarasikan di dalam file itu. Jika file yang tidak mengaktifkan pengetikan ketat memanggil fungsi yang ditentukan dalam file pengetikan ketat, pengaturan pemanggil (input paksa) dihormati dan nilainya dipaksakan'.
Pendekatan alternatif
Jika Anda memiliki parameter perutean skalar di controllers dan penutup, Anda dapat menghilangkan tipe skalar dan melakukan casting tipe dalam metode controller Anda:
Route::get(
'/order/{order_id}',
function ($orderId, SomeService $someService) {
// Cast for a method that strictly type-hints an integer
$result = $someService->someMethod((int) $orderId);
// ...
}
);
Dalam kebanyakan kasus, penggunaan route model binding untuk parameter rute yang cocok dengan ID numerik. Namun, pertimbangkan pendekatan ini jika Anda memiliki parameter akar numerik yang diisyaratkan oleh tipe menggunakan tipe skalar primitif int.