یکی از بزرگترین مشکلات در برنامه نویسی تکرار کد ها می باشد. مخصوصا در زمانی که شما یک باگ دارید و فکر می کنید که آن را حل کرده اید اما چندین مورد از این باگ باقی مانده است.
ما در این مقاله قصد داریم با یک آموزش ساده درباره کاهش تکرار کد ها صحبت کنیم. برای شروع مثلا ما یک فروشگاه داریم و قصد داریم در هر روز یک بارگزارشی از تمام فروش ها و وضعیت محصولات را تهیه کنیم. برای مثال در ادمین پنل با کلیک بر روی یک دکمه گزارش ایجاد شود.
اولین قدم ما ایجاد یک دستور Artisan یا مجموعه ای از دستورات Artisan می باشد، تا گزارش ها را برای ما ایجاد کند. ابتدا میخواهم درباره ارقام فروش شروع کنم:
final class SalesFigures extends Command
{
public $signature = 'reports:sales';
public $description = 'Run a daily report on sales.';
public function handle(): int
{
$date = now()->subDay();
$sales = Order::query()
->where('status', Status::COMPLETE)
->whereBetween(
'completed_at',
$date->startOfDay(),
$date->endOfDay(),
)->latest()->get();
// send information through to the report builder
}
}
ما در کد بالا یک دستور بسیار ساده داریم که با اجرای آن ارقام فروش دیروز که بعنوان کامل شده علامت گذاری شدند را دریافت کرده و همچنین کوئری(query) ما نیاز بسیار
ساده می باشد ، این طور که وضعیت و تاریخ دیروز را برای ما بررسی می کند و بعد آن را برای ما تهیه می کند. و این گزارش تهیه شده به صورت روزانه خواهد بود.
بیایم کد های بالا را یکم بهبود ببخشیم. و فرآیند refactoring را شروع کنیم. ما در بالا یک کوئری(query) بسیار ساده داشتیم پس در اینجا میخواهیم یک کوئری خاص که بشود در چند جای مختلف از آن استفاده کرد. پس چیکار می کنیم؟ یک کلاس مخصوص به خودش ایجاد می کنیم.
final class ResultsForPeriod implements ResultsForPeriodContract
{
public function handle(
Builder $query,
CarbonInterface $start,
CarbonInterface $end,
): Builder {
return $builder->whereBetween(
'completed_at',
$start,
$end,
);
}
}
کد های بالا این اجازه را به ما می دهند که نتایج را برای یک بازه زمانی مشخص برای هر مدلی(Model) به دست بیاوریم.
final class SalesFigures extends Command
{
public $signature = 'reports:sales';
public $description = 'Run a daily report on sales.';
public function handle(ResultsForPeriodContract $query): int
{
$date = now()->subDay();
$sales = $query->handle(
query: Order::query()
->where('status', Status::COMPLETE),
start: $date->startOfDay(),
end: $date->endOfDay()
)->latest()->get();
// send information through to the report builder
}
}
ما تا اینجا چیکار کردیم یک کوئری ساختیم برای فیلتر کردن و بر اساس بازه زمانی پیاده سازی کرده ایم. ما در ادمین پنل احتمالا اطلاعاتی از این گزارش خواهیم داشت، پس برای این که کد های ما دوباره تکرار نشود:
final class ReportService implements ReportServiceContract
{
public function __construct(
private readonly ResultsForPeriodContract $periodFilter,
) {}
public function dailySales(CarbonInterface $start, CarbonInterface $end): Collection
{
return $this->periodFilter->handle(
query: Order::query()->where('status', Status::COMPLETE),
)->latest()->get();
}
}
الان می توانیم از دستورات Artisan استفاده کنیم:
final class SalesFigures extends Command
{
public $signature = 'reports:sales';
public $description = 'Run a daily report on sales.';
public function handle(ReportServiceContract $service): int
{
$date = now()->subDay();
$sales = $service->dailySales(
query: Order::query(),
start: $date->startOfDay(),
end: $date->endOfDay()
);
// send information through to the report builder
}
}
ما اکنون یک کد بسیار تمیز با هم دیگر نوشتیم که در قسمت های مختلف برنامه های خود از آن استفاده کرده ایم بدون اینکه بخواهیم آن ها را تکرار کنیم.