Skip to content

Instantly share code, notes, and snippets.

@trovster
Created February 5, 2025 09:14
Show Gist options
  • Save trovster/e75a7f2f1729c49bbc6cf1b01f734448 to your computer and use it in GitHub Desktop.
Save trovster/e75a7f2f1729c49bbc6cf1b01f734448 to your computer and use it in GitHub Desktop.
API Controllers and Resources for Laravel
<?php
namespace App\Http\Controllers\Subdomains\Api;
use App\Http\Controllers\Subdomains\Api\Controller;
use App\Http\Requests\Api\Blog\StoreRequest;
use App\Http\Requests\Api\Blog\UpdateRequest;
use App\Http\Resources\Blog\Post as Resource;
use App\Http\Resources\Blog\Posts as Collection;
use App\Models\Blog\Post;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Knuckles\Scribe\Attributes\Endpoint;
use Knuckles\Scribe\Attributes\Group;
use Knuckles\Scribe\Attributes\ResponseFromApiResource;
use Knuckles\Scribe\Attributes\UrlParam;
#[Group('Blog')]
class BlogController extends Controller
{
#[ResponseFromApiResource(
Collection::class,
Post::class,
collection: true,
paginate: 10,
with: ['categories'],
factoryStates: ['published', 'notHidden']
)]
#[Endpoint('List All Blog Posts', <<<DESC
This endpoint allows you to list all blog posts.
Each entry contains basic post information, such as title, date, summary,
word count and an array of categories.
<aside>The results are paginated.</aside>
DESC)]
public function index(Request $request): Collection
{
return Collection::make(
Post::query()->withOnly('categories')->paginate(
$request->integer('per_page', 10)
)
)->preserveQuery();
}
public function store(StoreRequest $request): JsonResponse
{
$blog = Post::query()->create(
$request->validated()
);
return new JsonResponse([
'status' => JsonResponse::HTTP_CREATED,
'message' => 'Created',
'data' => Resource::make($blog),
], JsonResponse::HTTP_CREATED);
}
#[ResponseFromApiResource(
Resource::class,
Post::class,
with: ['categories'],
factoryStates: ['published', 'notHidden']
)]
#[UrlParam('ID', 'int', 'The ID of the blog post.', example: 1)]
#[Endpoint('Get a Blog Post', <<<DESC
This endpoint allows you to get a specific blog post, based on the ID.
The entry contains the full blog post information, including the full html.
The category array contains objects of category ID and name.
DESC)]
public function show(Post $blog): Resource
{
return Resource::make(
$blog->loadMissing('categories')
);
}
#[UrlParam('ID', 'int', 'The ID of the blog post.', example: 1)]
public function update(UpdateRequest $request, Post $blog): JsonResponse
{
$blog->update(
$request->validated()
);
return new JsonResponse([
'status' => JsonResponse::HTTP_OK,
'message' => 'Updated',
'data' => Resource::make($blog),
], JsonResponse::HTTP_OK);
}
#[UrlParam('ID', 'int', 'The ID of the blog post.', example: 1)]
public function destroy(Post $blog): JsonResponse
{
$blog->delete();
return new JsonResponse([
'status' => JsonResponse::HTTP_OK,
'message' => 'Deleted',
], JsonResponse::HTTP_OK);
}
}
<?php
namespace App\Http\Resources\Blog;
use App\Http\Resources\Blog\Category;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Collection;
use JsonSerializable;
/** @mixin \App\Models\Blog\Post */
class Post extends JsonResource
{
public function toArray(Request $request): Arrayable|JsonSerializable|array
{
return array_merge(
[
'id' => $this->resource->getKey(),
'title' => $this->Title->squish(),
'date' => $this->Published->format('c'),
'summary' => $this->Summary,
'wordCount' => $this->description->wordCount(),
],
$this->details($request),
$this->categories($request),
$this->links($request)
);
}
public function details(Request $request): array
{
return [
$this->mergeWhen(! $request->boolean('collection'), [
'html' => $this->html,
]),
];
}
public function categories(Request $request): array
{
return [
$this->mergeWhen($request->boolean('collection'), [
'categories' => $this->whenLoaded(
'categories',
fn (): Collection => $this->categories->pluck('Tag')->unique()
),
]),
$this->mergeWhen(! $request->boolean('collection'), [
'categories' => $this->whenLoaded(
'categories',
fn (): AnonymousResourceCollection => Category::collection($this->categories->unique())
),
]),
];
}
public function links(Request $request): array
{
return [
'links' => [
'self' => route('api.blog.show', $this),
'permalink' => $this->permalink,
],
];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment