Розробка кастомного пакету (Package) Concrete CMS
Пакет (Package) в Concrete CMS — це контейнер, який об'єднує теми, блоки, атрибути, типи сторінок, одиночні сторінки, Express-об'єкти та завдання автоматизації. Правильно структурований пакет дозволяє встановити все необхідне однією командою та легко переносити рішення між інсталяціями.
Структура пакету
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 # сторінка налаштувань Dashboard
elements/
my_package/
settings_form.php
jobs/
sync_products.php # автоматична задача (Cron Job)
config/
generated_overrides/
mail/
order_notification.php # шаблон email
src/
Entity/ # Doctrine-сущності
Order.php
Repository/
OrderRepository.php
Service/
OrderService.php
db.xml # загальні таблиці пакету
controller.php — серце пакету
<?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();
}
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);
// Сторінка налаштувань Dashboard
$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' => 'Налаштування']); }
// Cron Job
Job::installByPackage('sync_products', $pkg);
}
private function installBlock(string $handle, $pkg): void {
if (!\Concrete\Core\Block\BlockType\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();
}
}
Cron Job
<?php
// jobs/sync_products.php
namespace Concrete\Package\MyPackage\Job;
use Concrete\Core\Job\Job;
class SyncProducts extends Job {
public function getJobName(): string { return t('Sync Products'); }
public function getJobDescription(): string { return t('Синхронізація товарів із зовнішнім API'); }
public function run(): string {
$service = $this->app->make(\Concrete\Package\MyPackage\Service\ProductSyncService::class);
$count = $service->sync();
return t('Синхронізовано: %d товарів', $count);
}
}
Job запускається через Dashboard → System → Automated Jobs або по cron:
*/30 * * * * /usr/bin/php /var/www/mysite/concrete/bin/concrete5 c5:job:run sync_products
Сроки розробки пакету
| Компонент | Оцінка |
|---|---|
| Контролер пакету + встановлення | 4–8 ч |
| 3–5 кастомних блоків | 3–6 днів |
| Тема з 8–12 типами сторінок | 2–4 тижні |
| Doctrine-сущності + CRUD | 2–4 дні |
| Сторінка Dashboard + налаштування | 1–2 дні |
| REST API (3–5 ендпоінтів) | 2–3 дні |
| Cron Jobs (1–3 завдання) | 4–8 ч |
| Повний корпоративний пакет | 8–16 тижнів |







