Implement creator studio and upload updates
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Services\Sitemaps\AbstractSitemapBuilder;
|
||||
use App\Services\Sitemaps\ShardableSitemapBuilder;
|
||||
use App\Services\Sitemaps\SitemapUrl;
|
||||
use DateTimeInterface;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
abstract class AbstractIdShardableSitemapBuilder extends AbstractSitemapBuilder implements ShardableSitemapBuilder
|
||||
{
|
||||
/**
|
||||
* @return Builder<Model>
|
||||
*/
|
||||
abstract protected function query(): Builder;
|
||||
|
||||
abstract protected function shardConfigKey(): string;
|
||||
|
||||
abstract protected function mapRecord(Model $record): ?SitemapUrl;
|
||||
|
||||
public function items(): array
|
||||
{
|
||||
return $this->query()
|
||||
->orderBy($this->idColumn())
|
||||
->cursor()
|
||||
->map(fn (Model $record): ?SitemapUrl => $this->mapRecord($record))
|
||||
->filter()
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
public function lastModified(): ?DateTimeInterface
|
||||
{
|
||||
return $this->newest(...array_map(
|
||||
fn (SitemapUrl $item): ?DateTimeInterface => $item->lastModified,
|
||||
$this->items(),
|
||||
));
|
||||
}
|
||||
|
||||
public function totalItems(): int
|
||||
{
|
||||
return (clone $this->query())->count();
|
||||
}
|
||||
|
||||
public function shardSize(): int
|
||||
{
|
||||
return max(1, (int) \data_get(\config('sitemaps.shards', []), $this->shardConfigKey() . '.size', 10000));
|
||||
}
|
||||
|
||||
public function itemsForShard(int $shard): array
|
||||
{
|
||||
$window = $this->shardWindow($shard);
|
||||
|
||||
if ($window === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->applyShardWindow($window['from'], $window['to'])
|
||||
->get()
|
||||
->map(fn (Model $record): ?SitemapUrl => $this->mapRecord($record))
|
||||
->filter()
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
public function lastModifiedForShard(int $shard): ?DateTimeInterface
|
||||
{
|
||||
return $this->newest(...array_map(
|
||||
fn (SitemapUrl $item): ?DateTimeInterface => $item->lastModified,
|
||||
$this->itemsForShard($shard),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{from: int, to: int}|null
|
||||
*/
|
||||
protected function shardWindow(int $shard): ?array
|
||||
{
|
||||
if ($shard < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$size = $this->shardSize();
|
||||
$current = 0;
|
||||
$from = null;
|
||||
$to = null;
|
||||
|
||||
$windowQuery = (clone $this->query())
|
||||
->setEagerLoads([])
|
||||
->select([$this->idColumn()])
|
||||
->orderBy($this->idColumn());
|
||||
|
||||
foreach ($windowQuery->cursor() as $record) {
|
||||
$current++;
|
||||
|
||||
if ((int) ceil($current / $size) !== $shard) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$recordId = (int) $record->getAttribute($this->idColumn());
|
||||
$from ??= $recordId;
|
||||
$to = $recordId;
|
||||
}
|
||||
|
||||
if ($from === null || $to === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ['from' => $from, 'to' => $to];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Builder<Model>
|
||||
*/
|
||||
protected function applyShardWindow(int $from, int $to): Builder
|
||||
{
|
||||
return (clone $this->query())
|
||||
->whereBetween($this->qualifiedIdColumn(), [$from, $to])
|
||||
->orderBy($this->idColumn());
|
||||
}
|
||||
|
||||
protected function idColumn(): string
|
||||
{
|
||||
return 'id';
|
||||
}
|
||||
|
||||
protected function qualifiedIdColumn(): string
|
||||
{
|
||||
return $this->idColumn();
|
||||
}
|
||||
}
|
||||
45
app/Services/Sitemaps/Builders/ArtworksSitemapBuilder.php
Normal file
45
app/Services/Sitemaps/Builders/ArtworksSitemapBuilder.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Models\Artwork;
|
||||
use App\Services\Sitemaps\SitemapUrl;
|
||||
use App\Services\Sitemaps\SitemapUrlBuilder;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
final class ArtworksSitemapBuilder extends AbstractIdShardableSitemapBuilder
|
||||
{
|
||||
public function __construct(private readonly SitemapUrlBuilder $urls)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'artworks';
|
||||
}
|
||||
|
||||
protected function shardConfigKey(): string
|
||||
{
|
||||
return 'artworks';
|
||||
}
|
||||
|
||||
protected function mapRecord(Model $record): ?SitemapUrl
|
||||
{
|
||||
return $this->urls->artwork($record);
|
||||
}
|
||||
|
||||
protected function query(): Builder
|
||||
{
|
||||
return Artwork::query()
|
||||
->public()
|
||||
->published();
|
||||
}
|
||||
|
||||
protected function qualifiedIdColumn(): string
|
||||
{
|
||||
return 'artworks.id';
|
||||
}
|
||||
}
|
||||
45
app/Services/Sitemaps/Builders/CardsSitemapBuilder.php
Normal file
45
app/Services/Sitemaps/Builders/CardsSitemapBuilder.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Models\NovaCard;
|
||||
use App\Services\Sitemaps\SitemapUrl;
|
||||
use App\Services\Sitemaps\SitemapUrlBuilder;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
final class CardsSitemapBuilder extends AbstractIdShardableSitemapBuilder
|
||||
{
|
||||
public function __construct(private readonly SitemapUrlBuilder $urls)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'cards';
|
||||
}
|
||||
|
||||
protected function shardConfigKey(): string
|
||||
{
|
||||
return 'cards';
|
||||
}
|
||||
|
||||
protected function mapRecord(Model $record): ?SitemapUrl
|
||||
{
|
||||
return $this->urls->card($record);
|
||||
}
|
||||
|
||||
protected function query(): Builder
|
||||
{
|
||||
return NovaCard::query()
|
||||
->publiclyVisible()
|
||||
->orderBy('id');
|
||||
}
|
||||
|
||||
protected function qualifiedIdColumn(): string
|
||||
{
|
||||
return 'nova_cards.id';
|
||||
}
|
||||
}
|
||||
60
app/Services/Sitemaps/Builders/CategoriesSitemapBuilder.php
Normal file
60
app/Services/Sitemaps/Builders/CategoriesSitemapBuilder.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Models\ContentType;
|
||||
use App\Services\Sitemaps\AbstractSitemapBuilder;
|
||||
use App\Services\Sitemaps\SitemapUrlBuilder;
|
||||
use DateTimeInterface;
|
||||
|
||||
final class CategoriesSitemapBuilder extends AbstractSitemapBuilder
|
||||
{
|
||||
public function __construct(private readonly SitemapUrlBuilder $urls)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'categories';
|
||||
}
|
||||
|
||||
public function items(): array
|
||||
{
|
||||
$items = [$this->urls->categoryDirectory()];
|
||||
|
||||
$contentTypes = ContentType::query()
|
||||
->whereIn('slug', $this->contentTypeSlugs())
|
||||
->ordered()
|
||||
->get();
|
||||
|
||||
foreach ($contentTypes as $contentType) {
|
||||
$items[] = $this->urls->contentType($contentType);
|
||||
}
|
||||
|
||||
$categories = Category::query()
|
||||
->with('contentType')
|
||||
->active()
|
||||
->whereHas('contentType', fn ($query) => $query->whereIn('slug', $this->contentTypeSlugs()))
|
||||
->orderBy('content_type_id')
|
||||
->orderBy('parent_id')
|
||||
->orderBy('sort_order')
|
||||
->orderBy('name')
|
||||
->get();
|
||||
|
||||
foreach ($categories as $category) {
|
||||
$items[] = $this->urls->category($category);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function lastModified(): ?DateTimeInterface
|
||||
{
|
||||
return $this->dateTime(Category::query()
|
||||
->active()
|
||||
->max('updated_at'));
|
||||
}
|
||||
}
|
||||
47
app/Services/Sitemaps/Builders/CollectionsSitemapBuilder.php
Normal file
47
app/Services/Sitemaps/Builders/CollectionsSitemapBuilder.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Models\Collection;
|
||||
use App\Services\Sitemaps\SitemapUrl;
|
||||
use App\Services\Sitemaps\SitemapUrlBuilder;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
final class CollectionsSitemapBuilder extends AbstractIdShardableSitemapBuilder
|
||||
{
|
||||
public function __construct(private readonly SitemapUrlBuilder $urls)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'collections';
|
||||
}
|
||||
|
||||
protected function shardConfigKey(): string
|
||||
{
|
||||
return 'collections';
|
||||
}
|
||||
|
||||
protected function mapRecord(Model $record): ?SitemapUrl
|
||||
{
|
||||
return $this->urls->collection($record);
|
||||
}
|
||||
|
||||
protected function query(): Builder
|
||||
{
|
||||
return Collection::query()
|
||||
->with('user:id,username')
|
||||
->public()
|
||||
->whereNull('canonical_collection_id')
|
||||
->orderBy('id');
|
||||
}
|
||||
|
||||
protected function qualifiedIdColumn(): string
|
||||
{
|
||||
return 'collections.id';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Services\Sitemaps\AbstractSitemapBuilder;
|
||||
use App\Services\Sitemaps\SitemapUrlBuilder;
|
||||
use DateTimeInterface;
|
||||
use cPad\Plugins\Forum\Models\ForumBoard;
|
||||
use cPad\Plugins\Forum\Models\ForumCategory;
|
||||
|
||||
final class ForumCategoriesSitemapBuilder extends AbstractSitemapBuilder
|
||||
{
|
||||
public function __construct(private readonly SitemapUrlBuilder $urls)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'forum-categories';
|
||||
}
|
||||
|
||||
public function items(): array
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach (ForumCategory::query()->active()->ordered()->get() as $category) {
|
||||
$items[] = $this->urls->forumCategory($category);
|
||||
}
|
||||
|
||||
foreach (ForumBoard::query()->active()->ordered()->get() as $board) {
|
||||
$items[] = $this->urls->forumBoard($board);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function lastModified(): ?DateTimeInterface
|
||||
{
|
||||
return $this->newest(
|
||||
ForumCategory::query()->active()->max('updated_at'),
|
||||
ForumBoard::query()->active()->max('updated_at'),
|
||||
);
|
||||
}
|
||||
}
|
||||
31
app/Services/Sitemaps/Builders/ForumIndexSitemapBuilder.php
Normal file
31
app/Services/Sitemaps/Builders/ForumIndexSitemapBuilder.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Services\Sitemaps\AbstractSitemapBuilder;
|
||||
use App\Services\Sitemaps\SitemapUrlBuilder;
|
||||
use DateTimeInterface;
|
||||
|
||||
final class ForumIndexSitemapBuilder extends AbstractSitemapBuilder
|
||||
{
|
||||
public function __construct(private readonly SitemapUrlBuilder $urls)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'forum-index';
|
||||
}
|
||||
|
||||
public function items(): array
|
||||
{
|
||||
return [$this->urls->forumIndex()];
|
||||
}
|
||||
|
||||
public function lastModified(): ?DateTimeInterface
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Services\Sitemaps\SitemapUrl;
|
||||
use App\Services\Sitemaps\SitemapUrlBuilder;
|
||||
use cPad\Plugins\Forum\Models\ForumTopic;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
final class ForumThreadsSitemapBuilder extends AbstractIdShardableSitemapBuilder
|
||||
{
|
||||
public function __construct(private readonly SitemapUrlBuilder $urls)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'forum-threads';
|
||||
}
|
||||
|
||||
protected function shardConfigKey(): string
|
||||
{
|
||||
return 'forum-threads';
|
||||
}
|
||||
|
||||
protected function mapRecord(Model $record): ?SitemapUrl
|
||||
{
|
||||
return $this->urls->forumTopic($record);
|
||||
}
|
||||
|
||||
protected function query(): Builder
|
||||
{
|
||||
return ForumTopic::query()
|
||||
->visible()
|
||||
->whereHas('board', fn ($query) => $query->active())
|
||||
->orderBy('id');
|
||||
}
|
||||
|
||||
protected function qualifiedIdColumn(): string
|
||||
{
|
||||
return 'forum_topics.id';
|
||||
}
|
||||
}
|
||||
52
app/Services/Sitemaps/Builders/GoogleNewsSitemapBuilder.php
Normal file
52
app/Services/Sitemaps/Builders/GoogleNewsSitemapBuilder.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Services\Sitemaps\AbstractSitemapBuilder;
|
||||
use App\Services\Sitemaps\GoogleNewsSitemapUrl;
|
||||
use DateTimeInterface;
|
||||
use cPad\Plugins\News\Models\NewsArticle;
|
||||
|
||||
final class GoogleNewsSitemapBuilder extends AbstractSitemapBuilder
|
||||
{
|
||||
public function name(): string
|
||||
{
|
||||
return (string) \config('sitemaps.news.google_variant_name', 'news-google');
|
||||
}
|
||||
|
||||
public function items(): array
|
||||
{
|
||||
return NewsArticle::query()
|
||||
->published()
|
||||
->where('published_at', '>=', now()->subHours(max(1, (int) \config('sitemaps.news.google_lookback_hours', 48))))
|
||||
->orderByDesc('published_at')
|
||||
->limit(max(1, (int) \config('sitemaps.news.google_max_items', 1000)))
|
||||
->get()
|
||||
->map(function (NewsArticle $article): ?GoogleNewsSitemapUrl {
|
||||
if (trim((string) $article->slug) === '' || $article->published_at === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new GoogleNewsSitemapUrl(
|
||||
route('news.show', ['slug' => $article->slug]),
|
||||
trim((string) $article->title),
|
||||
$article->published_at,
|
||||
(string) \config('sitemaps.news.google_publication_name', 'Skinbase Nova'),
|
||||
(string) \config('sitemaps.news.google_language', 'en'),
|
||||
);
|
||||
})
|
||||
->filter(fn (?GoogleNewsSitemapUrl $item): bool => $item !== null && $item->title !== '')
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
public function lastModified(): ?DateTimeInterface
|
||||
{
|
||||
return $this->dateTime(NewsArticle::query()
|
||||
->published()
|
||||
->where('published_at', '>=', now()->subHours(max(1, (int) \config('sitemaps.news.google_lookback_hours', 48))))
|
||||
->max('published_at'));
|
||||
}
|
||||
}
|
||||
42
app/Services/Sitemaps/Builders/NewsSitemapBuilder.php
Normal file
42
app/Services/Sitemaps/Builders/NewsSitemapBuilder.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Services\Sitemaps\AbstractSitemapBuilder;
|
||||
use App\Services\Sitemaps\SitemapUrl;
|
||||
use App\Services\Sitemaps\SitemapUrlBuilder;
|
||||
use DateTimeInterface;
|
||||
use cPad\Plugins\News\Models\NewsArticle;
|
||||
|
||||
final class NewsSitemapBuilder extends AbstractSitemapBuilder
|
||||
{
|
||||
public function __construct(private readonly SitemapUrlBuilder $urls)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'news';
|
||||
}
|
||||
|
||||
public function items(): array
|
||||
{
|
||||
return NewsArticle::query()
|
||||
->published()
|
||||
->orderBy('id')
|
||||
->cursor()
|
||||
->map(fn (NewsArticle $article): ?SitemapUrl => $this->urls->news($article))
|
||||
->filter()
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
public function lastModified(): ?DateTimeInterface
|
||||
{
|
||||
return $this->dateTime(NewsArticle::query()
|
||||
->published()
|
||||
->max('updated_at'));
|
||||
}
|
||||
}
|
||||
61
app/Services/Sitemaps/Builders/StaticPagesSitemapBuilder.php
Normal file
61
app/Services/Sitemaps/Builders/StaticPagesSitemapBuilder.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Models\Page;
|
||||
use App\Services\Sitemaps\AbstractSitemapBuilder;
|
||||
use App\Services\Sitemaps\SitemapUrlBuilder;
|
||||
use DateTimeInterface;
|
||||
|
||||
final class StaticPagesSitemapBuilder extends AbstractSitemapBuilder
|
||||
{
|
||||
public function __construct(private readonly SitemapUrlBuilder $urls)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'static-pages';
|
||||
}
|
||||
|
||||
public function items(): array
|
||||
{
|
||||
$items = [
|
||||
$this->urls->staticRoute('/'),
|
||||
$this->urls->staticRoute('/faq'),
|
||||
$this->urls->staticRoute('/rules-and-guidelines'),
|
||||
$this->urls->staticRoute('/privacy-policy'),
|
||||
$this->urls->staticRoute('/terms-of-service'),
|
||||
$this->urls->staticRoute('/staff'),
|
||||
];
|
||||
|
||||
$marketingPages = Page::query()
|
||||
->published()
|
||||
->whereIn('slug', ['about', 'help'])
|
||||
->get()
|
||||
->keyBy('slug');
|
||||
|
||||
if ($marketingPages->has('about')) {
|
||||
$items[] = $this->urls->page($marketingPages['about'], '/about');
|
||||
}
|
||||
|
||||
if ($marketingPages->has('help')) {
|
||||
$items[] = $this->urls->page($marketingPages['help'], '/help');
|
||||
}
|
||||
|
||||
$excluded = array_values((array) config('sitemaps.static_page_excluded_slugs', []));
|
||||
|
||||
foreach (Page::query()->published()->whereNotIn('slug', $excluded)->orderBy('slug')->get() as $page) {
|
||||
$items[] = $this->urls->page($page, '/pages/' . $page->slug);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function lastModified(): ?DateTimeInterface
|
||||
{
|
||||
return $this->dateTime(Page::query()->published()->max('updated_at'));
|
||||
}
|
||||
}
|
||||
45
app/Services/Sitemaps/Builders/StoriesSitemapBuilder.php
Normal file
45
app/Services/Sitemaps/Builders/StoriesSitemapBuilder.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Models\Story;
|
||||
use App\Services\Sitemaps\SitemapUrl;
|
||||
use App\Services\Sitemaps\SitemapUrlBuilder;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
final class StoriesSitemapBuilder extends AbstractIdShardableSitemapBuilder
|
||||
{
|
||||
public function __construct(private readonly SitemapUrlBuilder $urls)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'stories';
|
||||
}
|
||||
|
||||
protected function shardConfigKey(): string
|
||||
{
|
||||
return 'stories';
|
||||
}
|
||||
|
||||
protected function mapRecord(Model $record): ?SitemapUrl
|
||||
{
|
||||
return $this->urls->story($record);
|
||||
}
|
||||
|
||||
protected function query(): Builder
|
||||
{
|
||||
return Story::query()
|
||||
->published()
|
||||
->orderBy('id');
|
||||
}
|
||||
|
||||
protected function qualifiedIdColumn(): string
|
||||
{
|
||||
return 'stories.id';
|
||||
}
|
||||
}
|
||||
45
app/Services/Sitemaps/Builders/TagsSitemapBuilder.php
Normal file
45
app/Services/Sitemaps/Builders/TagsSitemapBuilder.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Models\Tag;
|
||||
use App\Services\Sitemaps\AbstractSitemapBuilder;
|
||||
use App\Services\Sitemaps\SitemapUrl;
|
||||
use App\Services\Sitemaps\SitemapUrlBuilder;
|
||||
use DateTimeInterface;
|
||||
|
||||
final class TagsSitemapBuilder extends AbstractSitemapBuilder
|
||||
{
|
||||
public function __construct(private readonly SitemapUrlBuilder $urls)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'tags';
|
||||
}
|
||||
|
||||
public function items(): array
|
||||
{
|
||||
return Tag::query()
|
||||
->where('is_active', true)
|
||||
->where('usage_count', '>', 0)
|
||||
->whereHas('artworks', fn ($query) => $query->public()->published())
|
||||
->orderByDesc('usage_count')
|
||||
->orderBy('slug')
|
||||
->get()
|
||||
->map(fn (Tag $tag): SitemapUrl => $this->urls->tag($tag))
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
public function lastModified(): ?DateTimeInterface
|
||||
{
|
||||
return $this->dateTime(Tag::query()
|
||||
->where('is_active', true)
|
||||
->where('usage_count', '>', 0)
|
||||
->max('updated_at'));
|
||||
}
|
||||
}
|
||||
76
app/Services/Sitemaps/Builders/UsersSitemapBuilder.php
Normal file
76
app/Services/Sitemaps/Builders/UsersSitemapBuilder.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Sitemaps\Builders;
|
||||
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Collection;
|
||||
use App\Models\NovaCard;
|
||||
use App\Models\Story;
|
||||
use App\Models\User;
|
||||
use App\Services\Sitemaps\SitemapUrl;
|
||||
use App\Services\Sitemaps\SitemapUrlBuilder;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
final class UsersSitemapBuilder extends AbstractIdShardableSitemapBuilder
|
||||
{
|
||||
public function __construct(private readonly SitemapUrlBuilder $urls)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'users';
|
||||
}
|
||||
|
||||
protected function shardConfigKey(): string
|
||||
{
|
||||
return 'users';
|
||||
}
|
||||
|
||||
protected function mapRecord(Model $record): ?SitemapUrl
|
||||
{
|
||||
return $this->urls->profile($record, $record->updated_at);
|
||||
}
|
||||
|
||||
protected function query(): Builder
|
||||
{
|
||||
return User::query()
|
||||
->where('is_active', true)
|
||||
->whereNull('deleted_at')
|
||||
->whereNotNull('username')
|
||||
->where('username', '!=', '')
|
||||
->where(function (Builder $builder): void {
|
||||
$builder->whereExists(
|
||||
Artwork::query()
|
||||
->selectRaw('1')
|
||||
->public()
|
||||
->published()
|
||||
->whereColumn('artworks.user_id', 'users.id')
|
||||
)->orWhereExists(
|
||||
Collection::query()
|
||||
->selectRaw('1')
|
||||
->public()
|
||||
->whereNull('canonical_collection_id')
|
||||
->whereColumn('collections.user_id', 'users.id')
|
||||
)->orWhereExists(
|
||||
NovaCard::query()
|
||||
->selectRaw('1')
|
||||
->publiclyVisible()
|
||||
->whereColumn('nova_cards.user_id', 'users.id')
|
||||
)->orWhereExists(
|
||||
Story::query()
|
||||
->selectRaw('1')
|
||||
->published()
|
||||
->whereColumn('stories.creator_id', 'users.id')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
protected function qualifiedIdColumn(): string
|
||||
{
|
||||
return 'users.id';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user