178 lines
4.8 KiB
PHP
178 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace App\Domains\Tenant\Services;
|
|
|
|
use App\Domains\Attachable\Models\Attachment;
|
|
use App\Domains\Tenant\Models\Tenant;
|
|
use App\Domains\Tenant\Models\WebsiteExtra;
|
|
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
|
use Illuminate\Support\Collection;
|
|
|
|
class TenantInformationService
|
|
{
|
|
private const DEFAULT_RELATIONS = [
|
|
'headerLogo',
|
|
'footerLogo',
|
|
'socialMedia',
|
|
'websiteType.siteLogo',
|
|
'websiteExtras.websiteTypeExtra',
|
|
];
|
|
|
|
/**
|
|
* @param array<int|string, mixed> $relations
|
|
*/
|
|
public function load(Tenant $tenant, array $relations = []): Tenant
|
|
{
|
|
$this->loadMany(new EloquentCollection([$tenant]), $relations);
|
|
|
|
return $tenant;
|
|
}
|
|
|
|
/**
|
|
* @param EloquentCollection<int, Tenant> $tenants
|
|
* @param array<int|string, mixed> $relations
|
|
* @return EloquentCollection<int, Tenant>
|
|
*/
|
|
public function loadMany(
|
|
EloquentCollection $tenants,
|
|
array $relations = []
|
|
): EloquentCollection {
|
|
$tenants->loadMissing([
|
|
...self::DEFAULT_RELATIONS,
|
|
...$relations,
|
|
]);
|
|
|
|
$websiteExtras = $tenants
|
|
->flatMap(fn (Tenant $tenant): Collection => $tenant->websiteExtras);
|
|
|
|
$this->resolveWebsiteExtraAttachments($websiteExtras);
|
|
|
|
return $tenants;
|
|
}
|
|
|
|
/**
|
|
* Replace attachment IDs with their models in each extra's resolved config.
|
|
*
|
|
* @param Collection<int, WebsiteExtra> $websiteExtras
|
|
*/
|
|
private function resolveWebsiteExtraAttachments(Collection $websiteExtras): void
|
|
{
|
|
$attachmentIds = $websiteExtras
|
|
->flatMap(function (WebsiteExtra $extra): array {
|
|
$ids = [];
|
|
|
|
foreach ($this->attachmentTransformPaths($extra) as $path) {
|
|
$ids = [
|
|
...$ids,
|
|
...$this->valuesAtPath($extra->config, $this->pathSegments($path)),
|
|
];
|
|
}
|
|
|
|
return $ids;
|
|
})
|
|
->filter(fn (mixed $id): bool => is_int($id) || ctype_digit((string) $id))
|
|
->map(fn (mixed $id): int => (int) $id)
|
|
->unique()
|
|
->values();
|
|
|
|
$attachments = Attachment::query()
|
|
->whereIn('id', $attachmentIds)
|
|
->get()
|
|
->keyBy('id');
|
|
|
|
foreach ($websiteExtras as $extra) {
|
|
$config = $extra->config;
|
|
|
|
foreach ($this->attachmentTransformPaths($extra) as $path) {
|
|
$config = $this->replaceAtPath(
|
|
$config,
|
|
$this->pathSegments($path),
|
|
fn (mixed $attachmentId): ?Attachment => $attachments->get((int) $attachmentId)
|
|
);
|
|
}
|
|
|
|
$extra->setResolvedConfig($config);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array<int, string>
|
|
*/
|
|
private function attachmentTransformPaths(WebsiteExtra $extra): array
|
|
{
|
|
return collect($extra->websiteTypeExtra->config_schema['transforms'] ?? [])
|
|
->filter(
|
|
fn (mixed $transform): bool => is_array($transform)
|
|
&& ($transform['handler'] ?? null) === 'attachment'
|
|
)
|
|
->keys()
|
|
->all();
|
|
}
|
|
|
|
/**
|
|
* @return array<int, string>
|
|
*/
|
|
private function pathSegments(string $path): array
|
|
{
|
|
$path = ltrim($path, '$');
|
|
$path = ltrim($path, '.');
|
|
|
|
return $path === '' ? [] : explode('.', $path);
|
|
}
|
|
|
|
/**
|
|
* @return array<int, mixed>
|
|
*/
|
|
private function valuesAtPath(mixed $value, array $segments): array
|
|
{
|
|
if ($segments === []) {
|
|
return [$value];
|
|
}
|
|
|
|
if (! is_array($value)) {
|
|
return [];
|
|
}
|
|
|
|
$segment = array_shift($segments);
|
|
|
|
if ($segment === '*') {
|
|
return collect($value)
|
|
->flatMap(fn (mixed $item): array => $this->valuesAtPath($item, $segments))
|
|
->all();
|
|
}
|
|
|
|
if (! array_key_exists($segment, $value)) {
|
|
return [];
|
|
}
|
|
|
|
return $this->valuesAtPath($value[$segment], $segments);
|
|
}
|
|
|
|
private function replaceAtPath(mixed $value, array $segments, callable $replace): mixed
|
|
{
|
|
if ($segments === []) {
|
|
return $value === null ? null : $replace($value);
|
|
}
|
|
|
|
if (! is_array($value)) {
|
|
return $value;
|
|
}
|
|
|
|
$segment = array_shift($segments);
|
|
|
|
if ($segment === '*') {
|
|
foreach ($value as $key => $item) {
|
|
$value[$key] = $this->replaceAtPath($item, $segments, $replace);
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
if (array_key_exists($segment, $value)) {
|
|
$value[$segment] = $this->replaceAtPath($value[$segment], $segments, $replace);
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
}
|