Initial commit: Pireon Admin — headless admin panel and public API
Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer collective, backed by the Pireon OAuth identity server. The public site is a separate, server-rendered application consuming /api/public/v1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
124
app/Http/Controllers/Api/PublicApi/V1/ShowcaseController.php
Normal file
124
app/Http/Controllers/Api/PublicApi/V1/ShowcaseController.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\PublicApi\V1;
|
||||
|
||||
use App\Http\Resources\PublicApi\PublicClientResource;
|
||||
use App\Http\Resources\PublicApi\PublicServiceResource;
|
||||
use App\Http\Resources\PublicApi\PublicTalkResource;
|
||||
use App\Http\Resources\PublicApi\PublicTestimonialResource;
|
||||
use App\Models\Client;
|
||||
use App\Models\Service;
|
||||
use App\Models\Talk;
|
||||
use App\Models\Testimonial;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
|
||||
/**
|
||||
* The pieces a landing page is made of.
|
||||
*
|
||||
* Four collections rather than one "home page" payload, and no ordering of
|
||||
* sections anywhere in here: what gets composed, and in what order, is the
|
||||
* frontend's business. This side only knows what exists.
|
||||
*/
|
||||
class ShowcaseController extends PublicController
|
||||
{
|
||||
/**
|
||||
* GET /api/public/v1/services
|
||||
*/
|
||||
public function services(Request $request): AnonymousResourceCollection
|
||||
{
|
||||
$services = Service::query()
|
||||
->with(['technologies'])
|
||||
->withCount('projects')
|
||||
->unless($this->isPreview($request), fn (Builder $q) => $q->published())
|
||||
->when($request->boolean('featured'), fn (Builder $q) => $q->where('is_featured', true))
|
||||
->orderBy('position')
|
||||
->get();
|
||||
|
||||
return PublicServiceResource::collection($services);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/public/v1/services/{slug}
|
||||
*
|
||||
* Resolved by slug within the active locale, like every other record here.
|
||||
*/
|
||||
public function service(Request $request, string $slug): PublicServiceResource
|
||||
{
|
||||
$service = Service::query()
|
||||
->with([
|
||||
// The work that backs the offer, and what people said about
|
||||
// it: a service page with neither is a promise.
|
||||
'projects' => fn ($q) => $q->published()->with(['technologies', 'coverMedia']),
|
||||
'technologies',
|
||||
'testimonials' => fn ($q) => $q->public()->with(['client.logoMedia', 'avatarMedia'])->orderBy('position'),
|
||||
])
|
||||
->whereSlug($slug)
|
||||
->unless($this->isPreview($request), fn (Builder $q) => $q->published())
|
||||
->firstOrFail();
|
||||
|
||||
return PublicServiceResource::make($service)->detailed();
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/public/v1/clients
|
||||
*
|
||||
* The wall of logos. Organisations that asked not to appear are absent
|
||||
* whatever else is true of them.
|
||||
*/
|
||||
public function clients(Request $request): AnonymousResourceCollection
|
||||
{
|
||||
$clients = Client::query()
|
||||
->with('logoMedia')
|
||||
->withCount(['projects' => fn (Builder $q) => $q->published()])
|
||||
->public()
|
||||
// A client with nothing published behind it would be a logo the
|
||||
// site cannot justify showing.
|
||||
->when($request->boolean('with_projects', true), fn (Builder $q) => $q
|
||||
->whereHas('projects', fn (Builder $p) => $p->published()))
|
||||
->orderBy('position')
|
||||
->orderBy('name')
|
||||
->get();
|
||||
|
||||
return PublicClientResource::collection($clients);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/public/v1/testimonials
|
||||
*/
|
||||
public function testimonials(Request $request): AnonymousResourceCollection
|
||||
{
|
||||
$testimonials = Testimonial::query()
|
||||
->with(['client.logoMedia', 'avatarMedia'])
|
||||
->public()
|
||||
->when($request->filled('client'), fn (Builder $q) => $q
|
||||
->whereHas('client', fn (Builder $c) => $c->where('slug', $request->string('client'))))
|
||||
->when($request->filled('service'), fn (Builder $q) => $q
|
||||
->whereHas('service', fn (Builder $s) => $s
|
||||
->where('slug->'.app()->getLocale(), $request->string('service'))))
|
||||
->orderBy('position')
|
||||
->orderByDesc('given_at')
|
||||
->get();
|
||||
|
||||
return PublicTestimonialResource::collection($testimonials);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/public/v1/talks
|
||||
*/
|
||||
public function talks(Request $request): AnonymousResourceCollection
|
||||
{
|
||||
$talks = Talk::query()
|
||||
->with('member')
|
||||
->public()
|
||||
->when($request->boolean('with_video'), fn (Builder $q) => $q->whereNotNull('video_url'))
|
||||
->recentFirst()
|
||||
->limit(min($request->integer('limit', 50), 100))
|
||||
->get();
|
||||
|
||||
return PublicTalkResource::collection($talks);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user