نسخه:

مسیر فرماندهی

معرفی

گذرگاه فرمان لاراول روشی مناسب برای کپسوله کردن وظایفی که برنامه شما برای انجام آنها نیاز دارد به دستورات ساده و قابل فهم ارائه می دهد. برای کمک به درک هدف دستورات، بیایید وانمود کنیم که در حال ساخت اپلیکیشنی هستیم که به کاربران امکان خرید پادکست را می دهد.

هنگامی که کاربر یک پادکست را خریداری می کند، موارد مختلفی وجود دارد که باید اتفاق بیفتد. برای مثال، ممکن است لازم باشد کارت اعتباری کاربر را شارژ کنیم، رکوردی را به پایگاه داده خود اضافه کنیم که نمایانگر خرید است، و یک ایمیل تأیید خرید ارسال کنیم. شاید لازم باشد نوعی اعتبارسنجی نیز انجام دهیم که آیا کاربر مجاز به خرید پادکست است یا خیر.

ما می‌توانیم همه این منطق را در یک متد کنترل‌کننده قرار دهیم. با این حال، این دارای چندین معایب است. اولین عیب این است که کنترلر ما احتمالاً چندین عملکرد HTTP ورودی دیگر را مدیریت می کند و گنجاندن منطق پیچیده در هر روش کنترل کننده به زودی کنترل کننده ما را متورم می کند و خواندن آن را سخت تر می کند. ثانیا، استفاده مجدد از منطق پادکست خرید خارج از زمینه کنترلر دشوار است. ثالثاً، آزمایش واحد فرمان دشوارتر است زیرا باید یک درخواست HTTP خرد تولید کنیم و یک درخواست کامل به برنامه برای آزمایش منطق پادکست خرید ارائه کنیم.

به جای قرار دادن این منطق در کنترلر، ممکن است انتخاب کنیم که آن را در یک شیء "فرمان"، مانند یک PurchasePodcast فرمان، کپسوله کنیم.

ایجاد دستورات

Artisan CLI می تواند کلاس های دستوری جدیدی را با استفاده از make:command دستور ایجاد کند:

php artisan make:command PurchasePodcast

کلاس ایجاد شده جدید در app/Commands دایرکتوری قرار می گیرد. به طور پیش فرض، دستور شامل دو متد است: سازنده و handle متد. البته سازنده به شما این امکان را می دهد که هر شیء مربوطه را به دستور منتقل کنید، در حالی که handle متد دستور را اجرا می کند. مثلا:

class PurchasePodcast extends Command implements SelfHandling {
 
protected $user, $podcast;
 
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(User $user, Podcast $podcast)
{
$this->user = $user;
$this->podcast = $podcast;
}
 
/**
* Execute the command.
*
* @return void
*/
public function handle()
{
// Handle the logic to purchase the podcast...
 
event(new PodcastWasPurchased($this->user, $this->podcast));
}
 
}

این handle روش همچنین ممکن است وابستگی هایی را تایپ کند، و آنها به طور خودکار توسط ظرف سرویس تزریق می شوند . مثلا:

/**
* Execute the command.
*
* @return void
*/
public function handle(BillingGateway $billing)
{
// Handle the logic to purchase the podcast...
}

دستورات اعزام

بنابراین، پس از ایجاد یک فرمان، چگونه آن را ارسال کنیم؟ البته، می‌توانیم handle روش را مستقیماً فراخوانی کنیم. با این حال، ارسال فرمان از طریق "گذرگاه فرمان" لاراول دارای چندین مزیت است که در ادامه به آنها خواهیم پرداخت.

اگر به کنترل کننده پایه برنامه خود نگاهی بیندازید، این ویژگی را خواهید دید DispatchesCommands . این ویژگی به ما اجازه می دهد تا dispatch متد را از هر یک از کنترل کننده های خود فراخوانی کنیم. مثلا:

public function purchasePodcast($podcastId)
{
$this->dispatch(
new PurchasePodcast(Auth::user(), Podcast::findOrFail($podcastId))
);
}

گذرگاه فرمان اجرای دستور و فراخوانی کانتینر IoC را برای تزریق هر گونه وابستگی مورد نیاز به handle متد انجام می دهد.

می توانید این Illuminate\Foundation\Bus\DispatchesCommands ویژگی را به هر کلاسی که می خواهید اضافه کنید. اگر می خواهید یک نمونه گذرگاه فرمان را از طریق سازنده هر یک از کلاس های خود دریافت کنید، می توانید Illuminate\Contracts\Bus\Dispatcher رابط را تایپ کنید. در نهایت، می توانید از Bus نما برای ارسال سریع دستورات نیز استفاده کنید:

Bus::dispatch(
new PurchasePodcast(Auth::user(), Podcast::findOrFail($podcastId))
);

Mapping Command Properties از Requests

نگاشت متغیرهای درخواست HTTP در دستورات بسیار رایج است. بنابراین، به جای اینکه شما را مجبور به انجام این کار به صورت دستی برای هر درخواست کند، لاراول چند روش کمکی برای تبدیل آن به یک درخواست ارائه می دهد. بیایید نگاهی به dispatchFrom روش موجود در این DispatchesCommands صفت بیندازیم:

$this->dispatchFrom('Command\Class\Name', $request);

این متد سازنده کلاس دستوری را که به آن داده می شود بررسی می کند و سپس متغیرها را از درخواست HTTP (یا هر ArrayAccess شی دیگری) استخراج می کند تا پارامترهای سازنده مورد نیاز دستور را پر کند. بنابراین، اگر کلاس فرمان ما firstName متغیری را در سازنده خود بپذیرد، گذرگاه فرمان سعی می کند firstName پارامتر را از درخواست HTTP بیرون بکشد.

همچنین می توانید یک آرایه را به عنوان آرگومان سوم به dispatchFrom متد ارسال کنید. این آرایه برای پر کردن هر پارامتر سازنده که در درخواست موجود نیست استفاده می شود:

$this->dispatchFrom('Command\Class\Name', $request, [
'firstName' => 'Taylor',
]);

دستورات در صف

گذرگاه فرمان فقط برای کارهای همزمانی که در طول چرخه درخواست فعلی اجرا می شوند نیست، بلکه به عنوان راه اصلی برای ایجاد مشاغل در صف در لاراول نیز عمل می کند. بنابراین، چگونه به گذرگاه فرمان دستور دهیم تا به جای اجرای همزمان، کار ما را برای پردازش پس‌زمینه در صف قرار دهد؟ آسان است. ابتدا، هنگام ایجاد یک دستور جدید، فقط --queued پرچم را به دستور اضافه کنید:

php artisan make:command PurchasePodcast --queued

همانطور که خواهید دید، این چند ویژگی دیگر به دستور اضافه می کند، یعنی Illuminate\Contracts\Queue\ShouldBeQueued رابط و SerializesModels صفت. اینها به گذرگاه فرمان دستور می‌دهند تا دستور را در صف قرار دهد، و همچنین هر مدل Eloquent را که دستور شما به عنوان ویژگی ذخیره می‌کند، به‌خوبی سریال‌سازی و بی‌سریال‌سازی کند.

اگر می خواهید یک دستور موجود را به یک دستور در صف تبدیل کنید، به سادگی Illuminate\Contracts\Queue\ShouldBeQueued رابط را روی کلاس به صورت دستی پیاده سازی کنید. این شامل هیچ روشی نیست و صرفاً به عنوان یک "واسط نشانگر" برای توزیع کننده عمل می کند.

سپس، فقط دستور خود را به طور معمول بنویسید. هنگامی که آن را به مسیر ارسال می کنید، آن مسیر به طور خودکار دستور را برای پردازش پس زمینه در صف قرار می دهد. آسان تر از این نمی شود.

برای اطلاعات بیشتر در مورد تعامل با دستورات در صف، مستندات کامل صف را مشاهده کنید .

خط لوله فرمان

قبل از اینکه یک فرمان به یک کنترل کننده ارسال شود، می توانید آن را از کلاس های دیگر در یک "pipeline" عبور دهید. لوله های فرمان درست مانند میان افزار HTTP کار می کنند، به جز دستورات شما! به عنوان مثال، یک لوله فرمان می تواند کل عملیات فرمان را در یک تراکنش پایگاه داده بپیچد یا به سادگی اجرای آن را ثبت کند.

برای افزودن لوله به باس خود، pipeThrough متد دیسپچر را از App\Providers\BusServiceProvider::boot متد خود فراخوانی کنید:

$dispatcher->pipeThrough(['UseDatabaseTransactions', 'LogCommand']);

یک فرمان با یک handle متد تعریف می شود، درست مانند یک میان افزار:

class UseDatabaseTransactions {
 
public function handle($command, $next)
{
return DB::transaction(function() use ($command, $next)
{
return $next($command);
});
}
 
}

کلاس های لوله فرمان از طریق کانتینر IoC حل می شوند ، بنابراین با خیال راحت هر گونه وابستگی را که در سازنده آنها نیاز دارید تایپ کنید.

Closure حتی ممکن است a را به عنوان یک لوله فرمان تعریف کنید :

$dispatcher->pipeThrough([function($command, $next)
{
return DB::transaction(function() use ($command, $next)
{
return $next($command);
});
}]);