165 lines
6.1 KiB
PHP
165 lines
6.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\V1;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\Api\V1\StorePressReleaseRequest;
|
|
use App\Http\Requests\Api\V1\UpdatePressReleaseRequest;
|
|
use App\Http\Resources\PressReleaseResource;
|
|
use App\Models\Company;
|
|
use App\Models\PressRelease;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
|
use Illuminate\Http\Response;
|
|
use Illuminate\Support\Str;
|
|
|
|
class PressReleaseController extends Controller
|
|
{
|
|
public function index(Request $request): AnonymousResourceCollection
|
|
{
|
|
abort_unless($request->user()->tokenCan('press-releases:read'), 403);
|
|
|
|
$pressReleases = PressRelease::withoutGlobalScopes()
|
|
->where('user_id', $request->user()->id)
|
|
->with(['company:id,name,portal,legacy_portal,legacy_id', 'category.translations', 'images'])
|
|
->when($request->query('status'), fn ($query, string $status) => $query->where('status', $status))
|
|
->latest()
|
|
->paginate(min((int) $request->query('per_page', 25), 100));
|
|
|
|
return PressReleaseResource::collection($pressReleases);
|
|
}
|
|
|
|
public function store(StorePressReleaseRequest $request): JsonResponse
|
|
{
|
|
$validated = $request->validated();
|
|
$company = $this->findOwnedCompany((int) $validated['company_id'], $request);
|
|
abort_unless($company !== null, 403);
|
|
|
|
$pressRelease = PressRelease::withoutGlobalScopes()->create([
|
|
...$validated,
|
|
'uuid' => (string) Str::uuid(),
|
|
'user_id' => $request->user()->id,
|
|
'portal' => $company->portal->value,
|
|
'slug' => $this->uniqueSlug(
|
|
Str::slug($validated['title']),
|
|
$company->portal->value,
|
|
$validated['language'],
|
|
),
|
|
'status' => $validated['status'] ?? 'draft',
|
|
]);
|
|
|
|
return PressReleaseResource::make(
|
|
$pressRelease->load(['company:id,name,portal,legacy_portal,legacy_id', 'category.translations', 'images'])
|
|
)->response()->setStatusCode(201);
|
|
}
|
|
|
|
public function show(Request $request, int $pressRelease): PressReleaseResource
|
|
{
|
|
abort_unless($request->user()->tokenCan('press-releases:read'), 403);
|
|
$pressRelease = $this->findOwnedPressRelease($pressRelease, $request);
|
|
abort_unless($pressRelease !== null, 403);
|
|
|
|
return PressReleaseResource::make(
|
|
$pressRelease->load(['company:id,name,portal,legacy_portal,legacy_id', 'category.translations', 'images'])
|
|
);
|
|
}
|
|
|
|
public function update(UpdatePressReleaseRequest $request, int $pressRelease): PressReleaseResource|JsonResponse
|
|
{
|
|
$pressRelease = $this->findOwnedPressRelease($pressRelease, $request);
|
|
abort_unless($pressRelease !== null, 403);
|
|
|
|
if (! in_array($pressRelease->status->value, ['draft', 'rejected'], true)) {
|
|
return response()->json([
|
|
'message' => 'Only draft or rejected press releases may be edited.',
|
|
], 409);
|
|
}
|
|
|
|
$validated = $request->validated();
|
|
$company = isset($validated['company_id'])
|
|
? $this->findOwnedCompany((int) $validated['company_id'], $request)
|
|
: $this->findOwnedCompany((int) $pressRelease->company_id, $request);
|
|
abort_unless($company !== null, 403);
|
|
|
|
$language = $validated['language'] ?? $pressRelease->language;
|
|
$title = $validated['title'] ?? $pressRelease->title;
|
|
$portal = $company->portal->value;
|
|
|
|
$pressRelease->fill([
|
|
...$validated,
|
|
'portal' => $portal,
|
|
]);
|
|
|
|
if (
|
|
array_key_exists('title', $validated)
|
|
|| array_key_exists('language', $validated)
|
|
|| array_key_exists('company_id', $validated)
|
|
) {
|
|
$pressRelease->slug = $this->uniqueSlug(Str::slug($title), $portal, $language, $pressRelease->id);
|
|
}
|
|
|
|
$pressRelease->save();
|
|
|
|
return PressReleaseResource::make(
|
|
$pressRelease->load(['company:id,name,portal,legacy_portal,legacy_id', 'category.translations', 'images'])
|
|
);
|
|
}
|
|
|
|
public function destroy(Request $request, int $pressRelease): JsonResponse|Response
|
|
{
|
|
abort_unless($request->user()->tokenCan('press-releases:write'), 403);
|
|
$pressRelease = $this->findOwnedPressRelease($pressRelease, $request);
|
|
abort_unless($pressRelease !== null, 403);
|
|
|
|
if ($pressRelease->status->value === 'published') {
|
|
return response()->json([
|
|
'message' => 'Published press releases cannot be deleted via API.',
|
|
], 409);
|
|
}
|
|
|
|
$pressRelease->delete();
|
|
|
|
return response()->noContent();
|
|
}
|
|
|
|
private function findOwnedCompany(int $companyId, Request $request): ?Company
|
|
{
|
|
return Company::withoutGlobalScopes()
|
|
->whereKey($companyId)
|
|
->where(function ($query) use ($request): void {
|
|
$query->where('owner_user_id', $request->user()->id)
|
|
->orWhereHas('users', fn ($users) => $users->whereKey($request->user()->id));
|
|
})
|
|
->first();
|
|
}
|
|
|
|
private function findOwnedPressRelease(int $pressReleaseId, Request $request): ?PressRelease
|
|
{
|
|
return PressRelease::withoutGlobalScopes()
|
|
->whereKey($pressReleaseId)
|
|
->where('user_id', $request->user()->id)
|
|
->first();
|
|
}
|
|
|
|
private function uniqueSlug(string $baseSlug, string $portal, string $language, ?int $excludeId = null): string
|
|
{
|
|
$slug = $baseSlug !== '' ? $baseSlug : Str::random(8);
|
|
$candidate = $slug;
|
|
$suffix = 2;
|
|
|
|
while (
|
|
PressRelease::withoutGlobalScopes()
|
|
->where('portal', $portal)
|
|
->where('language', $language)
|
|
->where('slug', $candidate)
|
|
->when($excludeId !== null, fn ($query) => $query->whereKeyNot($excludeId))
|
|
->exists()
|
|
) {
|
|
$candidate = "{$slug}-{$suffix}";
|
|
$suffix++;
|
|
}
|
|
|
|
return $candidate;
|
|
}
|
|
}
|