fixed content module ( well reverted)

This commit is contained in:
Mark Gregory
2026-07-27 10:05:53 +01:00
parent 8dd9d77762
commit 712b985bc3
22 changed files with 774 additions and 978 deletions
@@ -1,16 +1,14 @@
<?php
declare(strict_types=1);
namespace Modules\Content\Http\Controllers;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\View as ViewFacade;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
use Modules\Content\Http\Requests\SaveContentDocumentRequest;
use Modules\Content\Models\ContentDocument;
use Modules\Content\Models\ContentType;
@@ -18,112 +16,131 @@ final class ContentDocumentController extends Controller
{
public function index(Request $request): View
{
$documents = ContentDocument::query()->with(['type','placements'])
->when($request->filled('search'), fn ($q) => $q->where(fn ($q) => $q->where('title','like','%'.$request->string('search').'%')->orWhere('slug','like','%'.$request->string('search').'%')))
->when($request->filled('type'), fn ($q) => $q->whereHas('type', fn ($q) => $q->where('key',$request->string('type'))))
->when($request->filled('status'), fn ($q) => $q->where('status',$request->string('status')))
->latest()->paginate(25)->withQueryString();
return view('content::admin.index', ['documents'=>$documents, 'types'=>ContentType::orderBy('name')->get()]);
$documents = ContentDocument::query()
->with('type')
->withCount('placements')
->when($request->string('type')->isNotEmpty(), fn ($query) => $query->whereHas('type', fn ($type) => $type->where('key', $request->string('type')->toString())))
->when($request->string('status')->isNotEmpty(), fn ($query) => $query->where('status', $request->string('status')->toString()))
->when($request->string('search')->isNotEmpty(), fn ($query) => $query->where('title', 'like', '%'.$request->string('search')->toString().'%'))
->orderBy('title')
->paginate(30)
->withQueryString();
return view('content::admin.index', [
'documents' => $documents,
'types' => ContentType::query()->where('is_active', true)->orderBy('name')->get(),
]);
}
public function create(Request $request): View
{
$types = ContentType::orderBy('name')->get();
$types = ContentType::query()->where('is_active', true)->orderBy('name')->get();
$selectedType = $types->firstWhere('id', (int) $request->integer('type')) ?? $types->first();
return view('content::admin.form', ['document'=>new ContentDocument(['status'=>'draft','content_type_id'=>$selectedType?->id]), 'types'=>$types, 'selectedType'=>$selectedType]);
return view('content::admin.form', [
'document' => new ContentDocument(['status' => 'draft', 'data' => []]),
'types' => $types,
'selectedType' => $selectedType,
]);
}
public function store(SaveContentDocumentRequest $request): RedirectResponse
public function store(Request $request): RedirectResponse
{
$document = ContentDocument::create($request->payload());
return redirect()->route('content.documents.edit',$document)->with('status','Content document created.');
$validated = $this->validateDocument($request);
$type = ContentType::query()->where('is_active', true)->findOrFail($validated['content_type_id']);
$document = ContentDocument::query()->create([
'content_type_id' => $type->id,
'title' => $validated['title'],
'slug' => $this->uniqueSlug($validated['slug'] ?: $validated['title']),
'status' => $validated['status'],
'published_at' => $validated['status'] === 'published' ? ($validated['published_at'] ?? now()) : null,
'expires_at' => $validated['expires_at'] ?? null,
'data' => json_decode($validated['data_json'], true, 512, JSON_THROW_ON_ERROR),
'created_by' => $request->user()?->id,
'updated_by' => $request->user()?->id,
]);
return redirect()->route('content.documents.edit', $document)->with('status', 'Content document created.');
}
public function edit(ContentDocument $document): View
{
$document->load('placements');
return view('content::admin.form', ['document'=>$document, 'types'=>ContentType::orderBy('name')->get(), 'selectedType'=>$document->type()->first()]);
}
$document->load('type', 'placements');
public function update(SaveContentDocumentRequest $request, ContentDocument $document): RedirectResponse
{
$document->update($request->payload());
return back()->with('status','Content document saved.');
}
public function autosave(Request $request, ContentDocument $document): JsonResponse
{
$validated = $request->validate([
'content_type_id' => ['required', 'integer', 'exists:content_types,id'],
'title' => ['required', 'string', 'max:255'],
'slug' => ['nullable', 'string', 'max:255'],
'status' => ['required', 'in:draft,published,archived'],
'data_json' => ['required', 'json'],
'published_at' => ['nullable', 'date'],
'expires_at' => ['nullable', 'date', 'after:published_at'],
return view('content::admin.form', [
'document' => $document,
'types' => ContentType::query()->where('is_active', true)->orderBy('name')->get(),
'selectedType' => $document->type,
]);
}
public function update(Request $request, ContentDocument $document): RedirectResponse
{
$validated = $this->validateDocument($request, $document);
$document->update([
'content_type_id' => $validated['content_type_id'],
'title' => $validated['title'],
'slug' => filled($validated['slug'] ?? null) ? $validated['slug'] : str($validated['title'])->slug(),
'slug' => $this->uniqueSlug($validated['slug'] ?: $validated['title'], $document),
'status' => $validated['status'],
'data' => json_decode($validated['data_json'], true, flags: JSON_THROW_ON_ERROR),
'published_at' => $validated['published_at'] ?? null,
'published_at' => $validated['status'] === 'published' ? ($validated['published_at'] ?? $document->published_at ?? now()) : null,
'expires_at' => $validated['expires_at'] ?? null,
'data' => json_decode($validated['data_json'], true, 512, JSON_THROW_ON_ERROR),
'updated_by' => $request->user()?->id,
]);
return response()->json([
'saved' => true,
'saved_at' => now()->toIso8601String(),
'updated_at' => $document->fresh()->updated_at?->toIso8601String(),
]);
return back()->with('status', 'Content document updated.');
}
public function preview(Request $request): JsonResponse
public function duplicate(Request $request, ContentDocument $document): RedirectResponse
{
$validated = $request->validate([
'content_type_id' => ['required', 'integer', 'exists:content_types,id'],
'data_json' => ['required', 'json'],
]);
$type = ContentType::query()->findOrFail($validated['content_type_id']);
$view = 'stylist-theme-saasland::documents.'.$type->key;
if (! ViewFacade::exists($view)) {
return response()->json([
'html' => '<div style="padding:2rem;font-family:system-ui;color:#71717a">No Saasland renderer exists for this content type yet.</div>',
'view' => $view,
]);
}
$data = json_decode($validated['data_json'], true, flags: JSON_THROW_ON_ERROR);
return response()->json([
'html' => ViewFacade::make($view, array_merge($data, [
'data' => $data,
'document' => new ContentDocument(['data' => $data]),
'placement' => null,
'preview' => true,
]))->render(),
'view' => $view,
]);
}
public function duplicate(ContentDocument $document): RedirectResponse
{
$copy = $document->replicate(['slug']);
$copy = $document->replicate(['slug', 'status', 'published_at', 'created_by', 'updated_by']);
$copy->title = $document->title.' copy';
$copy->slug = str($copy->title)->slug().'-'.str()->lower(str()->random(6));
$copy->slug = $this->uniqueSlug($copy->title);
$copy->status = 'draft';
$copy->published_at = null;
$copy->created_by = $request->user()?->id;
$copy->updated_by = $request->user()?->id;
$copy->save();
return redirect()->route('content.documents.edit',$copy)->with('status','Independent copy created.');
return redirect()->route('content.documents.edit', $copy)->with('status', 'Independent draft copy created.');
}
public function destroy(ContentDocument $document): RedirectResponse
{
abort_if($document->placements()->exists(), 422, 'A document used by a page cannot be deleted.');
if ($document->placements()->exists()) {
return back()->withErrors(['document' => 'Remove this document from every page before deleting it.']);
}
$document->delete();
return redirect()->route('content.index')->with('status','Content document deleted.');
return redirect()->route('content.index')->with('status', 'Content document deleted.');
}
private function validateDocument(Request $request, ?ContentDocument $document = null): array
{
return $request->validate([
'content_type_id' => ['required', 'integer', Rule::exists('content_types', 'id')->where('is_active', true)],
'title' => ['required', 'string', 'max:255'],
'slug' => ['nullable', 'string', 'max:255'],
'status' => ['required', Rule::in(['draft', 'published'])],
'published_at' => ['nullable', 'date'],
'expires_at' => ['nullable', 'date', 'after:published_at'],
'data_json' => ['required', 'json'],
]);
}
private function uniqueSlug(string $value, ?ContentDocument $except = null): string
{
$base = Str::slug($value) ?: 'content-document';
$slug = $base;
$suffix = 2;
while (ContentDocument::withTrashed()->where('slug', $slug)->when($except, fn ($query) => $query->whereKeyNot($except->getKey()))->exists()) {
$slug = $base.'-'.$suffix++;
}
return $slug;
}
}