158 lines
6.3 KiB
PHP
158 lines
6.3 KiB
PHP
<?php
|
|
|
|
namespace Modules\Content\Http\Controllers;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Validation\Rule;
|
|
use Illuminate\View\View;
|
|
use Modules\Content\Models\ContentDocument;
|
|
use Modules\Content\Models\PageContent;
|
|
|
|
final class PageCompositionController extends Controller
|
|
{
|
|
private const REGIONS = ['header', 'body', 'footer'];
|
|
|
|
public function edit(string $pageKey): View
|
|
{
|
|
$placements = PageContent::query()
|
|
->with('document.type')
|
|
->where('page_key', $pageKey)
|
|
->orderByRaw("CASE region WHEN 'header' THEN 1 WHEN 'body' THEN 2 ELSE 3 END")
|
|
->orderBy('sort_order')
|
|
->get()
|
|
->groupBy('region');
|
|
|
|
$documents = ContentDocument::query()
|
|
->with('type')
|
|
->withCount('placements')
|
|
->orderBy('title')
|
|
->get();
|
|
|
|
return view('content::admin.composition', compact('pageKey', 'placements', 'documents'));
|
|
}
|
|
|
|
public function preview(string $pageKey): View
|
|
{
|
|
abort_unless($pageKey === 'home', 404, 'This phase currently provides the Saasland homepage preview.');
|
|
|
|
return view('stylist-theme-saasland::home', ['pageKey' => $pageKey]);
|
|
}
|
|
|
|
public function store(Request $request, string $pageKey): RedirectResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'content_document_id' => ['required', 'integer', Rule::exists('content_documents', 'id')->whereNull('deleted_at')],
|
|
'region' => ['required', Rule::in(self::REGIONS)],
|
|
]);
|
|
|
|
$document = ContentDocument::query()->with('type')->findOrFail($validated['content_document_id']);
|
|
abort_unless(in_array($validated['region'], $document->type?->allowed_regions ?? [], true), 422, 'This content type is not allowed in the selected region.');
|
|
|
|
$nextOrder = ((int) PageContent::query()->where('page_key', $pageKey)->where('region', $validated['region'])->max('sort_order')) + 10;
|
|
PageContent::query()->create([
|
|
'page_key' => $pageKey,
|
|
'content_document_id' => $document->id,
|
|
'region' => $validated['region'],
|
|
'sort_order' => $nextOrder,
|
|
'is_enabled' => true,
|
|
]);
|
|
|
|
return back()->with('status', 'Document added to the '.$validated['region'].' region.');
|
|
}
|
|
|
|
public function update(Request $request, PageContent $placement): RedirectResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'is_enabled' => ['nullable', 'boolean'],
|
|
'starts_at' => ['nullable', 'date'],
|
|
'ends_at' => ['nullable', 'date', 'after:starts_at'],
|
|
]);
|
|
|
|
$placement->update([
|
|
'is_enabled' => $request->boolean('is_enabled'),
|
|
'starts_at' => $validated['starts_at'] ?? null,
|
|
'ends_at' => $validated['ends_at'] ?? null,
|
|
]);
|
|
|
|
return back()->with('status', 'Page placement updated.');
|
|
}
|
|
|
|
public function destroy(PageContent $placement): RedirectResponse
|
|
{
|
|
$placement->delete();
|
|
$this->normaliseRegion($placement->page_key, $placement->region);
|
|
|
|
return back()->with('status', 'Document removed from this page. The reusable document was not deleted.');
|
|
}
|
|
|
|
public function duplicate(Request $request, PageContent $placement): RedirectResponse
|
|
{
|
|
$source = $placement->document()->firstOrFail();
|
|
$copy = DB::transaction(function () use ($request, $placement, $source): ContentDocument {
|
|
$copy = $source->replicate(['slug', 'status', 'published_at', 'created_by', 'updated_by']);
|
|
$copy->title = $source->title.' copy';
|
|
$copy->slug = $source->slug.'-copy-'.strtolower(str()->random(6));
|
|
$copy->status = 'draft';
|
|
$copy->published_at = null;
|
|
$copy->created_by = $request->user()?->id;
|
|
$copy->updated_by = $request->user()?->id;
|
|
$copy->save();
|
|
|
|
PageContent::query()->create([
|
|
'page_id' => $placement->page_id,
|
|
'page_key' => $placement->page_key,
|
|
'content_document_id' => $copy->id,
|
|
'region' => $placement->region,
|
|
'sort_order' => $placement->sort_order + 5,
|
|
'is_enabled' => false,
|
|
'visibility_rules' => $placement->visibility_rules,
|
|
'presentation' => $placement->presentation,
|
|
]);
|
|
|
|
return $copy;
|
|
});
|
|
|
|
$this->normaliseRegion($placement->page_key, $placement->region);
|
|
|
|
return redirect()->route('content.documents.edit', $copy)->with('status', 'Page-specific draft copy created and added after the original.');
|
|
}
|
|
|
|
public function reorder(Request $request, string $pageKey): JsonResponse|RedirectResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'region' => ['required', Rule::in(self::REGIONS)],
|
|
'placements' => ['required', 'array'],
|
|
'placements.*' => ['required', 'integer'],
|
|
]);
|
|
|
|
$ids = array_values(array_unique(array_map('intval', $validated['placements'])));
|
|
$owned = PageContent::query()->where('page_key', $pageKey)->where('region', $validated['region'])->whereIn('id', $ids)->pluck('id')->all();
|
|
abort_unless(count($owned) === count($ids), 422, 'One or more placements do not belong to this page region.');
|
|
|
|
DB::transaction(function () use ($ids): void {
|
|
PageContent::query()->whereIn('id', $ids)->increment('sort_order', 100000);
|
|
foreach ($ids as $index => $id) {
|
|
PageContent::query()->whereKey($id)->update(['sort_order' => ($index + 1) * 10]);
|
|
}
|
|
});
|
|
|
|
return $request->expectsJson()
|
|
? response()->json(['message' => 'Order saved.'])
|
|
: back()->with('status', 'Page order updated.');
|
|
}
|
|
|
|
private function normaliseRegion(?string $pageKey, string $region): void
|
|
{
|
|
$ids = PageContent::query()->where('page_key', $pageKey)->where('region', $region)->orderBy('sort_order')->pluck('id');
|
|
DB::transaction(function () use ($ids): void {
|
|
foreach ($ids as $index => $id) {
|
|
PageContent::query()->whereKey($id)->update(['sort_order' => ($index + 1) * 10]);
|
|
}
|
|
});
|
|
}
|
|
}
|