با انتشار هر نسخه جدید از لاراول، تغییرات و ویژگیهای جدیدی به این فریمورک قدرتمند افزوده میشود که به توسعهدهندگان کمک میکند تا فرآیندهای برنامهنویسی را سادهتر، سریعتر و مؤثرتر انجام دهند. در اینجا به برخی از ویژگیهای جدید و بهروزرسانیهای مهم در نسخه اخیر لاراول پرداختهایم که شامل بهبودهایی در زمینه تستنویسی، کار با صفها، ارتباط با دیتابیس، مدیریت خطاها، و بسیاری دیگر از قابلیتها میشود. این ویژگیها نه تنها کارایی و قابلیتهای فریمورک را افزایش میدهند، بلکه تجربه توسعهدهندگان را نیز بهبود میبخشند.
در ادامه، برخی از متدها و قابلیتهای جدید لاراول مورد بررسی قرار میگیرند که میتوانند در ساخت اپلیکیشنهای پیچیده و مقیاسپذیر نقش مؤثری ایفا کنند.
متد castAsJson
این متد برای راحت شدن تست نویسی ، هنگام تست مقدار ستون های json در دیتابیس اضافه شده است.
$this->assertDatabaseHas('users', [
'name' => 'Peter Parker',
'email' => 'spidey@yahoo.com',
'skills' => $this->castAsJson(json_encode(['web slinging', 'spidey-sense', 'sticky feet'])),
]);
استفاده از Closure در صف های چند تایی
می توان closure ها را به متد batch داد تا نیاز نباشد برای قرار دادن کار های ساده در صف، کلاس job ایجاد کنید.
Bus::batch([
new ProcessPodcast,
function(){
// ...
},
new ReleasePodcast
])->dispatch();
بروز شدن کامنت Facade ها
Doc comment بیشتر Facade ها بروز شده است تا هنگام کدنویسی، IDE کمک بیشتری به ما کند. از جمله Facade هایی که Doc comment آن ها بروز شده می توان به Log ،Mail ،Redirect و Validator اشاره کرد.
پاکسازی صف ها
دستور queue:clear
برای پاک کردن صف برنامه اضافه شده است. با استفاده از این دستور می توان یک صف خاص یا همه صف ها را خالی کرد.
php artisan queue:clear
php artisan queue:clear redis --queue=emails
متد takeUntilTimeout
این متد به کلاس Collection اضافه شده و به شما اجازه می دهد زمانی را برای اجرای یک عملیات روی Collection تعیین کنید. برای نمونه اجرای این کد بیشتر از دو دقیقه ادامه نخواهد داشت.
$lazyCollection
->takeUntilTimeout(now()->add(2, 'minutes'))
->each(fn ($item) => doSomethingThatMayTakeSomeTime($item));
متد thorw در HTTP Client
با صدا زدن این متد هنگام ایجاد یک درخواست HTTP، در صورت بروز خطا (برای نمونه ۴۰۴) یک exception در برنامه throw می شود و روند اجرای برنامه را متوقف می کند. همچنین می توان به ورودی این متد یک callback داد تا قبل از throw شدن ،exception کاری مانند ثبت log انجام دهیم.
return $client->withHeaders($headers)
->post($url, $payload)
->throw(fn($response) => Log::error('Twitter API failed posting Tweet', [
'url' => $url,
'payload' => $payload,
'headers' => $headers,
'response' => $response->body(),
]))->json();
متد is و isNot برای رابطه های مدل
این متد بدون اجرای کوئری بررسی می کند که مدل داده شده به آن با مدلی که این متد را صدا می زند رابطه دارد یا خیر. برای نمونه کد زیر بررسی می کند که $user نویسنده $post هست یا خیر.
$post->author()->is($user);
تا قبل از این قابلیت کد بالا را باید به صورت زیر می نوشتیم که باعث اجرای کوئری می شد:
$post->author->is($user);
متد upsert
این متد می تواند توسط یک کوئری چندین row در دیتابیس ایجاد کند. در صورتی که یکی از مقدار های داده شده قبلا در دیتابیس وجود داشته باشد، به جای نشان دادن خطا، مقدار های آن ردیف بروزرسانی خواهند شد. ورودی اول مقدار هایی است که میخواهیم ذخیره یا بروز شوند و ورودی دوم آرایه ای از ستون ها است که می توان تکراری بودن داده ها را بر اساس آن تشخیص داد.
User::upsert(
[
['id' => ۱, 'email' => 'taylor@example.com'],
['id' => ۲, 'email' => 'dayle@example.com'],
],
'email'
);
متد newLine برای خروجی کنسول
این متد برای نمایش یک خط خالی در خروجی کنسول کاربرد دارد. همچنین می توان تعداد خط های خالی ای که می خواهیم را به ورودی این متد داد.
$this->newLine();
$this->newLine(5);
جلوگیری از تداخل job ها
یک middleware برای job ها اضافه شده است که با مشخص کردن یک کلید برای آن می توان از اجرای همزمان job ها بر اساس آن کلید جلوگیری کرد. برای نمونه، در برنامه ای ما نمی خواهیم یک سفارش را دو بار لغو کنیم کد زیر نوشته می شود.
public function middleware()
{
return [
new WithoutOverlapping($this->order->id)
];
}
در این نمونه ما از اجرای همزمان job هایی که شناسه سفارش آن ها یکی است جلوگیری می کنیم.
Rate Limit برای job ها
دو middleware جدید برای محدود کردن نرخ اجرای job ها اضافه شده است که به عنوان ورودی نام یک RateLimiter تعریف شده را دریافت می کند. RateLimiter ها پیش تر در نسخه ۸ اضافه شده بودند. برای نمونه، کد زیر یک RateLimiter را تعریف می کند:
RateLimiter::for('backups', function ($job) {
return $job->user->vipCustomer()
? Limit::none()
: Limit::perHour(1)->by($job->user->id);
});
و کد زیر نرخ اجرای job را محدود می کند:
public function middleware()
{
return [new RateLimited('backups')];
}
همچنین متد dontRelease را هم می توان روی این middleware ها صدا زد تا هنگامی که job ای به محدودیت اجرا می رسد به صف بر نگردد.
public function middleware()
{
return [(new RateLimited('backups'))->dontRelease()];
}
cast های جدید
encrypted
با استفاده از این cast می توان رشته های ساده را هنگام ذخیره در دیتابیس رمزگذاری کرد و هنگام خواندن از دیتابیس آن ها را به شکل اصلی و رمزگذاری نشده دریافت کرد.
protected $casts = [
'sensitive_data' => 'encrypted',
];
همچنین با استفاده از متد encryptUsing در کلاس Model می توانیم کلاسی که کار رمزگذاری را انجام می دهد، تغییر دهیم.
use Illuminate\Database\Eloquent\Model;
use Illuminate\Encryption\Encrypter;
$databaseEncryptionKey = config('database.encryption_key');
$encrypter = new Encrypter($databaseEncryptionKey);
Model::encryptUsing($encrypter);
AsArrayObject
تا اکنون لاراول می توانست آرایه ها را به نوع JSON تبدیل کند. اما این تبدیل با محدودیت هایی همراه بود. نمونه، کدی مانند زیر نمی تواند به JSON تبدیل شود:
$user = User::find(1);
$user->options['foo'] = 'bar';
$user->save();
برای این که تبدیل به درستی انجام شود مجبور به باز نویسی کد بالا به صورت زیر بودیم:
$user = User::find(1);
$user->options = ['foo' => 'bar'];
$user->save();
برای حل این مشکل cast جدیدی با نام AsArrayObject اضافه شده که به جای تبدیل JSON به آرایه معمولی آن را به ArrayObject که کلاسی در PHP است (و به ما اجازه می دهد با یک شی مانند آرایه برخورد کنیم) تبدیل می کند. با این کار اعمال تغییر در آرایه راحت تر است و تبدیل به JSON با هوشمندی بیشتری انجام خواهد شد. با استفاده از این cast تکه کد زیر به درستی کار خواهد کرد:
$user = User::find(1);
$user->options['foo'] = 'bar';
$user->save();
event جدید DatabaseRefreshed
این event پس از اجرای دستور های migrate:fresh و migrate:refresh فراخوانی می شود و به ما این امکان را می دهد که پس refresh شدن دیتابیس کاری انجام دهیم.
متد explain برای QueryBuilder
با استفاده از این متد می توان توضیحاتی که دیتابیس برای یک کوئری فراهم می کند را ببینیم. پیش از این مجبور بودیم شکل خام کوئری را در برنامه ای مانند table plus قرار دهیم و عبارت EXPLAIN را ابتدای کوئری بنویسیم.
Webhook::where('event', 'users.registered')->explain()
Webhook::where('event', 'users.registered')->explain()->dd()
متد های کمکی برای تعریف مسیر ها
متد های whereAlpha، whereNumber و whereAlphaNumeric به کلاس Route اضافه شده تا نیاز نباشد هنگام تعیین کردن الگو های ساده برای پارامتر مسیر ها از regex استفاده کنیم. برای نمونه قبل از اضافه شدن این قابلیت کدی مانند زیر می نوشتیم تا مشخص کنیم پارامتر مسیر ما فقط عدد یا حرف می تواند باشد:
Route::get('authors/{author}/{book}')
->where([
'author' => '[۰-۹]+',
'book' => '[a-zA-Z]+'
]);
با اضافه شدن این قابلیت کد بالا به شکل زیر تغییر می کند:
Route::get('authors/{author}/{book}')
->whereNumber('author')
->whereAlpha('book');
Dispatch شدن job ها به صورت یکتا
با اضافه شدن این قابلیت شما می توانید از dispatch شدن job های تکراری در صف جلوگیری کنید. برای این کار، job شما باید از interface ای به نام ShouldBeUnique پیروی کند و متد uniqueId را تعریف کند.
use Illuminate\Contracts\Queue\ShouldBeUnique;
class MyUniqueJob implements ShouldQueue, ShouldBeUnique
{
/**
* The number of seconds after which the job will no longer stay unique.
*
* @var int
*/
public $uniqueFor = ۳۶۰۰;
public function uniqueId()
{
return $this->user->id;
}
}
درایور Ably برای قابلیت broadcast
از این به بعد می توانیم علاوه بر Pusher و Redis از Ably هم به عنوان broadcast کننده استفاده کنیم. این درایور هم مانند pusher یک سرویس پولی است و کار توسعه دهنده ها را بسیار ساده تر می کند.
به تعویق انداختن ارسال اعلان ها بر اساس کانال
تا اکنون می توانستیم ارسال یک اعلان را به تعویق بیاندازیم اما نمی توانستیم زمان تعویق را برای هر کانال به صورت جداگانه تنظیم کنیم. در نسخه جدید این کار به شکل زیر امکان پذیر است:
$user->notify((new InvoicePaid($invoice))->delay([
'mail' => now()->addMinutes(5),
'sms' => now()->addMinutes(10),
]));
اجرای کد بعد از کامل شدن تراکنش
با استفاده از متد afterCommit که به کلاس DB اضافه شده می توانیم کد هایی را بعد از کامل شدن تراکنش های دیتابیس انجام دهیم. این قابلیت در job ها، listener ها و… هم قرار داده شده است و می توانیم آن ها را به گونه ای تنظیم کنیم که بعد از اتمام تراکنش های موجود اجرا شوند.
DB::afterCommit(function () {
Mail::send(...);
});
متد های dump و dd در کلاس Request
از این به بعد می تواینم محتویات شی Request را dd و یا dump کنیم.
$request->dd();
لیست task های برنامه ریزی شده
با استفاده از دستور php artisan schedule:list می توانیم لیستی از task های برنامه ریزی شده به همراه جزئیات مشاهده کنیم.
رمزگذاری محتویات job ها
در صورتی که job ای با اطلاعات حساس (مانند رمز عبور کاربر) داریم می توانیم آن را به صورت رمزگذاری شده در صف ذخیره کنیم تا از برخی آسیب پذیری ها جلوگیری کنیم. هنگام پردازش job، اطلاعات از حالت رمزگذاری شده خارج می شوند.
متد Sole در QueryBuilder
این متد هنگام دریافت نتیجه کوئری مطمئن می شود که کوئری ما تنها یک نتیجه داشته است. در صورتی که کوئری بدون نتیجه باشد یک exception با نام RecordsNotFoundException و در صورتی که بیش از یک نتیجه وجود داشته باشید یک exception با نام MultipleRecordsFoundException در برنامه throw خواهد شد.
Book::where('title', 'like', '%War%')->sole();
اجرای تست ها به صورت موازی
با گزینه جدید parallel– که به دستور php artisan test
اضافه شده است می توانیم چندین تست را به صورت همزمان (موازی) اجرا کنیم. این کار سرعت اجرای تست ها را به صورت چشم گیری افزایش می دهد.
دادن closure به متد sequence در factory ها
از این به بعد می توانیم به ورودی متد sequence یک closure بدهیم تا هر بار که مقدار جدیدی نیاز است این closure فراخوانی شود. برای نمونه:
$users = User::factory()
->count(10)
->state(new Sequence(
fn() => ['role' => UserRoles::all()->random()],
))
->create();
متد collect در کلاس HTTP
از این پس می توانیم پاسخ درخواست های HTTP را به صورت collection دریافت کنیم.
تا قبل از اضافه شدن این متد کدی مانند زیر می نوشتیم:
collect(Http::get("https://api.foo.bar/users")->json());
در نسخه جدید می توانیم کد زیر را بنویسیم:
Http::get("https://api.foo.bar/users")->collect();
ادامه ندادن فرآیند ارزیابی بعد از مشاهده اولین خطا
با کمک متد stopOnFirstFailure که به کلاس Validator اضافه شده می توان تعیین کرد که پس از دیده شدن اولین خطا در ارزیابی ورودی ها دیگر ارزیابی ادامه پیدا نکند و کاربر همان یک خطا را مشاهده کند.
متد های dd و dump در کلاس HTTP
از این به بعد می توانیم محتویات درخواست HTTP ایجادشده را dd و dump کنیم. این متد ها فرآیند دیباگ را راحت تر می کنند.
کنترل اجرای دوباره برای job ها
یک middleware جدید با نام ThrottlesExceptions برای job ها اضافه شده است که برای ارتباط با سرویس های third-party ای که پایدار نیستند کاربرد دارد.
public function middleware()
{
return [new ThrottlesExceptions(10, 5)];
}
در نمونه کد بالا اگر job ما در ۱۰ تلاش پشت سر هم با خطا مواجه شود، به مدت ۵ دقیقه اجرا نخواهد شد. همچنین می توان متد backoff را روی این middleware صدا زد تا ۱۰ تلاش اول پشت سر هم نباشند. این قابلیت برای پیاده سازی الگوی طراحی circuit breaker هم کاربرد دارد.
گزینه without-path برای دستور route:list
با اضافه شدن این گزینه شما می توانید لیستی از مسیر فایل هایی که نمی خواهید در خروجی دستور route:list بیایند را مشخص کنید. این قابلیت برای مخفی کردن مسیر هایی که پکیج ها به برنامه اضافه می کنند کاربرد دارد. برای نمونه:
php artisan route:list --exclude-path=telescope
متد remove در کلاس Str
با استفاده از این متد شما می توانید کاراکتر هایی را از یک رشته حذف کنید.
//Fbr
Str::remove(['o', 'a'], 'Foobar');
متد lazy و lazyById در QueryBuilder
این متد ها نتیجه کوئری را به صورت بخش بندی شده دریافت و در قالب یک LazyCollection بر می گردانند. هنگام حذف یا آپدیت داده های حجیم این قابلیت کاربرد زیادی دارد.
User::lazy()->each->greet();
کلاسی مخصوص ارزیابی کلمه عبور
در نسخه جدید، کلاس Password اضافه شده است که توسط متد های آن می توان مطمئن شد کلمه عبور وارد شده از امنیت مناسبی برخورد دار است.
$request->validate([
'password' => [
'required',
'confirmed',
Password::min(8)
->mixedCase()
->letters()
->numbers()
->symbols()
->uncompromised(),
],
]);
یکی از ویژگی های مهم این کلاس، قابلیت بررسی کلمه عبور وارد شده در دیتابیسی از کلمه عبور های لو رفته است.
تغییر ساختار پیش فرض مایگریشن ها
از نسخه جدید لاراول، کلاس های Migration نام نخواهند داشت و به صورت کلاس ناشناس نوشته می شوند. با استفاده از این syntax دیگر نگران منطبق نبودن نام کلاس migration و نام فایل آن نخواهیم بود.
return new class extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('people', function (Blueprint $table) {
$table->string('first_name')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('people', function (Blueprint $table) {
$table->dropColumn('first_name');
});
}
};
رابطه hasOneOfMany
این نوع رابطه جدید به شما کمک می کند رابطه ی یک به یکی را روی رابطه ای چند به چند تعریف کنید. برای نمونه آخرین تاریخ ورود یا آخرین قیمت محصول. این رابطه به صورت زیر تعریف می شود:
$this->hasOne(Login::class)->latestOfMany();
متد جدید برای صفحه بندی
متد جدید صفحه بندی (pagination) که cursor نام دارد سریع ترین روشی است که تا اکنون در لاراول برای این کار وجود دارد. این روش به جای استفاده از LIMIT و OFFSET در کوئری ها از where استفاده می کند و برای داده های حجیم و صفحه هایی که scroll بی نهایت دارند استفاده می شود. بر خلاف متد های قبلی صفحه بندی که شماره صفحه را دریافت می کنند، در این شیوه یک رشته به عنوان cursor وجود دارد که با استفاده از آن می توان نتیجه های صفحه بعد را دریافت کرد.
$users = User::orderBy('id')->cursorPaginate(10);
نتیجه گیری
نسخه جدید لاراول با مجموعهای از ویژگیهای کاربردی و بهبودهای متعدد، ابزارهای جدیدی را در اختیار توسعهدهندگان قرار میدهد تا فرآیند توسعه نرمافزار را سریعتر، امنتر و کارآمدتر انجام دهند. از قابلیتهای جدید مانند استفاده از Closure در صفها، مدیریت بهینه صفها، متدهای جدید برای کار با دیتابیس، قابلیتهای پیشرفته برای تستنویسی، و نیز مدیریت بهتر خطاها و محدودیتها، گرفته تا بهبودهای مربوط به ارتباطات شبکهای و ارسال اعلانها، همگی به توسعهدهندگان این امکان را میدهند که پروژههای پیچیدهتری را به راحتی پیادهسازی کنند. در نهایت، این بهروزرسانیها باعث میشوند که لاراول به عنوان یک فریمورک محبوب و قدرتمند، همچنان در خط مقدم توسعه وب باقی بماند.