Сидери бази даних часто потребують спільних даних між класами, але традиційні підходи створюють проблеми з обслуговуванням та неясними залежностями. Система Context в Laravel пропонує елегантне рішення для управління потоком даних сидерів.
Складні сценарії сидінга часто пов’язані із залежностями, коли наступні сидери мають доступ до попередньо створених записів. Традиційні методи покладаються на статичні змінні, глобальний стан або зайві запити до бази даних, що призводить до крихкого коду та нечітких зв'язків між класами сидерів.
Фасад Context в Laravel перетворює цю проблему, пропонуючи зрозумілий механізм для обміну даними, який зберігає правильний розподіл обов’язків і забезпечує безперешкодний потік інформації:
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Context;
class UserSeeder extends Seeder
{
public function run(): void
{
$adminUser = User::factory()->create([
'email' => 'admin@company.com',
'role' => 'administrator'
]);
Context::add('admin_user', $adminUser);
Context::add('admin_email', $adminUser->email);
}
}
Наступні сидери можуть отримати доступ до спільних даних через параметри з типом, використовуючи атрибути Context.
use App\Models\Project;
use App\Models\User;
use Illuminate\Container\Attributes\Context;
use Illuminate\Database\Seeder;
class ProjectSeeder extends Seeder
{
public function run(
#[Context('admin_user')] User $adminUser,
): void {
$project = Project::factory()->create([
'name' => 'Main Application',
'owner_id' => $adminUser->id,
'status' => 'active'
]);
$project->members()->attach($adminUser, ['role' => 'project_manager']);
Context::add('main_project', $project);
}
}
Ось приклад комплексної системи сидінга організаційних структур, яка демонструє розширене використання Context:
use App\Models\{Organization, Department, User, Project, Role};
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Context;
use Illuminate\Container\Attributes\Context as ContextAttribute;
class OrganizationSeeder extends Seeder
{
public function run(): void
{
$organization = Organization::factory()->create([
'name' => 'Tech Solutions Inc',
'type' => 'technology',
'status' => 'active'
]);
Context::add('organization', $organization);
Context::add('org_id', $organization->id);
$this->info("Created organization: {$organization->name}");
}
}
class DepartmentSeeder extends Seeder
{
public function run(
#[ContextAttribute('organization')] Organization $organization
): void {
$departments = collect([
['name' => 'Engineering', 'budget' => 500000],
['name' => 'Product Management', 'budget' => 200000],
['name' => 'Marketing', 'budget' => 300000],
['name' => 'Sales', 'budget' => 250000]
])->map(function ($dept) use ($organization) {
return Department::factory()->create(array_merge($dept, [
'organization_id' => $organization->id
]));
});
Context::add('departments', $departments);
Context::add('engineering_dept', $departments->firstWhere('name', 'Engineering'));
$this->info("Created {$departments->count()} departments");
}
}
class UserSeeder extends Seeder
{
public function run(
#[ContextAttribute('organization')] Organization $organization,
#[ContextAttribute('engineering_dept')] Department $engineeringDept
): void {
$users = collect();
$cto = User::factory()->create([
'name' => 'Sarah Johnson',
'email' => 'sarah.johnson@company.com',
'role' => 'cto',
'organization_id' => $organization->id,
'department_id' => $engineeringDept->id
]);
$users->push($cto);
Context::add('cto', $cto);
$teamLead = User::factory()->create([
'name' => 'Mike Chen',
'email' => 'mike.chen@company.com',
'role' => 'team_lead',
'organization_id' => $organization->id,
'department_id' => $engineeringDept->id,
'manager_id' => $cto->id
]);
$users->push($teamLead);
Context::add('team_lead', $teamLead);
$developers = User::factory(5)->create([
'role' => 'developer',
'organization_id' => $organization->id,
'department_id' => $engineeringDept->id,
'manager_id' => $teamLead->id
]);
$users = $users->concat($developers);
Context::add('all_users', $users);
Context::add('developers', $developers);
$this->info("Created {$users->count()} users across the organization");
}
}
class ProjectSeeder extends Seeder
{
public function run(
#[ContextAttribute('organization')] Organization $organization,
#[ContextAttribute('cto')] User $cto,
#[ContextAttribute('team_lead')] User $teamLead,
#[ContextAttribute('developers')] \Illuminate\Support\Collection $developers
): void {
$projects = collect();
$mainProject = Project::factory()->create([
'name' => 'Customer Portal Redesign',
'description' => 'Complete overhaul of customer-facing portal',
'status' => 'in_progress',
'priority' => 'high',
'organization_id' => $organization->id,
'owner_id' => $cto->id,
'lead_id' => $teamLead->id,
'budget' => 150000,
'deadline' => now()->addMonths(6)
]);
$mainProject->members()->attach($cto, ['role' => 'project_owner']);
$mainProject->members()->attach($teamLead, ['role' => 'project_lead']);
$developers->each(function ($developer) use ($mainProject) {
$mainProject->members()->attach($developer, ['role' => 'developer']);
});
$projects->push($mainProject);
Context::add('main_project', $mainProject);
$sideProject = Project::factory()->create([
'name' => 'API Documentation System',
'description' => 'Internal tool for API documentation',
'status' => 'planning',
'priority' => 'medium',
'organization_id' => $organization->id,
'owner_id' => $teamLead->id,
'lead_id' => $developers->first()->id,
'budget' => 50000,
'deadline' => now()->addMonths(3)
]);
$sideProject->members()->attach($teamLead, ['role' => 'project_owner']);
$sideProject->members()->attach($developers->take(2), ['role' => 'developer']);
$projects->push($sideProject);
Context::add('all_projects', $projects);
$this->info("Created {$projects->count()} projects with team assignments");
}
}
class PermissionsSeeder extends Seeder
{
public function run(
#[ContextAttribute('cto')] User $cto,
#[ContextAttribute('team_lead')] User $teamLead,
#[ContextAttribute('developers')] \Illuminate\Support\Collection $developers
): void {
$permissions = [
'cto' => ['manage_organization', 'view_all_projects', 'manage_budgets', 'hire_staff'],
'team_lead' => ['manage_team', 'view_team_projects', 'assign_tasks', 'review_code'],
'developer' => ['view_assigned_projects', 'submit_code', 'create_tasks', 'update_profile']
];
foreach ($permissions['cto'] as $permission) {
$cto->permissions()->firstOrCreate(['name' => $permission]);
}
foreach ($permissions['team_lead'] as $permission) {
$teamLead->permissions()->firstOrCreate(['name' => $permission]);
}
$developers->each(function ($developer) use ($permissions) {
foreach ($permissions['developer'] as $permission) {
$developer->permissions()->firstOrCreate(['name' => $permission]);
}
});
$totalPermissions = array_sum(array_map('count', $permissions));
$this->info("Assigned {$totalPermissions} permissions across all users");
}
}
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call([
OrganizationSeeder::class,
DepartmentSeeder::class,
UserSeeder::class,
ProjectSeeder::class,
PermissionsSeeder::class,
]);
$this->info('Database seeding completed successfully');
}
}
Цей підхід відкидає такі антипатерни, як зайві запити до бази даних, перенасичення класів сидерів статичними властивостями та тісну прив’язку, яка ускладнює тестування. Система Context забезпечує типову безпеку через ін'єкцію на основі атрибутів, що гарантує коректне автозаповнення та підтримку коду під час зростання складності сидінга.