hasComposition()

* renderRegion()
* draft and published-state handling
* document and placement scheduling
* disabled-placement filtering
* ordered region rendering
* safe fallback when composition tables or document views are missing
* compatibility with common alternative column names
This commit is contained in:
Mark Gregory
2026-07-27 09:43:42 +01:00
parent a211b10e29
commit c972808d0a
22 changed files with 455 additions and 754 deletions
+13 -62
View File
@@ -1,11 +1,11 @@
<?php
declare(strict_types=1);
namespace Modules\Content\Services;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\View;
use Modules\Content\Models\ContentDocument;
use Modules\Content\Models\PageContent;
use Throwable;
@@ -13,71 +13,22 @@ final class PageRenderer
{
public function hasComposition(string $pageKey): bool
{
try {
return Schema::hasTable('page_content')
&& PageContent::query()->where('page_key', $pageKey)->where('is_enabled', true)->exists();
} catch (Throwable) {
return false;
}
return Schema::hasTable('page_content') && PageContent::where('page_key',$pageKey)->exists();
}
public function renderRegion(string $pageKey, string $region): string
public function renderRegion(string $pageKey, string $region, bool $preview=false): string
{
if (! $this->hasComposition($pageKey)) {
return '';
}
$preview = Route::currentRouteNamed('content.pages.preview');
$placements = PageContent::query()
->with('document.type')
->where('page_key', $pageKey)
->where('region', $region)
->where('is_enabled', true)
->where(fn ($query) => $query->whereNull('starts_at')->orWhere('starts_at', '<=', now()))
->where(fn ($query) => $query->whereNull('ends_at')->orWhere('ends_at', '>', now()))
->orderBy('sort_order')
->get();
return $placements->map(function (PageContent $placement) use ($preview): string {
$document = $placement->document;
if (! $document || ! $document->type || ! $this->isRenderable($document, $preview)) {
return '';
}
$view = 'stylist-theme-saasland::documents.'.$document->type->renderer;
if (! View::exists($view)) {
$view = 'content::documents.unsupported';
}
$rendered = View::make($view, [
'document' => $document,
'data' => $document->data ?? [],
'placement' => $placement,
'isCompositionPreview' => $preview,
])->render();
if ($preview && $document->status !== 'published') {
return '<div style="position:relative"><div style="position:absolute;z-index:1000;top:8px;right:8px;padding:5px 9px;border-radius:999px;background:#f59e0b;color:#111827;font:600 12px/1 sans-serif">Draft preview</div>'.$rendered.'</div>';
}
return $rendered;
})->implode("\n");
if(!Schema::hasTable('page_content')) return '';
return PageContent::query()->with('document.type')->where('page_key',$pageKey)->where('region',$region)->where('is_enabled',true)->orderBy('sort_order')->get()
->filter(fn(PageContent $p)=>$preview || ($this->active($p) && $p->document && $this->active($p->document) && $p->document->status==='published'))
->map(function(PageContent $p) use($preview){
try { $view='stylist-theme-saasland::documents.'.$p->document->type->key; if(!View::exists($view)) return ''; $data=$p->document->data ?? []; return View::make($view, array_merge($data,['data'=>$data,'document'=>$p->document,'placement'=>$p,'preview'=>$preview]))->render(); }
catch(Throwable $e){ report($e); return ''; }
})->implode('');
}
private function isRenderable(ContentDocument $document, bool $preview): bool
private function active(object $model): bool
{
if ($preview) {
return true;
}
if ($document->status !== 'published') {
return false;
}
if ($document->published_at?->isFuture()) {
return false;
}
return ! $document->expires_at?->isPast();
return (!$model->published_at || $model->published_at->lte(now())) && (!$model->expires_at || $model->expires_at->gt(now()));
}
}