feat: ship creator journey v2 and profile updates
This commit is contained in:
27
app/Services/ContentTypes/ContentTypeSlugResolution.php
Normal file
27
app/Services/ContentTypes/ContentTypeSlugResolution.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\ContentTypes;
|
||||
|
||||
use App\Models\ContentType;
|
||||
|
||||
class ContentTypeSlugResolution
|
||||
{
|
||||
public function __construct(
|
||||
public readonly string $requestedSlug,
|
||||
public readonly ?ContentType $contentType = null,
|
||||
public readonly ?string $redirectSlug = null,
|
||||
public readonly bool $isVirtual = false,
|
||||
public readonly ?string $virtualType = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public function found(): bool
|
||||
{
|
||||
return $this->contentType !== null || $this->isVirtual;
|
||||
}
|
||||
|
||||
public function requiresRedirect(): bool
|
||||
{
|
||||
return $this->redirectSlug !== null && $this->redirectSlug !== '' && $this->redirectSlug !== $this->requestedSlug;
|
||||
}
|
||||
}
|
||||
151
app/Services/ContentTypes/ContentTypeSlugResolver.php
Normal file
151
app/Services/ContentTypes/ContentTypeSlugResolver.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\ContentTypes;
|
||||
|
||||
use App\Models\ContentType;
|
||||
use App\Models\ContentTypeSlugHistory;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class ContentTypeSlugResolver
|
||||
{
|
||||
public function publicContentTypes(): Collection
|
||||
{
|
||||
return Cache::rememberForever($this->publicListCacheKey(), function () {
|
||||
return ContentType::query()
|
||||
->ordered()
|
||||
->get(['id', 'name', 'slug', 'description', 'order', 'hide_from_menu']);
|
||||
});
|
||||
}
|
||||
|
||||
public function toolbarContentTypes(): Collection
|
||||
{
|
||||
return $this->publicContentTypes()
|
||||
->reject(static fn (ContentType $contentType): bool => (bool) $contentType->hide_from_menu)
|
||||
->values();
|
||||
}
|
||||
|
||||
public function resolve(string $slug, bool $allowVirtual = false): ContentTypeSlugResolution
|
||||
{
|
||||
$normalizedSlug = strtolower(trim($slug));
|
||||
|
||||
if ($allowVirtual && $this->isVirtualSlug($normalizedSlug)) {
|
||||
return new ContentTypeSlugResolution(
|
||||
requestedSlug: $normalizedSlug,
|
||||
isVirtual: true,
|
||||
virtualType: $normalizedSlug,
|
||||
);
|
||||
}
|
||||
|
||||
$slugMap = $this->currentSlugMap();
|
||||
if (isset($slugMap[$normalizedSlug])) {
|
||||
return new ContentTypeSlugResolution(
|
||||
requestedSlug: $normalizedSlug,
|
||||
contentType: $this->publicContentTypes()->firstWhere('id', $slugMap[$normalizedSlug]),
|
||||
);
|
||||
}
|
||||
|
||||
$historyMap = $this->historySlugMap();
|
||||
$redirectSlug = $historyMap[$normalizedSlug] ?? null;
|
||||
if ($redirectSlug !== null) {
|
||||
$contentTypeId = $slugMap[$redirectSlug] ?? null;
|
||||
|
||||
return new ContentTypeSlugResolution(
|
||||
requestedSlug: $normalizedSlug,
|
||||
contentType: $contentTypeId !== null ? $this->publicContentTypes()->firstWhere('id', $contentTypeId) : null,
|
||||
redirectSlug: $redirectSlug,
|
||||
);
|
||||
}
|
||||
|
||||
return new ContentTypeSlugResolution(requestedSlug: $normalizedSlug);
|
||||
}
|
||||
|
||||
public function reservedSlugs(): array
|
||||
{
|
||||
return array_values(array_unique(array_map(
|
||||
static fn (string $slug): string => strtolower(trim($slug)),
|
||||
(array) config('content_types.reserved_slugs', [])
|
||||
)));
|
||||
}
|
||||
|
||||
public function isReservedSlug(string $slug): bool
|
||||
{
|
||||
return in_array(strtolower(trim($slug)), $this->reservedSlugs(), true);
|
||||
}
|
||||
|
||||
public function historicalSlugExists(string $slug, ?int $ignoreContentTypeId = null): bool
|
||||
{
|
||||
$query = ContentTypeSlugHistory::query()->where('old_slug', strtolower(trim($slug)));
|
||||
|
||||
if ($ignoreContentTypeId !== null) {
|
||||
$query->where('content_type_id', '!=', $ignoreContentTypeId);
|
||||
}
|
||||
|
||||
return $query->exists();
|
||||
}
|
||||
|
||||
public function flushCaches(): void
|
||||
{
|
||||
Cache::forget($this->publicListCacheKey());
|
||||
Cache::forget($this->slugMapCacheKey());
|
||||
Cache::forget($this->historyMapCacheKey());
|
||||
}
|
||||
|
||||
public function dynamicSitemapContentTypes(): Collection
|
||||
{
|
||||
return $this->publicContentTypes();
|
||||
}
|
||||
|
||||
private function currentSlugMap(): array
|
||||
{
|
||||
return Cache::rememberForever($this->slugMapCacheKey(), function () {
|
||||
return ContentType::query()
|
||||
->ordered()
|
||||
->pluck('id', 'slug')
|
||||
->mapWithKeys(static fn ($id, $slug) => [strtolower((string) $slug) => (int) $id])
|
||||
->all();
|
||||
});
|
||||
}
|
||||
|
||||
private function historySlugMap(): array
|
||||
{
|
||||
return Cache::rememberForever($this->historyMapCacheKey(), function () {
|
||||
$currentSlugById = ContentType::query()
|
||||
->pluck('slug', 'id')
|
||||
->mapWithKeys(static fn ($slug, $id) => [(int) $id => strtolower((string) $slug)])
|
||||
->all();
|
||||
|
||||
return ContentTypeSlugHistory::query()
|
||||
->orderByDesc('id')
|
||||
->get(['content_type_id', 'old_slug'])
|
||||
->mapWithKeys(function (ContentTypeSlugHistory $history) use ($currentSlugById) {
|
||||
$currentSlug = $currentSlugById[(int) $history->content_type_id] ?? null;
|
||||
|
||||
return $currentSlug !== null
|
||||
? [strtolower((string) $history->old_slug) => $currentSlug]
|
||||
: [];
|
||||
})
|
||||
->all();
|
||||
});
|
||||
}
|
||||
|
||||
private function isVirtualSlug(string $slug): bool
|
||||
{
|
||||
return array_key_exists($slug, (array) config('content_types.virtual_types', []));
|
||||
}
|
||||
|
||||
private function publicListCacheKey(): string
|
||||
{
|
||||
return (string) config('content_types.cache.public_list_key', 'content-types.public-list');
|
||||
}
|
||||
|
||||
private function slugMapCacheKey(): string
|
||||
{
|
||||
return (string) config('content_types.cache.slug_map_key', 'content-types.slug-map');
|
||||
}
|
||||
|
||||
private function historyMapCacheKey(): string
|
||||
{
|
||||
return (string) config('content_types.cache.history_map_key', 'content-types.slug-history-map');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user