ویژگی های جدید لاراول 9 - قسمت اول

ویژگی های جدید لاراول 9 - قسمت اول

حدود یک سال پیش نسخه 8 فریم ورک لاراول منتشر شد و در مقاله ویژگی های جدید لاراول 8 به مهم ترین قابلیت های این نسخه اشاره کردیم. اکنون که زمان کمی به انتشار نسخه 9 این فریم ورک باقی مانده، قصد داریم شما را با مهم ترین قابلیت هایی که طی این یک سال به فریم ورک اضافه شده است آشنا کنیم.

متد 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، در صورت بروز خطا (برای نمونه 404) یک 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' => 1, 'email' => 'taylor@example.com'],
        ['id' => 2, '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 ها پیش تر در نسخه 8 اضافه شده بودند. برای نمونه، کد زیر یک 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' => '[0-9]+',
        '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 = 3600;

    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 ما در 10 تلاش پشت سر هم با خطا مواجه شود، به مدت 5 دقیقه اجرا نخواهد شد. همچنین می توان متد backoff را روی این middleware صدا زد تا 10 تلاش اول پشت سر هم نباشند. این قابلیت برای پیاده سازی الگوی طراحی 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);

 

در این مقاله به ویژگی های کاربردی زیادی از جمله متد صفحه بندی جدید، رابطه cast ،hasOneOfMany های مختلف و... اشاره شد. دانستن این ویژگی ها تاثیر زیادی در کد نویسی روزمره یک توسعه دهنده لاراول خواهد داشت. قابلیت های جدید لاراول به مواردی که در این مقاله اشاره کردیم محدود نمی شود و و در مقاله دیگری سایر ویژگی ها را بررسی خواهیم کرد. شما می توانید با دنبال کردن نویسنده این مقاله، از انتشار قسمت بعدی مطلع شوید.

پیشنهادات بیشتر سکان بلاگ برای شما