} $attributes */ public function handle(ProjectTemplate $projectTemplate, array $attributes): ProjectTemplate { $projectTemplate->loadMissing('sourceProject'); $sourceProject = $projectTemplate->sourceProject; abort_if($sourceProject === null, 422, 'This template cannot be edited because the source project is missing.'); $copyAreas = array_values(Arr::get($attributes, 'copy_areas', [])); $normalizedCopyAreas = $this->duplicateProjectConfiguration->normalizeCopyAreas($copyAreas); $previousConfiguration = $this->duplicateProjectConfiguration->normalizeConfiguration( $projectTemplate->configuration_snapshot ?? [], ); $nextConfiguration = $this->duplicateProjectConfiguration->export($sourceProject, $normalizedCopyAreas); $affectedAreas = collect(array_unique([...array_keys($previousConfiguration), ...array_keys($nextConfiguration)])) ->filter(fn (string $area): bool => ($previousConfiguration[$area] ?? null) !== ($nextConfiguration[$area] ?? null)) ->values() ->all(); DB::transaction(function () use ($projectTemplate, $attributes, $normalizedCopyAreas, $nextConfiguration, $previousConfiguration, $affectedAreas): void { ProjectTemplate::query()->whereKey($projectTemplate->id)->lockForUpdate()->firstOrFail(); $projectTemplate->forceFill([ 'name' => $attributes['name'], 'copy_areas' => $normalizedCopyAreas, 'configuration_snapshot' => $nextConfiguration, ])->save(); Project::query() ->where('project_template_id', $projectTemplate->id) ->orderBy('id') ->chunkById(100, function ($linkedProjects) use ($affectedAreas, $nextConfiguration, $previousConfiguration): void { foreach ($linkedProjects as $linkedProject) { $linkedProject = Project::query()->whereKey($linkedProject->id)->lockForUpdate()->firstOrFail(); $currentConfiguration = $this->duplicateProjectConfiguration->export($linkedProject, $affectedAreas); $overrides = $linkedProject->template_overrides ?? []; $replacementConfiguration = []; foreach ($affectedAreas as $area) { if (in_array($area, $overrides, true)) { continue; } if (! array_key_exists($area, $nextConfiguration)) { $this->projectTemplateOverrideManager->mark($linkedProject, $area); continue; } if (! array_key_exists($area, $previousConfiguration) && array_key_exists($area, $currentConfiguration) && $currentConfiguration[$area] !== []) { $this->projectTemplateOverrideManager->mark($linkedProject, $area); continue; } $replacementConfiguration[$area] = $nextConfiguration[$area]; } if ($replacementConfiguration !== []) { $this->duplicateProjectConfiguration->apply($linkedProject, $replacementConfiguration); } } }); }); return $projectTemplate->fresh(['sourceProject'])->loadCount('linkedProjects'); } }