147 lines
6.0 KiB
PHP
147 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace Modules\Content\Http\Controllers;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Validation\Rule;
|
|
use Illuminate\View\View;
|
|
use Modules\Content\Models\ContentDocument;
|
|
use Modules\Content\Models\ContentType;
|
|
|
|
final class ContentDocumentController extends Controller
|
|
{
|
|
public function index(Request $request): View
|
|
{
|
|
$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::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', 'data' => []]),
|
|
'types' => $types,
|
|
'selectedType' => $selectedType,
|
|
]);
|
|
}
|
|
|
|
public function store(Request $request): RedirectResponse
|
|
{
|
|
$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('type', 'placements');
|
|
|
|
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' => $this->uniqueSlug($validated['slug'] ?: $validated['title'], $document),
|
|
'status' => $validated['status'],
|
|
'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 back()->with('status', 'Content document updated.');
|
|
}
|
|
|
|
public function duplicate(Request $request, ContentDocument $document): RedirectResponse
|
|
{
|
|
$copy = $document->replicate(['slug', 'status', 'published_at', 'created_by', 'updated_by']);
|
|
$copy->title = $document->title.' copy';
|
|
$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 draft copy created.');
|
|
}
|
|
|
|
public function destroy(ContentDocument $document): RedirectResponse
|
|
{
|
|
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.');
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|