Мультивитринность

Мультивитринность позволяет организовать отдельные витрины в рамках одного интернет-магазина и управлять ими из единой панели. Все витрины работают с общей базой данных, но могут иметь различные дизайн, каталоги товаров, пользователей и настройки.

Отдельные витрины могут быть полезны для выделения отдельных направлений продаж, позволяя сконцентрироваться на конкретной целевой аудитории. Например, в магазине продается одежда, в том числе детская. Тогда можно сделать отдельную витрину со своим дизайном, и отображать на ней только детскую одежду.

Подсказка

Дополнительную информацию о мультивитринности можно почерпнуть из этого видео YouTube.

Управление витринами

Управление витринами доступно из раздела “Администрирование → Магазины”.

Основные параметры витрины:

  • Название магазина.
  • URL витрины — в качестве значения необходимо указать домен магазина, например example.com. В случае, если магазин находится в поддиректории, то эту поддиректорию тоже необходимо указать, например example.com/stores/. Значение этого параметра будет использовано для определения витрины в клиентской части.
  • Безопасный URL витрины — аналогично параметру “URL витрины”. Значение “Безопасный URL витрины” будет использовано для определения витрины в клиентской части при защищенном соединении. В большинстве случаев значение “Безопасный URL витрины” равно значению “URL витрины”.

Для хранения информации о витринах используются следующие таблицы БД:

  • cscart_companies — основные параметры витрины.
  • cscart_company_descriptions — параметры, зависящие от языка.

Панель администратора

Для удобного управления объектами панель управления имеет 3 режима:

  • All stores — режим управления всеми витринами. В этом режиме администратору доступны управление всеми объектами и параметрами интернет магазина.
  • Selected storefront — режим управления витриной. В этом режиме панель настроена на эффективное управление данными конкретной витрины.
  • Simple Ultimate — частный случай, когда в магазине всего лишь одна витрина. В таком режиме администратор не имеет никаких ограничений и может управлять любыми объектами. В этом режиме опускаются ряд проверок, связанных с определением владельца того или иного объекта, что ускоряет работу скриптов. В режиме Simple ultimate значение Registry::get('runtime.simple_ultimate') будет определено в true, таким образом можно разделять логику внутри кода.

Текущий идентификатор витрины можно получить следующим образом:

Registry::get('runtime.company_id')

Соответственно, если в текущий момент администратор находится в режиме All stores, значением этого ключа будет 0. Нужно отметить, что в режиме Simple ultimate значение Registry::get('runtime.company_id') также будет равно 0, для получения текущего идентификатора витрины необходимо использовать Registry::get('runtime.forced_company_id').

Некоторые интерфейсы, например, темы, могут требовать перехода в режим “Selected storefront” — в этом случае администратор увидит диалог выбора витрины.

Типы объектов

Все объекты условно можно разделить на два типа: с владельцем и без владельца.

Объекты обязательно имеющие владельца (витрину)

  • Категории товаров
  • Страницы
  • Платежные системы
  • Характеристики товаров
  • Опции товаров
  • Товары
  • Промо-акции/скидки
  • Службы доставки
  • Пользователи
  • Темы
  • Заказы
  • Фильтры

Все таблицы объектов в БД имеют поле company_id, которое однозначно определяет владельца - витрину. Доступ к таким объектам можно получить либо в режиме All stores, либо в режиме Selected storefront, используя витрину - владельца.

В списке представлены только базовые объекты. Модули могут иметь свои объекты, которые также могут иметь владельца (витрину).

Объекты без владельца (витрины)

  • Языки
  • Валюты
  • Поля профиля

Эти объекты не имеют прямой связи с витринами и могут быть доступны только в режиме All stores.

В списке представлены только базовые объекты. Модули могут иметь свои объекты, которые так же могут быть без владельца.

Шаринг объектов

Витрины могут иметь как собственные наборы объектов, которые доступны только для указанной витрины, так и общие для разных витрин. Шаринг — это механизм, позволяющий предоставить доступ к объектам одной витрины из другой. Несмотря на то, что объект расшарен для витрины, доступ для редактирования объекта будет иметь только владелец, в остальных случаях объект будет доступен для чтения.

Примечание

Есть более подробная статья о шаринге.

Схема

Доступные для шаринга объекты описываются в схеме.

Пример описания:

'shippings' => array(
    'controller' => 'shippings',
    'api' => 'shippings',
    'mode' => 'update',
    'type' => 'tpl_tabs',
    'params' => array(
        'object_id' => '@shipping_id',
        'object' => 'shippings'
    ),
    'table' => array(
        'name' => 'shippings',
        'key_field' => 'shipping_id',
    ),
    'buttons' => array(
        'type' => 'save_cancel',
        'but_name' => 'dispatch[shippings.update_shipping]',
    ),
    'request_object' => 'shipping_data',
    'have_owner' => true,
),

Где:

  • controller — имя бэкенд контроллера, при взаимодействии с которым будут обрабатываться правила шаринга, обязательное поле
  • mode — режим работы контроллера, при взаимодействии с которым будут обрабатываться правила шаринга, обязательное поле
  • api — имя сущности в API, при взаимодействии с которым будут обрабатываться правила шаринга
  • type — тип отображения в интерфейсе, на данный момент реализован только tpl_tabs
  • params - массив параметров, которые будут собраны и переданы в шаблон таба шаринга
  • object_id — идентификатор объекта, можно использовать знак “@”, в этом случае, значение этого ключа будет получено из $_REQUEST
  • object — символьное название объекта, например “language”
  • table — массив с информацией о таблице объекта
  • name — имя таблицы
  • key_field — первичный ключ
  • request_object — имя ключа в $_REQUEST, значение которого будет использовано для поиска текущего владельца — витрины
  • have_owner — флаг о том, что объект имеет владельца - витрину, то есть в таблице есть поле company_id
  • conditions — массив дополнительных условий
    • display_condition — условия отображения таба с выбором витрин
    • skip_selection — условия исключения для выборки объекта
  • no_item_text — символьный код языковой переменной, содержимое которой будет выведено в табе выбора витрин
  • pre_processing — имя функции, которая будет выполнена перед сохранением значения в шаред таблицы
  • post_processing — имя функции, которая будет выполнена после сохранения значения в шаред таблицы
  • skip_checking_status — флаг, позволяет отключить механиз шаринга в момент обновления статуса объекта

На данный момент для шаринга доступны следующие объекты:

  • Характеристики товаров
  • Опции товаров
  • Фильтры товаров
  • Службы доставки
  • Платежные системы
  • Промо-акции/скидки
  • Валюты
  • Языки
  • Поля профиля
  • Страницы

Примечание

Модули могут расширять схему шаринга и добавлять в нее свои объекты.

Процесс шаринга

Страницы редактирования объектов, которых можно расшарить, имеют отдельный таб Share, который отображается автоматически в зависимости от настроек схемы, все витрины перечисленные в этом табе будут иметь доступ к объекту. Сохранение информации о шаринге происходит автоматически на основе данных схемы. Механизм шаринга отлавливает запрос до выполнения его обработки текущим контроллером, осуществляет сопоставление данных со схемой и, в случае успешности проверок, сохраняет данные шаринга. Расшаренные объекты сохраняются в таблицу cscart_ult_objects_sharing, которая имеет следующие поля:

  • share_company_id - идентификатор витрины
  • share_object_id - идентификатор объекта
  • share_object_type - символьный код типа объекта

Выборка объектов

При выборке объектов, которые описаны в схеме, исходный запрос будет расширен для проверки прав текущей витрины.

Пример выборки объекта страницы для витрины с идентификатором 3:

Оригинальный запрос:

SELECT cscart_pages.page_id
  FROM cscart_pages
  WHERE page_id = 2

Конечный запрос:

SELECT cscart_pages.page_id
  FROM cscart_pages
  INNER JOIN cscart_ult_objects_sharing ON (
    cscart_ult_objects_sharing.share_object_id = cscart_pages.page_id
    AND cscart_ult_objects_sharing.share_company_id = 3
    AND cscart_ult_objects_sharing.share_object_type = 'pages'
  )
  WHERE page_id = 2

Таким образом, если информации о шаринге объекта страницы с идентификатором 2 нет в таблице cscart_ult_objects_sharing, то запрос ничего не вернет. Расширение запроса происходит автоматически и реализовано на низком уровне работы с БД, так любой запрос выполненный через db_query, будет проанализирован и расширен если это нужно. Логика, отвечающая за расширение запроса, находится в функции fn_ult_db_query_process, которая в свою очередь является обработчиком стандартного хука db_query_process. Для того, чтобы предотвратить расширение запроса, можно установить в true значение ключа runtime.skip_sharing_selection в Registry:

Registry::set('runtime.skip_sharing_selection', true);

Таким образом, запросы не будут изменены.

Как дебажить:

  • Debug-панель. В панели будут отображены все запросы в конечном виде.
  • Константа DEBUG_QUERIES. Если определить константу, то при каждом вызове db_query, будет выведен итоговый запрос.
  • xdebug + IDE. Xdebug — мощный инструмент для отладки кода.

Шаринг товаров

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

Переопределение значений

Переопределение значений — возможность для отдельной витрины изменить значения свойств расшаренного на нее объекта. Например, один и тот же товар для разных витрин может иметь свое название, описание, цену. Данный функционал реализуется самостоятельно для каждого из требуемых объектов. На текущий момент есть следующие объекты, которые имеют возможность переопределения значений:

  • Товары
  • Варианты опций товара
  • Языковые переменные
  • Статусы

Товары

Следующие свойства товаров могут быть изменены отдельно для витрины:

  • Название
  • Краткое описание
  • Полное описание
  • Мета-данные: Название страницы (SEO)
  • Мета-данные: Мета-описание (SEO)
  • Мета-данные: Мета-ключевые слова (SEO)
  • Ключевые слова для поиска
  • Промо-текст
  • Цена

Определение того, какие свойства доступны для обновления, реализовано на уровне кода и не имеет никаких схем для простого расширения. Для сохранения значений используются следующие таблицы:

  • cscart_ult_product_prices
  • cscart_ult_product_descriptions

Варианты опций товара

Каждая витрина может указать свои модификаторы вариантов опций. Для хранения этих значений используется таблица cscart_ult_product_option_variants.

Языковые переменные

При редактировании языковых переменных в режиме Selected storefront, значения будут сохраняться только для текущей витрины. Для хранения значений используется таблица: cscart_ult_language_values.

Статусы

Следующие поля статусов могут быть изменены отдельно для витрины:

  • Тема email
  • Заголовок email

Для сохранения значений используются таблица: cscart_ult_status_descriptions.

Полезные функции

  • fn_get_company_condition - возвращает строку с условием по идентификатору витрины, если это необходимо.
  • fn_ult_get_shared_companies - возвращает идентификаторы витрин, для которых расшарен объект
  • fn_ult_is_shared_product - проверяет, расшарен ли товар для витрины
  • fn_ult_get_shared_product_companies - возвращает идентификаторы витрин, для которых расшарен товар
  • fn_share_object - делает шаринг объектов одной компании для другой
  • fn_share_object_to_all - расшаривает объект по всем витринам
  • fn_ult_update_share_object - расшаривает объект в конкретную витрину

Настройки

Есть 2 типа настроек:

  • Общие настройки. Это настройки, которые не могут быть определены отдельно для каждой витрины. Такие настройки доступны только в режиме All stores.
  • Настройки витрины. Это настройки, которые для каждой витрины могут иметь свои значения. Такие настройки доступны как в режиме Selected storefront, так и в режиме All stores, в последнем случае изменение значения настройки возможно лишь с указанием обновления значения во всех витринах.

Каждая настройка сама определяет свой тип, для этого используется поле edition_type в таблице cscart_settings_objects. Если в этом поле есть строка VENDOR, то настройка может иметь специфичное значение для каждой витрины, такие значения сохраняются в таблице cscart_settings_vendor_values, за исключением Simple ultimate режима, в последнем случае, данные сохраняются в таблицу cscart_settings_objects.

Доступ к значениям настроек текущей витрины можно получить из Registry, например:

Registry::get('settings.Appearance.frontend_default_language')

Для управления настройками на уровне кода, необходимо использовать класс Settings, в котором реализованы все необходимые методы.

Определение URL витрины в клиентской части

Витрины могут располагаться как на отдельном домене, так и в поддиректории основного домена. Для определения используется параметр витрины URL витрины (Storefront URL), а для https соединения - Безопасный URL витрины (Secure storefront URL). Вся логика, отвечающая за определение витрины находится в функции fn_init_store_params_by_host.

Для правильной работы витрин необходимо настроить веб-сервер таким образом, чтобы все запросы, отправляемые на несуществующие физически адреса, обрабатывались входным скриптом index.php.

Особенности при работе с apache

Из коробки cs-cart уже снабжен правильным файлом .htaccess, важно проверить, чтобы его поддержка была включена в настройках веб-сервера. Выдержка из файла:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L,QSA]

Таким образом, все необходимые запросы будут отправлены на index.php.

Особенности при работе с nginx

Для случаев, когда nginx напрямую проксирует в php (FastCGI, php-fpm), необходимо самостоятельно настроить правила переадресации. Часть конфига, отвечающая за эту логику:

location / {
    # Перенаправляем все запросы к несуществующим директориям и файлам на index.php
    try_files $uri $uri/ /index.php?$args;
}

location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {
    try_files $uri =404;
}

location ~ \.php$ {
    include fastcgi.conf;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}