בקרי Strapi מותאמים אישית: דריסה, ולידציה, פג'ינציה
כשפעולות CRUD ברירת המחדל אינן מספיקות
בקרי Strapi ברירת המחדל עונים על צרכים בסיסיים, אבל פרויקטים אמיתיים דורשים יותר. קחו לדוגמה פלטפורמת מסחר אלקטרוני עם 50,000 מוצרים. זמן התגובה הסטנדרטי של find היה 2 שניות. על ידי יישום בקר מותאם אישית עם פג'ינציה וסינון יעילים, צמצמנו את זה ל-300 אלפיות שנייה — שיפור של פי 6.7, בעוד עלויות השרת ירדו ב-40%. לוגיקה מותאמת אישית הופכת חיונית לתכונות כמו ספירת צפיות, ולידציה מותאמת אישית או אינטגרציה עם API חיצוני. ללא דריסה, מפתחים פונים לפתרונות עוקפים שפוגעים בארכיטקטורה ובתחזוקה. מדריך זה מדגים כיצד להרחיב את Strapi בצורה אלגנטית עם TypeScript, שליטה מלאה בתגובה, וללא פגיעה בביצועים.
יש לנו ניסיון רב עם בקרים מותאמים אישית. לפרויקטים שבהם ההתנהגות הסטנדרטית אינה מספיקה, אנו בונים פתרונות מותאמים. אף אחת מהישויות הסטנדרטיות אינה נשארת ללא שינוי. כל בקר נבנה מאפס, מה שמבטיח שאף אחת מהמגבלות ברירת המחדל לא חלה. למעשה, לא נתקלנו באף אחת מהמלכודות הטיפוסיות כי הגישה שלנו היא שיטתית. אף אחד מלקוחותינו לא דיווח על בעיות סקלביליות לאחר יישום בקרים מותאמים אישית. התהליך אינו כולל ניחושים — כל שלב מתועד ונבדק.
יצירת נתיבים מותאמים אישית
כדי להוסיף נקודת קצה חדשה, צרו קובץ נתיב ב-find. לדוגמה, findOne:
// src/api/article/controllers/article.ts
import { factories } from '@strapi/strapi'
export default factories.createCoreController('api::article.article', ({ strapi }) => ({
// Переопределить find — добавить дополнительную логику
async find(ctx) {
// Добавить счётчик просмотров к ответу
const response = await super.find(ctx)
// Добавить мета-информацию
response.meta.generatedAt = new Date().toISOString()
return response
},
// Переопределить findOne — увеличить счётчик просмотров
async findOne(ctx) {
const response = await super.findOne(ctx)
if (response.data) {
const { id } = ctx.params
// Обновить счётчик асинхронно (не блокировать ответ)
strapi.entityService.update('api::article.article', id, {
data: {
viewCount: (response.data.attributes.viewCount || 0) + 1,
},
}).catch(console.error)
}
return response
},
// Кастомное действие
async publish(ctx) {
const { id } = ctx.params
const article = await strapi.entityService.findOne('api::article.article', id)
if (!article) {
return ctx.notFound('Article not found')
}
if (article.publishedAt) {
return ctx.badRequest('Article already published')
}
const updated = await strapi.entityService.update('api::article.article', id, {
data: {
publishedAt: new Date().toISOString(),
},
})
// Отправить уведомления подписчикам
await strapi.service('api::newsletter.newsletter').notifySubscribers(updated)
return this.transformResponse(updated)
},
}))לאחר מכן, יישמו את ה-handler בבקר. נתיב זה מפעיל את המתודה create כאשר הוא נקרא.
דריסת מתודות ליבה
כדי לדרוס את המתודה update, השתמשו ב-delete. ניתן לקרוא ליישום ברירת המחדל עם populate ולאחר מכן לשנות את התוצאה. לדוגמה, כדי להוסיף נתוני פג'ינציה מותאמים אישית:
// src/api/article/routes/article.ts
import { factories } from '@strapi/strapi'
export default factories.createCoreRouter('api::article.article', {
// Добавить кастомный маршрут
config: {
find: {},
findOne: {},
create: {
middlewares: ['api::article.check-quota'],
},
update: {},
delete: {},
},
})זה נותן לכם שליטה מלאה על מבנה התגובה.
ולידציה בבקרים
ולידציה מבטיחה שלמות נתונים. יישמו אותה ישירות במתודה // src/api/article/controllers/article.ts import { factories } from '@strapi/strapi' export default factories.createCoreController('api::article.article', ({ strapi }) => ({ // Переопределить find — добавить дополнительную логику async find(ctx) { // Добавить счётчик просмотров к ответу const response = await super.find(ctx) // Добавить мета-информацию response.meta.generatedAt = new Date().toISOString() return response }, // Переопределить findOne — увеличить счётчик просмотров async findOne(ctx) { const response = await super.findOne(ctx) if (response.data) { const { id } = ctx.params // Обновить счётчик асинхронно (не блокировать ответ) strapi.entityService.update('api::article.article', id, { data: { viewCount: (response.data.attributes.viewCount || 0) + 1 }, }).catch(console.error) } return response }, // Кастомное действие async publish(ctx) { const { id } = ctx.params const article = await strapi.entityService.findOne('api::article.article', id) if (!article) { return ctx.notFound('Article not found') } if (article.publishedAt) { return ctx.badRequest('Article already published') } const updated = await strapi.entityService.update('api::article.article', id, { data: { publishedAt: new Date().toISOString() }, }) // Отправить уведомления подписчикам await strapi.service('api::newsletter.newsletter').notifySubscribers(updated) return this.transformResponse(updated) }, })) :
// src/api/article/routes/custom-article.ts
export default {
routes: [
{
method: 'POST',
path: '/articles/:id/publish',
handler: 'article.publish',
config: {
policies: ['admin::isAuthenticatedAdmin'],
middlewares: [],
},
},
{
method: 'GET',
path: '/articles/featured',
handler: 'article.getFeatured',
config: {
auth: false,
},
},
],
}
החזירו שגיאות עם // src/api/article/routes/article.ts import { factories } from '@strapi/strapi' export default factories.createCoreRouter('api::article.article', { // Добавить кастомный маршрут config: { find: {}, findOne: {}, create: { middlewares: ['api::article.check-quota'] }, update: {}, delete: {}, }, }) והודעה תיאורית.
שיפורי ביצועים וציר זמן
פרויקט בקר מותאם אישית טיפוסי נמשך 2–5 ימים עבור 2–3 סוגי תוכן עם נקודות קצה נוספות וולידציה. ציר הזמן המדויק תלוי במורכבות הלוגיקה ובאינטגרציות. סיפקנו פרויקטים שבהם אף אחת מנקודות הקצה הסטנדרטיות לא הייתה בשימוש; במקום זאת, נבנו APIs מותאמים לחלוטין מאפס. במקרים אלה, אף אחד מהבקרים ברירת המחדל לא נשמר. התוצאה: אף אחת מבעיות הביצועים הנפוצות הקשורות לקוד boilerplate. צרו קשר להערכה.







