פיתוח אוספים מותאמים אישית עבור Payload CMS
תארו לעצמכם: אתם צריכים לארגן קטלוג מוצרים עם וריאנטים (גודל, צבע, מלאי), בלוק SEO, והרשאות גישה לתפקידים שונים. מערכות CMS רגילות לא מציעות גמישות כזו—אתם חייבים לכתוב תוספים מותאמים אישית או לעבור לפתרון headless. Payload CMS פותר זאת ברמת הארכיטקטורה: אוספים, hooks ובקרת גישה מאפשרים לבנות כל לוגיקה עסקית ללא פשרות. אנו מגדירים אוסף כך שיתאים בדיוק לתהליכים שלכם. בואו נפרק זאת עם דוגמה מעשית של בניית קטלוג מוצרים לחנות מקוונת. חיסכון בזמן בפיתוח API—עד 50% בהשוואה לפתרונות מותאמים אישית.
כיצד אנו בונים את אוסף המוצרים
אנו מתחילים בהגדרה. כל אוסף הוא אובייקט TypeScript עם שדות, הגדרות גישה ו-hooks. הנה מבנה מינימלי:
// collections/Products.ts
import { CollectionConfig } from 'payload/types'
const Products: CollectionConfig = {
slug: 'products',
labels: {
singular: 'Товар',
plural: 'Товары',
},
admin: {
useAsTitle: 'name',
defaultColumns: ['name', 'price', 'category', 'inStock'],
group: 'Каталог',
},
// ...
}לאחר מכן אנו מרחיבים את השדות בפירוט. Payload גמיש פי 2 בהגדרת שדות מאשר Strapi: הוא תומך ב-blocks, arrays ו-groups. להלן סט שדות אמיתי למוצר עם וריאנטים ובלוק SEO:
fields: [
// Текстовые поля
{
name: 'name',
type: 'text',
required: true,
},
{
name: 'description',
type: 'textarea',
},
{
name: 'content',
type: 'richText',
},
// Цена и дата публикации
{
name: 'price',
type: 'number',
min: 0,
required: true,
},
{
name: 'publishedAt',
type: 'date',
},
// Статус товара
{
name: 'status',
type: 'select',
options: [
{
label: 'Активен',
value: 'active',
},
{
label: 'Архив',
value: 'archived',
},
],
defaultValue: 'active',
},
// Изображение
{
name: 'image',
type: 'upload',
relationTo: 'media',
},
// Связи с категориями и тегами
{
name: 'category',
type: 'relationship',
relationTo: 'categories',
hasMany: false,
},
{
name: 'tags',
type: 'relationship',
relationTo: 'tags',
hasMany: true,
},
// Массив вариантов (SKU, цвет, размер, остаток)
{
name: 'variants',
type: 'array',
fields: [
{
name: 'sku',
type: 'text',
required: true,
},
{
name: 'color',
type: 'text',
},
{
name: 'size',
type: 'text',
},
{
name: 'stock',
type: 'number',
defaultValue: 0,
},
],
},
// Блоки для динамических секций (например, описание, характеристики, CTA)
{
name: 'sections',
type: 'blocks',
blocks: [TextBlock, ImageBlock, CTABlock],
},
// Группа для SEO-метаданных
{
name: 'seo',
type: 'group',
fields: [
{
name: 'title',
type: 'text',
},
{
name: 'description',
type: 'textarea',
},
],
},
] למה אתם צריכים את ה-Hooks מסוג beforeChange ו-afterChange?
ללא hooks, אוסף הוא רק CRUD. Hooks מוסיפים לוגיקה עסקית. אנו משתמשים ב-// collections/Products.ts import { CollectionConfig } from 'payload/types' const Products: CollectionConfig = { slug: 'products', labels: { singular: 'Товар', plural: 'Товары', }, admin: { useAsTitle: 'name', defaultColumns: ['name', 'price', 'category', 'inStock'], group: 'Каталог', }, // ... } כדי ליצור slug על סמך שם המוצר ולהגדיר אוטומטית את המחבר. fields: [ // Текстовые поля { name: 'name', type: 'text', required: true }, { name: 'description', type: 'textarea' }, { name: 'content', type: 'richText' }, // Цена и дата публикации { name: 'price', type: 'number', min: 0, required: true }, { name: 'publishedAt', type: 'date' }, // Статус товара { name: 'status', type: 'select', options: [ { label: 'Активен', value: 'active' }, { label: 'Архив', value: 'archived' }, ], defaultValue: 'active', }, // Изображение { name: 'image', type: 'upload', relationTo: 'media' }, // Связи с категориями и тегами { name: 'category', type: 'relationship', relationTo: 'categories', hasMany: false, }, { name: 'tags', type: 'relationship', relationTo: 'tags', hasMany: true, }, // Массив вариантов (SKU, цвет, размер, остаток) { name: 'variants', type: 'array', fields: [ { name: 'sku', type: 'text', required: true }, { name: 'color', type: 'text' }, { name: 'size', type: 'text' }, { name: 'stock', type: 'number', defaultValue: 0 }, ], }, // Блоки для динамических секций (например, описание, характеристики, CTA) { name: 'sections', type: 'blocks', blocks: [TextBlock, ImageBlock, CTABlock], }, // Группа для SEO-метаданных { name: 'seo', type: 'group', fields: [ { name: 'title', type: 'text' }, { name: 'description', type: 'textarea' }, ], }, ] משמש לאיפוס המטמון של Next.js או לשליחת הודעות ל-Telegram. הנה איך נראית סט hooks טיפוסי בפרויקט שלנו:
hooks: {
beforeChange: [
async ({ data, req, operation }) => {
if (operation === 'create' && !data.slug) {
data.slug = data.name
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/[^\w-]/g, '')
}
if (operation === 'create' && req.user) {
data.author = req.user.id
}
return data
},
],
afterChange: [
async ({ doc, operation }) => {
if (operation === 'update') {
await fetch(`/api/revalidate?path=/products/${doc.slug}`, {
method: 'POST',
})
}
},
],
afterDelete: [
async ({ doc }) => {
console.log(`Product ${doc.id} deleted`)
},
],
} כיצד להגדיר גישה לאוסף?
בקרת הגישה ב-Payload גמישה: ניתן להגדיר כללים לקריאה, יצירה, עדכון ומחיקה. אנו נתקלים לעיתים קרובות בבקשות כמו: "קריאה - לכולם, יצירה - למשתמשים מאומתים, עדכון - רק למחבר או למנהל." זה מיושם באמצעות פילטרים מותנים. דוגמה:
access: {
read: () => true,
create: ({ req: { user } }) => Boolean(user),
update: ({ req: { user }, id }) => {
if (!user) return false
if (user.role === 'admin') return true
return { author: { equals: user.id } }
},
delete: ({ req: { user } }) => user?.role === 'admin',
}, ולידציה מותאמת אישית
לפעמים סוגי שדות סטנדרטיים לא מכסים את הדרישות. לדוגמה, צריך לבדוק ייחודיות של SKU בין כל וריאנטי המוצר. לשם כך, אנו משתמשים בפונקציית beforeChange בשדה המערך. קוד הולידציה רץ על השרת לפני השמירה, ומבטיח שלמות נתונים. דוגמה:
{
name: 'variants',
type: 'array',
fields: [
{
name: 'sku',
type: 'text',
required: true,
unique: true,
},
],
validate: (value) => {
const skus = value.map(v => v.sku)
if (new Set(skus).size !== skus.length)
return 'SKU must be unique'
return true
},
} גרסאות ושליפת נתונים דרך API
לפרויקטים של תוכן, אנו מפעילים גרסאות. Payload שומר עד 20 גרסאות עם שמירה אוטומטית כל 2 שניות. זה חיוני כאשר מספר עורכים עובדים על התוכן. לאחר הגדרת האוסף, נקודות קצה של REST ו-GraphQL נוצרות אוטומטית. הנה דוגמה לבקשת צד שרת (Next.js Server Component) עם סינון:
import { getPayload } from 'payload'
import config from '@payload-config'
const payload = await getPayload({ config })
const result = await payload.find({
collection: 'products',
where: {
and: [
{
status: { equals: 'active' },
},
{
category: { equals: categoryId },
},
{
price: { less_than: 10000 },
},
],
},
sort: '-createdAt',
limit: 20,
page: 1,
depth: 2,
})
const { docs, totalDocs, hasNextPage } = result השוואת סוגי שדות
| סוג | מטרה | דוגמת שימוש |
|---|---|---|
| text | טקסט קצר | שם מוצר |
| textarea | טקסט ארוך | תיאור מוצר |
| richText | תוכן מעוצב | מאמר בבלוג |
| number | ערך מספרי | מחיר, כמות |
| date | תאריך/שעה | תאריך פרסום |
| select | בחירה מרשימה | סטטוס מוצר |
| relationship | קישור לאוסף אחר | קטגוריה, תגיות |
| array | מערך של אובייקטים | וריאנטים של מוצר |
| blocks | עורך בלוקים (Gutenberg) | קטעי עמוד |
| group | קיבוץ שדות | מטא-דאטה של SEO |
השוואת Payload עם מערכות Headless CMS אחרות
| קריטריון | Payload | Strapi | Directus |
|---|---|---|---|
| גמישות שדות | מקסימלית: blocks, arrays | בינונית: רק סוגים בסיסיים | גבוהה: שדות מותאמים אישית |
| Hooks ואירועים | מלא: beforeChange, after... | Middleware | Hooks של קלט/פלט |
| בקרת גישה | גרנולרית: read/create/... | תפקידים והרשאות | הרשאות + פילטרים |
| גרסאות | מובנה, שמירה אוטומטית | תוספים | תוספים |
| ביצועים | מהיר על PostgreSQL/MySQL | בינוני | גבוה על MySQL |
זה מאפשר חיסכון של עד 40% מהתקציב בהשוואה לפתרונות דומים על Strapi.
טעויות נפוצות ביצירת אוספים
- שימוש ב-textarea במקום richText—אובדן עיצוב.
- חוסר ולידציה לייחודיות slug—כתובות URL כפולות.
- גישה פתוחה מדי—דליפת נתונים.
- התעלמות מ-hooks—הלוגיקה העסקית נשארת בצד הלקוח.
מה כלול בפיתוח וציר הזמן
אנו מתכננים את סכימת השדות והיחסים תוך מחשבה על הרחבות עתידיות, כותבים hooks ומגדירים בקרת גישה. הגדרת אוסף אחד אורכת 2–4 שעות. קטלוג מלא של 5–10 אוספים מקושרים אורך 2–4 ימים. עלות פיתוח אוסף אחד דומה למספר ימי פיתוח, וזה זול משמעותית מבניית פונקציונליות דומה על פתרון מותאם אישית. כלול: אינטגרציה עם מסד נתונים קיים, תיעוד API והדרכת צוות. קבלו ייעוץ להערכה חינמית של הפרויקט שלכם.
הזמינו פיתוח אוספים מותאמים אישית עבור Payload CMS—קבלו API מוכן תוך 2–4 ימים. המהנדסים שלנו עובדים עם Payload מאז גרסה 1.0 ומוסמכים ב-Next.js ו-TypeScript. במהלך השנים, יישמנו יותר מ-50 פרויקטים על Payload. אנו מבטיחים שהאוספים יעמדו בדרישותיכם ויתאימו להתרחבות בקלות.







