Інтерфейс Isolatable в Laravel забезпечує виконання команд в ексклюзивному режимі, запобігаючи одночасному виконанню кількох інстанцій і, тим самим, захищаючи від конфліктів ресурсів або пошкодження даних під час критичних операцій.
Цей інтерфейс автоматично управляє блокуванням команд, що робить його ідеальним для тривалих процесів, синхронізації даних або обслуговування системи:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Isolatable;
class SyncInventoryCommand extends Command implements Isolatable
{
protected $signature = 'inventory:sync';
protected $description = 'Синхронізувати інвентар з зовнішніми системами';
public function handle()
{
$this->info('Синхронізація даних інвентарю...');
}
}
Laravel автоматично додає опцію --isolated
при реалізації цього інтерфейсу, що дозволяє виконувати команди в ексклюзивному режимі.
php artisan inventory:sync --isolated
Ось зразок системи резервного копіювання, яка демонструє ізоляцію з відстеженням прогресу та обробкою помилок:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Isolatable;
use App\Services\DatabaseBackupService;
use App\Services\FileBackupService;
use App\Services\NotificationService;
use Exception;
class SystemBackupCommand extends Command implements Isolatable
{
protected $signature = 'backup:system
{--type=full : Тип резервного копіювання (повне, інкрементальне, база даних)}
{--compress : Стиснути файли резервного копіювання}
{--verify : Перевірити цілісність резервної копії}';
protected $description = 'Створити комплексну резервну копію системи';
protected $databaseService;
protected $fileService;
protected $notificationService;
public function __construct(
DatabaseBackupService $databaseService,
FileBackupService $fileService,
NotificationService $notificationService
) {
parent::__construct();
$this->databaseService = $databaseService;
$this->fileService = $fileService;
$this->notificationService = $notificationService;
}
public function handle()
{
$startTime = now();
$backupType = $this->option('type');
$shouldCompress = $this->option('compress');
$shouldVerify = $this->option('verify');
$this->info("Розпочато {$backupType} резервне копіювання о {$startTime->format('Y-m-d H:i:s')}");
try {
$backupResults = $this->performBackup($backupType, $shouldCompress);
if ($shouldVerify) {
$this->info('Перевірка цілісності резервної копії...');
$this->verifyBackupIntegrity($backupResults);
}
$duration = now()->diffInMinutes($startTime);
$this->info("Резервне копіювання успішно завершено за {$duration} хвилин");
$this->notificationService->sendBackupSuccess($backupResults, $duration);
return Command::SUCCESS;
} catch (Exception $e) {
$this->error("Резервне копіювання не вдалося: {$e->getMessage()}");
$this->notificationService->sendBackupFailure($e);
return Command::FAILURE;
}
}
private function performBackup($type, $compress)
{
$results = [];
if (in_array($type, ['full', 'database'])) {
$this->info('Резервне копіювання бази даних...');
$results['database'] = $this->databaseService->createBackup([
'compress' => $compress,
'include_structure' => true,
'include_data' => true
]);
$this->info('Резервне копіювання бази даних завершено');
}
if (in_array($type, ['full', 'incremental'])) {
$this->info('Резервне копіювання файлів додатку...');
$fileOptions = [
'compress' => $compress,
'incremental' => $type === 'incremental',
'exclude_patterns' => [
'storage/logs/*',
'storage/cache/*',
'node_modules/*',
'.git/*'
]
];
$results['files'] = $this->fileService->createBackup($fileOptions);
$this->info('Резервне копіювання файлів завершено');
}
$this->info('Очищення старих резервних копій...');
$this->cleanupOldBackups();
return $results;
}
private function verifyBackupIntegrity($backupResults)
{
foreach ($backupResults as $backupType => $result) {
$this->line("Перевірка резервної копії {$backupType}...");
$isValid = match($backupType) {
'database' => $this->databaseService->verifyBackup($result['path']),
'files' => $this->fileService->verifyBackup($result['path']),
default => false
};
if ($isValid) {
$this->info("✓ Перевірка резервної копії {$backupType} пройдена");
} else {
throw new Exception("Перевірка резервної копії не пройшла для {$backupType}");
}
}
}
private function cleanupOldBackups()
{
$retentionDays = config('backup.retention_days', 30);
$cutoffDate = now()->subDays($retentionDays);
$deletedCount = $this->fileService->cleanupOldBackups($cutoffDate);
$this->info("Очищено {$deletedCount} старих резервних копій");
}
}
class ReportGenerationCommand extends Command implements Isolatable
{
protected $signature = 'reports:generate
{type : Тип звіту для генерації}
{--period=monthly : Період часу для звіту}
{--format=pdf : Формат виводу (pdf, excel, csv)}
{--email= : Адреса електронної пошти для надсилання звіту}';
protected $description = 'Генерувати комплексні бізнес-звіти';
public function handle()
{
$reportType = $this->argument('type');
$period = $this->option('period');
$format = $this->option('format');
$emailRecipient = $this->option('email');
$this->info("Генерація звіту {$reportType} за період {$period}...");
$reportData = $this->gatherReportData($reportType, $period);
$reportPath = $this->generateReport($reportData, $format);
$this->info("Звіт згенеровано: {$reportPath}");
if ($emailRecipient) {
$this->emailReport($reportPath, $emailRecipient);
$this->info("Звіт надіслано на {$emailRecipient}");
}
return Command::SUCCESS;
}
private function gatherReportData($type, $period)
{
$this->info('Збір даних для звіту...');
return match($type) {
'sales' => $this->getSalesData($period),
'user-activity' => $this->getUserActivityData($period),
'financial' => $this->getFinancialData($period),
default => throw new Exception("Невідомий тип звіту: {$type}")
};
}
private function generateReport($data, $format)
{
$this->info("Форматування звіту у форматі {$format}...");
$reportGenerator = app("App\\Services\\ReportGenerators\\{$format}Generator");
return $reportGenerator->generate($data);
}
private function emailReport($reportPath, $recipient)
{
$this->info('Відправка звіту електронною поштою...');
Mail::to($recipient)->send(new ReportGeneratedMail($reportPath));
}
}
Спеціальні коди виходу забезпечують зворотний зв'язок, коли ізоляція заважає виконанню:
php artisan backup:system --isolated=42
Інтерфейс Isolatable гарантує безпеку команд у виробничих середовищах, запобігаючи конфліктам ресурсів та підтримуючи цілісність даних під час критичних операцій.