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()]); } public function create(Request $request): View { $types = ContentType::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]); } public function store(SaveContentDocumentRequest $request): RedirectResponse { $document = ContentDocument::create($request->payload()); 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()]); } 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'], ]); $document->update([ 'content_type_id' => $validated['content_type_id'], 'title' => $validated['title'], 'slug' => filled($validated['slug'] ?? null) ? $validated['slug'] : str($validated['title'])->slug(), 'status' => $validated['status'], 'data' => json_decode($validated['data_json'], true, flags: JSON_THROW_ON_ERROR), 'published_at' => $validated['published_at'] ?? null, 'expires_at' => $validated['expires_at'] ?? null, ]); return response()->json([ 'saved' => true, 'saved_at' => now()->toIso8601String(), 'updated_at' => $document->fresh()->updated_at?->toIso8601String(), ]); } public function preview(Request $request): JsonResponse { $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' => '
No Saasland renderer exists for this content type yet.
', '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->title = $document->title.' copy'; $copy->slug = str($copy->title)->slug().'-'.str()->lower(str()->random(6)); $copy->status = 'draft'; $copy->save(); return redirect()->route('content.documents.edit',$copy)->with('status','Independent copy created.'); } public function destroy(ContentDocument $document): RedirectResponse { abort_if($document->placements()->exists(), 422, 'A document used by a page cannot be deleted.'); $document->delete(); return redirect()->route('content.index')->with('status','Content document deleted.'); } }