פרויקט Concrete CMS גדל. בלוקים מתרבים, ערכות נושא נערכות בשידור חי, והעברת פונקציונליות בין התקנות הופכת להעתק-הדבק ידני. תרחיש טיפוסי: לקוח מזמין בלוק "כרטיס מוצר", אחר כך עוד אחד, ואז ערכת נושא. שישה חודשים לאחר מכן — שישה בלוקים, שתי ערכות נושא, ושלושה מאפיינים מותאמים אישית — ואף אחד מהם לא עובר לאתר חדש בלי ריקוד עם טמבורין. הפתרון הוא לארוז הכל בחבילה מותאמת אישית אחת.
המהנדסים שלנו עם ניסיון של 5+ שנים בפיתוח עבור Concrete CMS עוזרים להפוך עבודה מפוזרת למודול יחיד וניתן לשימוש חוזר. במהלך תקופה זו יישמנו למעלה מ-30 חבילות עבור לקוחות ארגוניים — ממערכי בלוקים פשוטים ועד מודולים מלאים עם REST API, ישויות Doctrine ומשימות רקע. החיסכון בכל פריסה מגיע ל-90% מהזמן, ומפחית את עלות הבעלות הכוללת בעד 40%. צרו קשר כדי לדון בפרויקט שלכם — ייעוץ ניתן תוך יום.
חיסכון בשימוש בחבילה מותאמת אישית
התקנה ידנית של כל רכיב בנפרד מובילה לשכפול קוד, שגיאות בעדכוני סכמת מסד נתונים וגרסאות לא מבוקרות. חבילה מותאמת אישית פותרת בעיות אלה. קובץ controller.php אחד מתקין אוטומטית בלוקים, ערכות נושא, מאפיינים, סוגי עמודים ומשימות Cron. השוו: התקנה ידנית באתר חדש אורכת 2 עד 5 שעות של הגדרה לכל רכיב. חבילה מתקינה תוך 5 דקות. חיסכון בזמן — פי 10–20 בכל פריסה.
כיצד בנויה חבילה מותאמת אישית ב-Concrete CMS?
חבילה היא רק תיקייה עם מבנה ברור. הקובץ הראשי הוא controller.php, המתאר תלויות ומתקין אוטומטית את כל הרכיבים.
packages/my-package/
controller.php
blocks/
feature-card/
controller.php
db.xml
add.php
edit.php
view.php
themes/
my-theme/
attributes/
color_picker/
controller.php
single_pages/
dashboard/my_package/settings.php
elements/my_package/settings_form.php
jobs/sync_products.php
src/Entity/Order.php
src/Repository/OrderRepository.php
src/Service/OrderService.php
db.xml
רכיבי מפתח של החבילה
-
packages/my-package/ controller.php blocks/ feature-card/ controller.php db.xml add.php edit.php view.php themes/ my-theme/ attributes/ color_picker/ controller.php single_pages/ dashboard/my_package/settings.php elements/my_package/settings_form.php jobs/sync_products.php src/Entity/Order.php src/Repository/OrderRepository.php src/Service/OrderService.php db.xml— רושם את החבילה, מתקין בלוקים, סוגי עמודים, מאפיינים ועמודי Dashboard. - בלוקים — רכיבי ממשק מותאמים אישית (feature-card, team-member).
- ערכות נושא — עיצוב גלובלי של האתר.
- מאפיינים — שדות מותאמים אישית לעמודים ובלוקים.
- עמודים בודדים — עמודי הגדרות ב-Dashboard.
- משימות Cron — משימות רקע (סנכרון, שליחת דוא"ל).
- ישויות Doctrine — עבודה עם טבלאות דרך ORM.
- REST API — נקודות קצה מותאמות אישית לאינטגרציות.
בקר החבילה
הלב של החבילה הוא המחלקה controller.php המרחיבה את Controller. היא מתארת את ההתקנה, השדרוג וההסרה של רכיבים.
<?php
namespace Concrete\Package\MyPackage;
use Concrete\Core\Package\Package;
use Concrete\Core\Page\Single as SinglePage;
use Concrete\Core\Block\BlockType\BlockType;
use Concrete\Core\Page\Type\Type as PageType;
use Concrete\Core\Attribute\Type as AttributeType;
use Concrete\Core\Job\Job;
defined('C5_EXECUTE') or die('Access Denied.');
class Controller extends Package
{
protected string $pkgHandle = 'my-package';
protected string $appVersionRequired = '9.0.0';
protected string $pkgVersion = '2.1.0';
public function getPackageName(): string
{
return t('My Package');
}
public function getPackageDescription(): string
{
return t('Полный функциональный пакет для корпоративного сайта');
}
public function on_start(): void
{
$this->app->make(\Concrete\Package\MyPackage\Routing\RouteRegistrar::class)->register();
$this->app->make('Concrete\Core\Foundation\Service\ProviderList')
->registerProvider(\Concrete\Package\MyPackage\Provider\ServiceProvider::class);
}
public function install(): void
{
$pkg = parent::install();
$this->installOrUpgrade($pkg);
}
public function upgrade(): void
{
parent::upgrade();
$pkg = $this->getPackageEntity();
$this->installOrUpgrade($pkg);
}
private function installOrUpgrade(\Concrete\Core\Entity\Package $pkg): void
{
$this->installBlock('feature-card', $pkg);
$this->installBlock('team-member', $pkg);
$this->installBlock('testimonial', $pkg);
$this->installPageType('service-detail', 'Service Detail', $pkg);
$this->installPageType('team-member', 'Team Member', $pkg);
$this->installPageAttribute('hero_image', 'image', 'Hero Image', $pkg);
$this->installPageAttribute('intro_text', 'text', 'Intro Text', $pkg);
$this->installPageAttribute('meta_description', 'textarea', 'Meta Description', $pkg);
$this->installPageAttribute('show_in_nav', 'boolean', 'Show in Navigation', $pkg);
$sp = SinglePage::add('/dashboard/my_package', $pkg);
if ($sp) $sp->update(['cName' => 'My Package', 'cDescription' => 'Настройки']);
$sp = SinglePage::add('/dashboard/my_package/settings', $pkg);
if ($sp) $sp->update(['cName' => 'Settings']);
Job::installByPackage('sync_products', $pkg);
}
private function installBlock(string $handle, $pkg): void
{
if (!BlockType::getByHandle($handle)) {
BlockType::installBlockTypeFromPackage($handle, $pkg);
}
}
private function installPageType(string $handle, string $name, $pkg): void
{
if (!PageType::getByHandle($handle)) {
PageType::add(['ptHandle' => $handle, 'ptName' => $name, 'ptIsFrequentlyAdded' => 0, 'ptLaunchInComposer' => 1], $pkg);
}
}
private function installPageAttribute(string $handle, string $type, string $name, $pkg): void
{
$at = AttributeType::getByHandle($type);
$ak = \Concrete\Core\Attribute\Key\CollectionKey::getByHandle($handle);
if (!$ak) {
\Concrete\Core\Attribute\Key\CollectionKey::add($at, ['akHandle' => $handle, 'akName' => $name], $pkg);
}
}
public function uninstall(): void
{
parent::uninstall();
$db = $this->app->make('database')->connection();
$db->executeStatement('DROP TABLE IF EXISTS MyPackageOrders');
}
} השוואה: חבילה מותאמת אישית מול התקנה ידנית
| קריטריון | חבילה מותאמת אישית | התקנה ידנית |
|---|---|---|
| זמן פריסה | פקודת Concrete\Core\Package\Package אחת → התקנה דרך admin | התקנה לכל רכיב בנפרד |
| ניהול גרסאות | ניהול אחיד דרך <?php namespace Concrete\Package\MyPackage; use Concrete\Core\Package\Package; use Concrete\Core\Page\Single as SinglePage; use Concrete\Core\Block\BlockType\BlockType; use Concrete\Core\Page\Type\Type as PageType; use Concrete\Core\Attribute\Type as AttributeType; use Concrete\Core\Job\Job; defined('C5_EXECUTE') or die('Access Denied.'); class Controller extends Package { protected string $pkgHandle = 'my-package'; protected string $appVersionRequired = '9.0.0'; protected string $pkgVersion = '2.1.0'; public function getPackageName(): string { return t('My Package'); } public function getPackageDescription(): string { return t('Полный функциональный пакет для корпоративного сайта'); } public function on_start(): void { $this->app->make(\Concrete\Package\MyPackage\Routing\RouteRegistrar::class)->register(); $this->app->make('Concrete\Core\Foundation\Service\ProviderList') ->registerProvider(\Concrete\Package\MyPackage\Provider\ServiceProvider::class); } public function install(): void { $pkg = parent::install(); $this->installOrUpgrade($pkg); } public function upgrade(): void { parent::upgrade(); $pkg = $this->getPackageEntity(); $this->installOrUpgrade($pkg); } private function installOrUpgrade(\Concrete\Core\Entity\Package $pkg): void { $this->installBlock('feature-card', $pkg); $this->installBlock('team-member', $pkg); $this->installBlock('testimonial', $pkg); $this->installPageType('service-detail', 'Service Detail', $pkg); $this->installPageType('team-member', 'Team Member', $pkg); $this->installPageAttribute('hero_image', 'image', 'Hero Image', $pkg); $this->installPageAttribute('intro_text', 'text', 'Intro Text', $pkg); $this->installPageAttribute('meta_description', 'textarea', 'Meta Description', $pkg); $this->installPageAttribute('show_in_nav', 'boolean', 'Show in Navigation', $pkg); $sp = SinglePage::add('/dashboard/my_package', $pkg); if ($sp) $sp->update(['cName' => 'My Package', 'cDescription' => 'Настройки']); $sp = SinglePage::add('/dashboard/my_package/settings', $pkg); if ($sp) $sp->update(['cName' => 'Settings']); Job::installByPackage('sync_products', $pkg); } private function installBlock(string $handle, $pkg): void { if (!BlockType::getByHandle($handle)) { BlockType::installBlockTypeFromPackage($handle, $pkg); } } private function installPageType(string $handle, string $name, $pkg): void { if (!PageType::getByHandle($handle)) { PageType::add(['ptHandle' => $handle, 'ptName' => $name, 'ptIsFrequentlyAdded' => 0, 'ptLaunchInComposer' => 1], $pkg); } } private function installPageAttribute(string $handle, string $type, string $name, $pkg): void { $at = AttributeType::getByHandle($type); $ak = \Concrete\Core\Attribute\Key\CollectionKey::getByHandle($handle); if (!$ak) { \Concrete\Core\Attribute\Key\CollectionKey::add($at, ['akHandle' => $handle, 'akName' => $name], $pkg); } } public function uninstall(): void { parent::uninstall(); $db = $this->app->make('database')->connection(); $db->executeStatement('DROP TABLE IF EXISTS MyPackageOrders'); } } ו-zip | ללא בקרת גרסאות |
| ניידות | התקנה בכל אתר תוך 5 דקות | דורש העתקת כל רכיב |
| תמיכה | עדכון אוטומטי של סכמת מסד הנתונים | מיגרציות ידניות |
חבילה מותאמת אישית מאיצה את הפריסה פי 3–5 ומבטלת לחלוטין שגיאות הרכבה ידניות. אנו מבטיחים תאימות לגרסאות העדכניות ביותר של Concrete CMS.
תהליך פיתוח החבילה
- ניתוח דרישות — קביעת הרכב החבילה (בלוקים, ערכות נושא, מאפיינים, ישויות, API).
- עיצוב ארכיטקטורה — סכמת מסד נתונים, מבנה מחלקות, נתיבים.
- יישום — כתיבת בקר, בלוקים, ישויות, משימות.
- בדיקות — בדיקת התקנה, שדרוג והסרה באתר בדיקה.
- תיעוד — הוראות, תיאור API, דוגמאות.
- פריסה — אריזה, מסירה ללקוח.
מה כלול בפיתוח חבילה
- קוד מקור במאגר Git.
- תיעוד: התקנה, הגדרה, ניהול.
- הדרכת צוות (1–2 שעות).
- אחריות ל-6 חודשים על תמיכה ותיקון באגים.
- שדרוג חינם בעת שחרור גרסת CMS חדשה (בתקופת האחריות).
אילו שגיאות טיפוסיות חבילה פותרת?
שקלו מקרה אמיתי. לקוח פיתח ידנית 6 בלוקים, אך באתר חדש הבלוקים לא הוצגו עקב תלויות חסרות ב-pkgVersion. נדרשו 3 ימים להתקין מחדש כל בלוק ולתקן מיגרציות. עם חבילה, פתרנו זאת תוך שעתיים: תיארנו את כל הבלוקים בבקר, הוספנו יצירת טבלאות אוטומטית דרך db.xml, ובדקנו התקנה על ליבה נקייה. אין יותר שגיאות העברה.
מתי להזמין חבילה מותאמת אישית?
חבילה מוצדקת אם אתם מתכננים להשתמש באותה פונקציונליות במספר אתרים, רוצים לתקנן פיתוח, או זקוקים לניידות מובטחת של רכיבים. אם הפרויקט חד-פעמי ואינו דורש הרחבה, התקנה ידנית עשויה להספיק. אך ברוב התרחישים הארגוניים, החבילה מחזירה את עצמה כבר בפריסה השנייה.
לוח זמנים לפיתוח חבילה
| רכיב | הערכה |
|---|---|
| בקר חבילה + התקנה | 4–8 שעות |
| 3–5 בלוקים מותאמים אישית | 3–6 ימים |
| ערכת נושא עם 8–12 סוגי עמודים | 2–4 שבועות |
| ישויות Doctrine + CRUD | 2–4 ימים |
| עמוד Dashboard + הגדרות | 1–2 ימים |
| REST API (3–5 נקודות קצה) | 2–3 ימים |
| משימות Cron (1–3 משימות) | 4–8 שעות |
| חבילה ארגונית מלאה | 8–16 שבועות |
קראו עוד על Concrete CMS בויקיפדיה.
הזמינו פיתוח חבילה מותאמת אישית — קבלו ייעוץ תוך יום. צרו קשר כדי לדון בפרויקט שלכם.







