FAQ: Как отдавать обновления модулей через Центр обновлений¶
Это статья для разработчиков, которые предлагают модули для CS-Cart и Multi-Vendor и хотят упростить процесс обновления для своих пользователей. Вместо того, чтобы разрабатывать собственный механизм или просить клиентов вручную менять файлы, вы можете просто предложить им перейти в раздел Администрирование → Центр обновлений и получить обновления для модулей там.
Когда выпускать обновления модулей?¶
Есть две основные причины для выпуска обновления:
Вы разработали новую функциональность и готовы отдать её клиентам.
В ядре CS-Cart или Multi-Vendor произошли изменения:
удалены константы, классы, функции или хуки, которые используются вашим модулем;
поменялись аргументы у функций или хуков, которые используются вашим модулем;
константы, классы, функции или хуки, которые используются вашим модулем, помечены как устаревшие (deprecated). Мы не удаляем их сразу, но лучше переключиться на новую функциональность до того, как мы всё-таки удалим старую;
в базе данных изменилась структура таблиц, используемых модулем.
Это изменение не обязательно приведёт к поломке модуля — мы сохраняем обратную совместимость между релизами CS-Cart. Но если модуль использует прямые запросы к таблицам вместо функций ядра, то модуль может перестать работать.
Примечание
Примерно в то же время, когда мы выпускаем новую версию CS-Cart/Multi-Vendor, мы также анонсируем изменения ядра в специальном разделе документации для разработчиков.
Куда загружать пакеты обновлений?¶
Если хотите, чтобы покупатели могли обновить модуль через Центр обновлений, то есть два способа:
- CS-Cart Marketplace. Там есть все необходимые инструменты для сборки и распространения обновлений. Пока эти инструменты в закрытом бета-тестировании; чтобы получить доступ, напишите нам на marketplace@cs-cart.com.
- Собственный сервер обновлений. Он нужен, если вы используете собственный механизм лицензирования и раздачи пакетов обновлений. Такой подход потребует от вас специальным образом подготовить модуль и самостоятельно собирать пакеты обновлений. Инструкции есть дальше в статье.
Как сделать модуль совместимым только с определёнными версиями CS-Cart?¶
В файле addon.xml можно задать все требования модуля: поддерживаемые версии CS-Cart и/или Multi-Vendor, PHP и его расширения, необходимые и конфликтующие модули.
Важно
Требования в файле addon.xml проверяются только в процессе установке модуля. Они НЕ проверяются при обновлении.
Если после обновления требования модуля поменялись, то недостаточно поменять addon.xml. Чтобы клиенты не установили обновление, которое не будет работать с их версией, у вас есть два варианта:
- передавайте версию CS-Cart/Multi-Vendor на ваш сервер обновлений, и пусть сервер решает, предлагать ли обновление;
- добавьте в пакет обновления валидатор, и пусть он не даёт установить обновление, если не выполнены определённые требования.
Как подготовить модуль к работе с Центром обновлений?¶
CS-Cart и Multi-Vendor проверяют наличие обновлений в 2 случаях:
- Когда кто-то заходит в панель администратора (но только если в Настройки → Общие включена настройка Проверять наличие обновлений автоматически).
- Когда кто-то заходит в Администрирование → Центр обновлений.
Вот несколько вещей, которые нужно учесть при адаптации модуля для работы с Центром обновлений:
- Чтобы проверять наличие обновлений и скачивать их, в модуле должен быть коннектор Центра обновлений. Этот коннектор — класс, который должен быть размещен в app/addons/[sample_addon]/Tygh/UpgradeCenter/Connectors/[SampleAddon]/Connector.php, где
[sample_addon]
— идентификатор модуля;[SampleAddon]
— идентификатор модуля, записанный в CamelCase.
- Коннектор должен расширять класс
\Tygh\UpgradeCenter\Connectors\BaseAddonConnector
и реализовывать интерфейс\Tygh\UpgradeCenter\Connectors\IConnector
. - Адрес сервера обновлений указывается в поле
url
результата вызова методаConnector::getConnectionData
. - Данные, передаваемые на сервер обновлений для проверки наличия обновлений, задаются в поле
data
результата вызова методаConnector::getConnectionData
. Полеdata
представляет из себя массив видаparameter_name => parameter_value
. - Непосредственное скачивание пакета обновлений реализуется в методе
Connector::downloadPackage
.
Вот пример коннектора с комментариями:
<?php
namespace Tygh\UpgradeCenter\Connectors\SampleAddon;
use Tygh\Addons\SchemesManager;
use Tygh\Http;
use Tygh\Registry;
use Tygh\Settings;
use Tygh\Tools\Url;
use Tygh\UpgradeCenter\Connectors\BaseAddonConnector;
use Tygh\UpgradeCenter\Connectors\IConnector;
/**
* Класс Connector реализует коннектор Центра обновлений для модуля Sample Add-on.
*
* @package Tygh\UpgradeCenter\Connectors\SampleAddon
*/
class Connector extends BaseAddonConnector implements IConnector
{
/**
* @var string Имя параметра HTTP-запроса, который определяет, какое действие требуется от сервера обновлений
*/
const ACTION_PARAM = 'dispatch';
/**
* @var string Значение параметра ACTION_PARAM, при котором сервер обновлений поймёт, что CS-Cart запрашивает проверку наличия обновлений
*/
const ACTION_CHECK_UPDATES = 'updates.check';
/**
* @var string Значение параметра ACTION_PARAM, при котором сервер обновлений поймёт, что CS-Cart запрашивает скачивание пакета обнолвений
*/
const ACTION_DOWNLOAD_PACKAGE = 'updates.download_package';
/**
* @var string Уникальный идентификатор модуля
*/
protected $addon_id = 'sample_addon';
/**
* @var string Текущая версия модуля
*/
protected $addon_version;
/**
* @var string URL магазина
*/
protected $product_url;
public function __construct()
{
parent::__construct();
// адрес сервера обновлений нужно указывать в конструкторе Connector
$this->updates_server = 'https://updates.example.com';
// данные модуля
$addon = SchemesManager::getScheme($this->addon_id);
$this->addon_version = $addon->getVersion() ? $addon->getVersion() : '1.0';
// значение настройки 'license_number' модуля
$this->license_number = (string) Settings::instance()->getValue('license_number', $this->addon_id);
// данные о магазине
$this->product_name = PRODUCT_NAME;
$this->product_version = PRODUCT_VERSION;
$this->product_build = PRODUCT_BUILD;
$this->product_edition = PRODUCT_EDITION;
$this->product_url = Registry::get('config.current_location');
}
/**
* Пердоставляет данные для подключения к серверу обновлений модуля.
* Вызывается при проверке на наличие доступных обновлений.
*
* @return array
*/
public function getConnectionData()
{
// номер лицензии магазина можно получить через $this->uc_settings['license_number']
$data = [
self::ACTION_PARAM => self::ACTION_CHECK_UPDATES,
'addon_id' => $this->addon_id,
'addon_version' => $this->addon_version,
'license_number' => $this->license_number,
'product_name' => $this->product_name,
'product_version' => $this->product_version,
'product_build' => $this->product_build,
'product_edition' => $this->product_edition,
'product_url' => $this->product_url,
];
$headers = [];
return [
'method' => 'get',
'url' => $this->updates_server,
'data' => $data,
'headers' => $headers,
];
}
/**
* Скачивает пакет обновлений модуля.
*
* @param array $schema Схема пакета обновлений
* @param string $package_path Абсолютный путь на сервере, куда нужно поместить пакет обновлений
*
* @return array
*/
public function downloadPackage($schema, $package_path)
{
$download_url = new Url($this->updates_server);
$download_url->setQueryParams(array_merge($download_url->getQueryParams(), [
self::ACTION_PARAM => self::ACTION_DOWNLOAD_PACKAGE,
'package_id' => $schema['package_id'],
'addon_id' => $this->addon_id,
'license_number' => $this->license_number,
]));
$download_url = $download_url->build();
$request_result = Http::get($download_url, [], [
'write_to_file' => $package_path,
]);
if (!$request_result || strlen($error = Http::getError())) {
$download_result = [false, __('text_uc_cant_download_package')];
fn_rm($package_path);
} else {
$download_result = [true, ''];
}
return $download_result;
}
}
Как подготовить пакет обновлений?¶
Основная информация¶
Для сборки пакета обновлений нужны два архива: со старой и новой версиями модуля. Чтобы собрать пакет обновлений, используйте следующие команды из нашего SDK:
cscart-sdk addon:export #экспортировать архив с текущей версией модуля
cscart-sdk addon:build_upgrade #собрать пакет обновлений
Важно
Чтобы собирать обновления c помощью SDK, придерживайтесь следующей структуры модуля при разработке:
├── app
│ └── addons
│ └── [sample_addon]
│ ├── addon.xml
│ ├── config.php
│ ├── func.php
│ ├── Tygh
│ │ └── UpgradeCenter
│ │ └── Connectors
│ │ └── [SampleAddon]
│ │ └── Connector.php
│ └── upgrades
│ ├── [version1]
│ │ ├── migrations
│ │ │ ├── 467676233_migration1.php
│ │ │ └── 467676233_migration2.php
│ │ │
│ │ ├── validators
│ │ │ ├── validator1.php
│ │ │ └── validator2.php
│ │ │
│ │ ├── scripts
│ │ │ ├── pre_script.php
│ │ │ └── post_script.php
│ │ │
│ │ ├── extra_files
│ │ │ ├── extra_file1.php
│ │ │ └── extra_file2.php
│ │ │
│ │ └── extra
│ │ └── extra.php
│ │
│ ├── [version2]
│ │ ├── migrations
│ │ │ ├── 467676233_migration1.php
│ │ │ └── 467676233_migration2.php
│ │ │
│ │ ├── validators
│ │ │ ├── validator1.php
│ │ │ └── validator2.php
│ │ │
│ │ ├── scripts
│ │ │ ├── pre_script.php
│ │ │ └── post_script.php
│ │ │
│ │ ├── extra_files
│ │ │ ├── extra_file1.php
│ │ │ └── extra_file2.php
│ │ │
│ │ ├── extra
│ │ │ └── extra.php
...
[sample_addon]
— идентификатор модуля;[SampleAddon]
— идентификатор модуля в CamelCase;[version1]
— номер версии, например 1.1.0.[version2]
— номер версии, например 1.1.1.app/addons/[sample_addon]/upgrades/[version]/migrations
— папка с миграциями, которые надо применить при обновлении до [version].app/addons/[sample_addon]/upgrades/[version]/validators
— папка с валидаторами, которые должны выполнить свои проверки перед обновлением до [version].app/addons/[sample_addon]/upgrades/[version]/scripts
— папка с pre/post-скриптами, которые нужно выполнить перед или после обновления до [version].app/addons/[sample_addon]/upgrades/[version]/extra_files
— папка с дополнительными файлами, которые используются только в процессе обновления и не добавляются в установку CS-Cart/Multi-Vendor.app/addons/[sample_addon]/upgrades/[version]/extra/extra.php
— файл для расширения package.json пакета обновлений.Примечание
Файлы и папки в app/addons/[sample_addon]/upgrades/[version] не обязательны. Например, если у новой версии нет изменений в базе данных, то нет необходимости создавать папку с миграциями.
Миграции¶
Миграции применяются во время обновления и меняют структуру таблиц в базе данных и сами данные в них.
Для написания миграций используйте Phinx. Обратите внимание, что в CS-Cart старая версия Phinx (0.4.3), поэтому не все инструкции из современной документации могут подойти. Вот старая документация Phinx 0.4.3 о:
Класс миграции должен содержать метод up
, который будет выполняться в процессе обновления.
Например:
use Phinx\Migration\AbstractMigration;
class AddonsSampleAddonUpdateVersion extends AbstractMigration
{
public function up()
{
$options = $this->adapter->getOptions();
$pr = $options['prefix'];
$this->execute("UPDATE {$pr}addons SET version = '1.1' WHERE addon = 'sample_addon'");
}
}
Разделяйте изменения по отдельным миграциям; каждая миграция должна содержать одно логически завершённое действие.
Не переименовывайте сгенерированные в формате YYYYMMDDHHMMSS_my_new_migration.php
миграции. В случае, если вы пишете миграции вручную, присваивайте им имена согласно правилам Phinx.
Не используйте чистый SQL в миграциях для изменения структуры таблицы; используйте только методы Phinx.
Не используйте функции ядра CS-Cart в миграциях: нет гарантии, что они будут доступны во время обновления модуля. В результате процесс обновления может прерваться, а магазин — сломаться.
Валидаторы¶
Валидаторы проверяют перед установкой обновления, соответствует ли магазин определённым требованиям. Каждый валидатор — отдельный класс в пространстве имён Tygh\UpgradeCenter\Validators
.
Валидатор должен реализовывать интерфейс IValidator и содержать 2 обязательных метода:
getName()
должен возвращать строку с отображаемым именем валидатора;check($schema, $request)
должен возвращать массив с двумя значениями:- флаг (boolean), который означает, что проверка успешно пройдена;
- строка (string) с сообщением, которое отобразится, если результат проверки будет неудачным.
Например:
<?php
namespace Tygh\UpgradeCenter\Validators;
/**
* Проверяет минимальную версию PHP.
*/
class PhpVersionValidator implements IValidator
{
protected $minimal_php_version = '5.6.0';
/** @inheritdoc */
public function check($schema, $request)
{
if (version_compare(PHP_VERSION, $this->minimal_php_version) == -1) {
return [
false,
__('checking_php_version_is_not_suitable', [
'[version]' => PHP_VERSION,
'[min]' => $this->minimal_php_version,
'[max]' => '7.x',
]),
];
}
return [true, []];
}
/** @inheritdoc */
public function getName()
{
return 'PHP Version';
}
}
Скрипты¶
Скрипты расширяют или меняют то, как Центр обновлений работает с вашим пакетом обновлений при обновлении. Есть 2 типа скриптов:
перед обновлением: скрипт pre_script.php выполняется после того, как прошли все проверки валидаторов;
после обновления: скрипт post_script.php выполняется после того, как установлен пакет обновлений. Пост-скрипт в основном используется для того, чтобы показывать какие-то уведомления после обновления. Чтобы сделать такое уведомление, добавьте новый элемент в массив
$upgrade_notes
в скрипте:<?php $upgrade_notes[] = [ 'title' => 'Sample Add-on v1.1 Changes', 'message' => 'Sample Add-on v1.1 Changes Description', ];
Эти скрипты включены в контекст класса \Tygh\UpgradeCenter\App
и могут использовать все свойства и методы этого класса. Также вы можете в них использовать все функции и классы CS-Cart.
Папка Extra Files¶
В папку extra_files вы можете положить файлы, которые используются только при обновлении и не добавляются в установку CS-Cart/Multi-Vendor.
Расширение схемы пакета обновлений¶
Чтобы расширить схему, напишите свой скрипт в файле extra.php. Скрипт должен возвращать массив. Этот массив сольётся с package.json пакета обновлений.
Так вы можете добавлять любые дополнительные данные в пакет обновлений. Эти данные можно использовать в процессе обновления в пре- и пост-скриптах.
Например, вот как можно предложить клиентам пропустить встроенное в CS-Cart резервное копирование при установке обновления:
<?php
return [
'backup' => [
'is_skippable' => true,
'skip_by_default' => true,
],
];
Как настроить собственный сервер обновлений?¶
Примечание
Свой сервер обновлений настраивать необязательно — есть и другой путь. Но если ваши модули используют свой механизм лицензирования, то стоит завести свой сервер обновлений.
Когда CS-Cart отправляет запрос о наличии обновлений, сервер обновлений должен вернуть XML следующего вида:
<?xml version="1.0" encoding="utf-8" ?>
<update>
<packages>
<item id="unique_update_package_id">
<file>update_package_name.zip</file>
<name>Update package name</name>
<description><![CDATA[Update package description.]]></description>
<from_version>1.0</from_version>
<to_version>1.1</to_version>
<timestamp>1547199854</timestamp>
<size>2048</size>
</item>
</packages>
</update>
update/packages/item
содержит в себе информацию о доступном обновлении;update/packages/item@id
— уникальный идентификатор пакета обновлений. Когда коннектор передаёт этот идентификатор серверу обновлений, то сервер должен предоставить архив с пакетом обновлений;update/packages/item/file
— название архива с пакетом обновлений;update/packages/item/name
— заголовок пакета обновлений (отобразится в Центре обновлений);update/packages/item/description
— описание пакета обновлений. Может содержать HTML-разметку;update/packages/item/from_version
— текущая версия модуля;update/packages/item/to_version
— версия, до которой будет обновлен модуль;update/packages/item/timestamp
— дата создания пакета обновлений в формате UNIX timestamp;update/packages/item/size
— размер пакета обновлений в байтах.Примечание
Если доступных обновлений нет, сервер должен вернуть пустой ответ.
Вот как данный пакет обновлений отобразится в Центре обновлений: