feat(blog): implement tag filtering and facets endpoint

- Fix tag filtering to correctly match Persian tags by adjusting JSON encoding in the applyTagFilter method.
- Add new endpoint GET /api/v1/blogs/tags to retrieve distinct tag names and their counts for published posts, respecting city scope.
- Update API documentation to reflect changes in tag filtering and the new tags endpoint.
- Create BlogTagFilterTest to ensure correct functionality of tag filtering and facets, including edge cases for Persian tags and city filtering.
This commit is contained in:
hamed
2026-07-29 14:23:36 +03:30
parent 4f4bce9fe2
commit 9b05c6d1ff
6 changed files with 707 additions and 15 deletions
+45
View File
@@ -105,6 +105,51 @@ class BlogController extends BaseController
return $this->paginated($blogs, $total, $page, $limit);
}
#[OA\Get(
path: '/api/v1/blogs/tags',
summary: 'Distinct tag names of published posts with post counts',
parameters: [
new OA\Parameter(
name: 'city_id',
in: 'query',
required: false,
description: 'Same scope as GET /api/v1/blogs: that city\'s posts plus nationwide posts (city_id IS NULL). Omit to count every published post.',
schema: new OA\Schema(type: 'integer')
),
],
responses: [
new OA\Response(
response: 200,
description: 'Tag vocabulary of published posts, most used first',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'success', type: 'boolean', example: true),
new OA\Property(
property: 'data',
type: 'array',
items: new OA\Items(
properties: [
new OA\Property(property: 'name', type: 'string', example: 'چشم و گوش'),
new OA\Property(property: 'count', type: 'integer', example: 16),
],
type: 'object'
)
),
]
)
),
]
)]
#[Route('/api/v1/blogs/tags', methods: ['GET'])]
public function tags(Request $request): JsonResponse
{
$cityId = $request->query->get('city_id') !== null
? max(1, (int) $request->query->get('city_id'))
: null;
return $this->success($this->blogRepo->tagFacets($cityId));
}
#[OA\Get(
path: '/api/v1/blog/{slug}',
summary: 'Get a published blog post by slug or UUID',