89 lines
4.1 KiB
PHP
89 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Actions\Projects;
|
|
|
|
use App\Models\Project;
|
|
use App\Models\ProjectTemplate;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class UpdateProjectTemplate
|
|
{
|
|
public function __construct(
|
|
private readonly DuplicateProjectConfiguration $duplicateProjectConfiguration,
|
|
private readonly ProjectTemplateOverrideManager $projectTemplateOverrideManager,
|
|
) {}
|
|
|
|
/**
|
|
* @param array{name: string, copy_areas?: array<int, string>} $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');
|
|
}
|
|
}
|