Кабинет
Как устроен пакет

#Манифест по частям

#Шапка

{
  "name": "Портфолио",
  "code": "portfolio",
  "version": "1.2.0",
  "description": "Работы с картинкой, тегом и ценой",
  "author": "Wacis",
  "core_version": ">=1.0.0",
  "dependencies": ["form"]
}
КлючПравило
codeлатиница, цифры, дефис. Точка не принимается: код идёт в имя каталога и в адреса реестра
versionровно три числа. Не вырастили — обновление не предложится
core_versionограничение со знаком: ">=1.0.0"
dependenciesкоды решений, которые поставятся первыми

#dictionaries — справочники

"dictionaries": [
  {
    "name": "portfolio",
    "title": "Портфолио",
    "description": "Выполненные работы",
    "menu": true,
    "menu_sort": 30,
    "menu_section": "content",
    "fields": [
      { "name": "zagolovok", "title": "Заголовок", "type": "text" },
      { "name": "cena",      "title": "Цена",      "type": "price" },
      { "name": "kartinka",  "title": "Картинка",  "type": "image" },
      { "name": "category",  "title": "Категория", "type": "select",
        "select": "category", "select_by": "id", "select_show": "title" }
    ],
    "demo": [
      { "zagolovok": "Кухня 12 м²", "cena": "450000" }
    ]
  }
]
КлючЧто делает
nameимя таблицы: латиница и подчёркивание
titleкак называется в панели
menuпоказывать ли пунктом меню. false — для служебных
menu_sortпорядок среди пунктов
menu_sectionв какой раздел меню попасть
menu_section_titleназвание раздела, если решение заводит свой
fieldsполя, они же столбцы
demoначальные записи
Важно

"menu": false нужен служебным справочникам — полям форм, комментариям к задачам. Это строки-дети: отдельным разделом меню они только загромождают панель.

#Поля-выборки

Связь описывается тремя ключами, а не одним:

КлючСмысл
selectна какой справочник смотрим
select_byчто храним — столбец цели
select_showчто показываем — столбец цели
{ "name": "category", "type": "select",
  "select": "category", "select_by": "id", "select_show": "title" }
Частая ошибка

Пропустить select_by и select_show опаснее, чем кажется. Поле заведётся, но пустое: тип есть, а на что смотреть — не сказано. Хуже того, пустое «что хранить» попадает в список столбцов запроса — и каждая страница, где читается этот справочник, отвечает ошибкой базы. Это ронял живые сайты дважды.

#modules — модули

"modules": [
  {
    "name": "portfolio",
    "title": "Портфолио",
    "type": "BFM",
    "dictionary": "portfolio",
    "box_layout":  "<div class=\"grid\">{item}</div>",
    "item_layout": "<article><h3>{module.portfolio.zagolovok}</h3></article>"
  }
]
КлючЧто делает
nameимя, которым модуль зовут: {module.portfolio}
typeBFM (блок) или PFM (страница). По умолчанию BFM
dictionaryимя справочника-источника. Без него блок статический
box_layoutоболочка списка, внутри {item}
item_layoutразметка одной записи
Важно

Справочник, указанный в dictionary, должен существовать к моменту заведения модуля. В манифесте он обычно объявлен выше — установщик заводит справочники раньше модулей. Ссылка на несуществующий справочник прерывает установку.

Вёрстка записывается дважды: в рабочую и в исходную. Рабочую владелец правит как хочет, исходная нужна кнопке «Вернуть исходный код» и обновлению — как это работает.

#routes — свои адреса

"routes": [
  { "method": "POST", "pattern": "/form/submit/", "handler": "wForm::submit" },
  { "method": "POST", "pattern": "/admin/crm/move/", "handler": "wCrmPack::move" }
]

Обработчик — строка Класс::метод. Класс лежит в src/ решения и подгружается сам.

#permissions — права

"permissions": [
  { "code": "crm.view", "title": "Смотреть заявки" },
  { "code": "crm.edit", "title": "Править заявки" }
]

#admin_menu — свои пункты меню

"admin_menu": [
  { "title": "Движение товара", "url": "/admin/shop/moves/", "sort": 40 }
]

Нужно для экранов, которых нет среди справочников: справочники попадают в меню сами, по галочке.

#listeners — подписки на события

"listeners": {
  "form.submitted":   "wCrmForm::onFormSubmitted",
  "crm.lead.created": "wCrmTasks::onLeadCreated"
}

Принимается и второй вид записи — списком:

"listeners": [
  { "event": "form.submitted", "handler": "wCrmForm::onFormSubmitted" }
]

Слушатели живут в базе и уходят вместе с решением — править чужой код для этого не нужно. Список событий — События.

#Что проверяется при установке

ПроверкаЧто будет при провале
имя справочникаотказ: «Недопустимое имя справочника»
справочник модуля существуетотказ: «Модулю X нужен справочник Y, а его нет»
у модуля есть имяотказ
core_version подходитотказ с указанием нужной версии
зависимости доступныставятся сами либо отказ со списком

Отказ на любом шаге откатывает всё сделанное — половины решения не остаётся.