feat(blog): add city_id to blogs for city-specific scoping
- Introduced a new nullable city_id column in the blogs table to allow scoping of blog posts to specific cities. - Updated Blog entity to include a ManyToOne relationship with the City entity. - Enhanced BlogController to handle city_id in the request, allowing filtering of posts by city. - Modified BlogRepository to support querying published posts based on city_id. - Added tests to ensure correct behavior for city-scoped and nationwide posts, including creation and updating of posts with city associations.
This commit is contained in:
+23
-1
@@ -16,6 +16,7 @@ List published blog posts.
|
||||
| `page` | integer | ❌ | 1 | Page number |
|
||||
| `limit` | integer | ❌ | 20 | Items per page |
|
||||
| `tag` | string | ❌ | — | Filter by exact tag **name** (e.g. `?tag=دیابت`). Blog tags are stored as a JSON array of names; only blogs whose `tags` array contains this exact name are returned. |
|
||||
| `city_id` | integer | ❌ | — | Scope to one city. Returns that city's posts **plus every nationwide post** (`city_id IS NULL`). Omit it to return all published posts regardless of city. |
|
||||
|
||||
### Response `200`
|
||||
```json
|
||||
@@ -31,17 +32,34 @@ List published blog posts.
|
||||
"author": { "uuid": "...", "real_name": "دکتر احمدی" },
|
||||
"tags": [{ "id": 1, "name": "دیابت" }],
|
||||
"status": "published",
|
||||
"city": { "id": "123", "name": "یاسوج" },
|
||||
"created_at": 1717000000
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"totalRecords": 25,
|
||||
"totalPages": 2,
|
||||
"currentPage": 1
|
||||
"currentPage": 1,
|
||||
"limit": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### فیلد `city` — شهر پست
|
||||
|
||||
`city` در پاسخ لیست و جزئیات وجود دارد و دو حالت دارد:
|
||||
|
||||
| مقدار | معنا |
|
||||
|-------|------|
|
||||
| `{ "id": "123", "name": "یاسوج" }` | پست **شهری** — به آن شهر تعلق دارد |
|
||||
| `null` | پست **سراسری** — حالت دائمی و معتبر، نه «تنظیمنشده» |
|
||||
|
||||
- `city_id` روی `blogs` **nullable** است و `NULL` معنای دائمی «سراسری» دارد. رکوردهای قبل از این تغییر همگی سراسری شدند.
|
||||
- حذف شهر پست را حذف نمیکند (`ON DELETE SET NULL`) — پست سراسری میشود.
|
||||
- پست سراسری روی **همهٔ** دامنههای شهری در لیست دیده میشود؛ فقط canonical آن روی دامنهٔ اصلی مینشیند. به همین دلیل `city_id=X` هم پستهای شهر X و هم پستهای سراسری را برمیگرداند — نه فقط شهر X.
|
||||
|
||||
> 🔗 مصرفکننده: سایت عمومی چند-دامنهای (`nobat724_front`) با همین فیلد تصمیم میگیرد پست را روی دامنهٔ شهر canonical کند یا روی دامنهٔ اصلی، و در کدام sitemap بگذارد. تغییر معنای `null` قرارداد آن را میشکند.
|
||||
|
||||
---
|
||||
|
||||
## GET `/api/v1/blog/{slug}`
|
||||
@@ -108,6 +126,7 @@ Create a new blog post.
|
||||
| `tags` | integer[] | ❌ | Array of tag IDs |
|
||||
| `status` | string | ❌ | `"draft"` (default) or `"published"` |
|
||||
| `image_url` | string | ❌ | Cover image path returned by the upload endpoint |
|
||||
| `city_id` | integer\|null | ❌ | City this post belongs to. **Omitting it, or sending `null`/`0`, creates a nationwide post.** An unknown city id is rejected with `422`. |
|
||||
|
||||
### Response `201`
|
||||
```json
|
||||
@@ -168,6 +187,8 @@ Update a blog post.
|
||||
|
||||
All fields optional. Send `image_url: ""` to clear the cover image.
|
||||
|
||||
`city_id` follows PATCH semantics: **omit it and the post's city is left untouched**; send `null` (or `0`) to turn the post into a nationwide one; send a city id to move it to that city.
|
||||
|
||||
### Response `200`
|
||||
Updated blog object.
|
||||
|
||||
@@ -177,6 +198,7 @@ Updated blog object.
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_AUTH_006` | 403 | Not admin |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Blog not found |
|
||||
| `ERR_VALIDATION_002` | 422 | Unknown `city_id` |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user