Перейти к основному содержимому

Внедрение управления продуктовой информацией

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

Использование PIM позволяет интегрировать информацию из разных источников в единую платформу, логично ее структурировать и перераспределять различными способами — для e-commerce-платформ, веб-сайтов, печатных каталогов и т.д. Также PIM обеспечивает полное и динамическое управление данными о продуктах, включая настройку и фильтрацию информации для разных типов продуктов, управление ценами и валютами, различными единицами измерения, многоязычными переводами, мультимедийным контентом и многим другим.

Возможность централизовать всю эту информацию в одной платформе позволяет управлять продуктами полностью независимо от каналов распространения: PIM отвечает за передачу в каждый канал только той информации, которая ему необходима. Такое централизованное управление обеспечивает согласованность продуктовой информации, устраняет необходимость дублирования данных в разных платформах и поддерживает их синхронизацию, снижая количество ошибок в дизайне и данных. Это также дает преимущества с точки зрения затрат на управление каталогами, прайс-листами и онлайн-платформами.

Использование PIM является критически важным для компаний, которым необходимо продавать или представлять свои продукты на различных платформах и в разных медиа, а также для бизнес-сценариев, где требуется обмен продуктовой информацией между разными отделами или направлениями разработки при сохранении централизованного управления этими данными. Потребность в PIM возрастает по мере увеличения количества управляемых продуктов, поскольку затраты на управление и поддержку данных существенно сокращаются за счет концентрации всего в одной платформе.

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

ПИМ архитектура

Как видно на предыдущей диаграмме, продукты могут быть импортированы в PIM-систему путем загрузки файлов или реализации специальных потоков для интеграции с другим программным обеспечением или базами данных. Продукты также могут создаваться и обогащаться непосредственно в среде PIM через графический пользовательский интерфейс (GUI). После этого данные о продуктах могут экспортироваться в e-commerce-платформы или другое ПО, либо корректно выгружаться в файлы. Подводя итог, в этом разделе вы познакомились с концепцией PIM и преимуществами, которые PIM-система дает с точки зрения управления и распространения продуктовых данных. В следующем разделе мы шаг за шагом создадим сущность продукта, определив класс Pimcore, который будет представлять концепцию продукта.

Определение сущности продукта

В предыдущем разделе вы узнали о концепции управления продуктовой информацией (Product Information Management). Как следует из названия, в PIM-системе обязательно необходимо создать класс, который представляет продукты, и именно этим мы займемся в данном разделе.

Pimcore не накладывает каких-либо ограничений на саму концепцию продукта, поэтому мы можем просто создать класс с именем Product и определить все атрибуты в соответствии с нашими требованиями. Перейдите в Settings | Data Objects | Classes и создайте новый класс с именем Product. Если у вас установлен демо-пакет Pimcore, вы уже найдете там заранее определенный класс Product. Обратите внимание, что это лишь один из возможных вариантов определения концепции продукта, и вы можете добавлять или удалять атрибуты класса в зависимости от своих задач. Ниже вы можете увидеть пример того, как может выглядеть такой класс.

Класс продукта

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

  • Информация о продукте (Product Information): на этой панели вы можете найти подразделение для хранения акций (SKU), которая является уникальным идентификатором для продуктов и цены продукта. Затем мы добавили название продукта и описание в качестве локализованных полей, чтобы вы могли предоставить переводы.
  • Категоризация (Categorization): В этой панели определены атрибуты для классификации продукта. Атрибут бренда — это пользовательское поле одиночного выбора с распространенными брендами в качестве вариантов. Также присутствует предопределенный список опций для выбора страны производства продукта. Последний атрибут — это связь many-to-one с другим классом, предназначенная для описания категорий.
  • Состав (Composition): В этой панели используется продвинутый компонент связи many-to-many, который связывает продукт с материалами, из которых он состоит. В рамках этой связи можно указать процентное соотношение различных материалов для определения состава продукта.
  • Атрибуты (Attributes): В этой панели определены атрибуты, которые используются для представления вариантов продукта, таких как цвет и размер. Для атрибута цвета определена связь с отдельным специализированным классом.
  • Изображения (Images): Как следует из названия, эта панель предназначена для хранения изображений продукта. В частности, используется Fieldcollection для управления неопределенным количеством изображений. Для полноты картины на следующем скриншоте будут показаны остальные определения классов:

Определения класса

На предыдущем скриншоте вы можете увидеть определения классов Category, Material и Color. Для класса Category был определен уникальный атрибут кода, а также несколько локализуемых полей — name и description. Аналогичная структура используется и для класса Material, с добавлением еще одного поля для определения типологии материала. Для класса Color был добавлен атрибут для задания шестнадцатеричного (hex) значения цвета. На следующем скриншоте показан пример созданного продукта:

Продукция

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

Создание вариантов продуктов

В предыдущем разделе вы увидели, как определить сущность Product и пример создания объектa продукта. В этом разделе вы узнаете, как включить наследование для класса Product, чтобы создавать варианты продуктов и определять конфигурируемые продукты. Далее в этом разделе мы также рассмотрим примеры кода на PHP (Hypertext Preprocessor), которые покажут, как на практике использовать варианты в процессе разработки.

Чтобы включить наследование для класса Product, откройте определение класса и выберите корневой элемент General Settings. Вы можете включить наследование классов и разрешить создание вариантов, установив флажки, показанные на следующем скриншоте:

Обеспечение наследства

Как видно на предыдущем скриншоте, можно включить три параметра:

  • Allow inheritance (Разрешить наследование): Если включено, это свойство разрешает наследование между объектами в древовидной структуре. Дочерние объекты могут быть экземплярами того же класса или принадлежать к другому классу.
  • Allow variants (Разрешить варианты): Если включено, появляется возможность создавать варианты объектов. Определение варианта является особым видом наследования. Тип варианта выбрать нельзя — класс варианта всегда совпадает с классом родительского объекта.
  • Show variants in tree (Показывать варианты в дереве): Если включено, это свойство позволяет отображать варианты в дереве объектов.

После включения этих параметров необходимо нажать кнопку Save, чтобы применить изменения. После активации поддержки вариантов для класса можно создавать варианты продуктов. Чтобы создать вариант, щелкните правой кнопкой мыши по ранее созданному продукту, выберите Add Variant и укажите имя варианта.

На следующем скриншоте показан пример конфигурируемого продукта с определенными вариантами:

Пример варианта продукта

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

В данном примере для создания конфигурируемого продукта с различными вариантами был использован атрибут Color. Теперь, когда вы узнали, как создавать варианты продуктов в интерфейсе Pimcore, давайте посмотрим, как использовать варианты на практике в коде, рассмотрев несколько примеров.

Ключевым аспектом, связанным с вариантами, является свойство Type объекта. Это системное свойство, определенное для объектов всех классов. Оно не может быть заполнено вручную и автоматически задается при создании элемента. Существуют три различных типа объектов:

  • folder: этот тип присваивается при создании папки в разделе Data Objects.
  • object: этот тип присваивается при создании экземпляра объекта класса.
  • variant: этот тип присваивается при создании варианта объекта.

При разработке в Pimcore важно учитывать, что все встроенные методы, выполняющие поиск списков объектов, по умолчанию учитывают только объекты типов folder и object. Поэтому, если требуется получить варианты, необходимо явно указать это в запросе. В следующих примерах мы покажем, как это сделать.

Родительский PHP-класс

В этом первом примере вы узнаете, как создать родительский PHP-класс для класса Product, а также как реализовать метод для получения вариантов продукта.

Создание этого класса не является строго обязательным для работы с вариантами, однако это полезная возможность, которая может применяться в различных сценариях. Класс Concrete — это PHP-класс, который изначально расширяется всеми классами Pimcore и содержит общие методы для всех объектов, такие как save() и getById(), если привести несколько примеров.

В следующем фрагменте кода показан пример родительского класса продукта с определенным методом для получения вариантов продукта:

<?php  

namespace App\Model;
use Pimcore\Model\DataObject;

class AbstractProduct extends DataObject\Concrete
{
/**
* @return self[]
*/
public function getVariants(): array
{
$variantType = self::OBJECT_TYPE_VARIANT; //variant
$variants = $this->getChildren(array($variantType))->load();
return $variants;
}
}

Как видно из предыдущего фрагмента кода, этот класс расширяет ранее упомянутый класс Concrete. В нем мы создали метод getVariants(), который вызывает исходный метод getChildren(), явно указывая, что необходимо получить объекты типа variant.

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

После создания PHP-класса необходимо корректно указать соответствующее свойство в классе Product, как показано на следующем скриншоте:

Настройка класса Parent PHP

Как видно на скриншоте, в поле Parent PHP Class нужно указать полное пространство имен класса. После этого необходимо нажать кнопку Save, чтобы применить изменения.

После определения родительского PHP-класса вы можете вызывать метод getVariants() для каждого экземпляра объекта продукта, как показано в следующем примере кода:

<?php  
use Pimcore\Model\DataObject;

$product = DataObject\Product::getById(1);
$variants = $product->getVariants();

В приведенном выше примере видно, что поскольку класс Product расширяет ранее созданный класс AbstractProduct, каждый объект продукта может использовать определенный в нем метод getVariants().

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

Листинг объектов

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

В следующем фрагменте кода показано, как варианты продуктов могут быть получены через листинг-запросы:

<?php  

use Pimcore\Model\DataObject\Product;
use Pimcore\Model\DataObject\AbstractObject;

$list = new Product\Listing();
$list->setObjectTypes([AbstractObject::OBJECT_TYPE_VARIANT]);
$variants = $list->load();

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

Метод load() возвращает массив объектов, соответствующих условиям листинга.

В этих двух первых примерах вы научились получать уже существующие варианты продуктов. В следующем примере мы рассмотрим, как создать новый вариант продукта.

Создание нового варианта

В предыдущих примерах вы узнали, как получать существующие варианты. В этом примере вы узнаете, как создать вариант продукта.

В следующем фрагменте кода показано, как создать вариант для существующего продукта:

<?php  
use Pimcore\Model\DataObject\Product;
use Pimcore\Model\DataObject\AbstractObject;

$tshirt = Product::getByName("Classic T-Shirt", "en", 1);

$orange = new Product();
$orange->setKey("Orange");
$orange->setParent($tshirt);
$orange->setType(AbstractObject::OBJECT_TYPE_VARIANT);

$orange->save();

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

В примере используется метод getByName. Подобные методы автоматически генерируются для каждого атрибута класса при сохранении определения класса. Первый аргумент — значение, по которому выполняется поиск, второй — язык (для локализованных полей), третий — параметр лимита. Если лимит равен 1, возвращается один объект; в противном случае возвращается экземпляр листинга класса.

Подводя итог, в этом разделе вы узнали, как включить поддержку вариантов для класса Product и как создавать варианты продуктов. Также на примерах кода было показано, как получать существующие варианты и как создавать новые.

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

Создание комплектного продукта

В предыдущем разделе вы узнали, как включить и создавать варианты продуктов, а также как использовать их на практике в примерах кода. В этом разделе вы узнаете, как определять комплектные продукты.

В маркетинге понятие Product Bundling обозначает практику предложения набора отдельных продуктов или услуг как одного объединенного продукта или сервисного пакета. Как правило, комплектные продукты представляют собой наборы или, в более общем виде, группу логически связанных продуктов, преимущество которых заключается в сниженной цене при покупке вместе по сравнению с покупкой по отдельности.

В рамках этой главы мы сначала рассмотрим, как модифицировать ранее созданный класс Product для определения комплектных продуктов. Затем мы изучим, как автоматически выполнять операции при сохранении продукта, чтобы рассчитывать цену комплекта.

Определение комплектных продуктов

В этом разделе вы узнаете, как модифицировать класс Product, чтобы иметь возможность определять комплектные продукты. Самым простым решением является создание самоссылочного атрибута связи с самим классом Product. Это позволит выбирать продукты, которые будут входить в комплект, из ранее созданных продуктов.

Чтобы добавить этот атрибут, необходимо выполнить следующие действия:

  1. Откройте определение класса Product.
  2. Щелкните правой кнопкой мыши по панели Product Information и выберите Add Data Component | Relation | Many-to-Many Object Relation.
  3. Укажите bundle_products в качестве имени атрибута и Bundle Products в качестве заголовка атрибута.
  4. В списке Allowed Classes выберите класс Product.
  5. В списке Visible Fields выберите поля класса, которые вы хотите отображать в связи — например, sku и name.
  6. Нажмите кнопку Save, чтобы применить изменения.

На следующем скриншоте показано, как должен выглядеть класс Product после выполнения этих действий:

Атрибут продуктов связывания

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

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

Отношение продуктов

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

Атрибут цены

На предыдущем скриншоте видно, что этот атрибут помечен как Not editable, а это означает, что нам необходим метод для расчета цены комплекта.

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

Создание обработчика событий

В предыдущем разделе вы узнали, как определять наборные продукты (bundle products). В типовых сценариях цена наборного продукта меньше суммы цен отдельных продуктов, из которых он состоит. Может быть полезно автоматически рассчитывать цену наборного продукта по заранее заданному правилу, вместо того чтобы вводить это значение вручную.

В этом разделе вы узнаете, как создать обработчик событий, который будет перехватывать события, срабатывающие при сохранении продукта. Сохранение объекта — лишь одно из событий, на которые можно подписаться в Pimcore; система поддерживает все операции создания, чтения, обновления и удаления (CRUD) для объектов, ассетов и документов, операции над пользователями, открытие поисковых или табличных списков и многие другие. Чтобы создать обработчик событий, сначала необходимо зарегистрировать новый класс в файле config/services.yaml, как показано в следующем фрагменте кода:

services:  
App\EventListener\DataObjectListener:
tags:
- { name: kernel.event_listener, event: pimcore.dataobject.postUpdate, method: onObjectPostUpdate }

Как видно из предыдущего фрагмента, новый сервис можно определить, указав пространство имен класса. В атрибуте tags необходимо определить один или несколько тегов сервиса. Каждый тег состоит из трех свойств:

  • name: имя тега. Необходимо указать фиксированное значение kernel.event_listener, чтобы сервис был корректно распознан как обработчик событий.
  • event: используется для указания события, на которое мы хотим подписаться. В нашем случае нас интересует событие postUpdate для объектов. Как следует из названия, это событие срабатывает после сохранения объекта.
  • method: в этом свойстве необходимо указать имя метода класса, который будет автоматически вызван при срабатывании события.

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

<?php  
namespace App\EventListener;

use Pimcore\Event\Model\DataObjectEvent;
use Pimcore\Model\DataObject\Product;

class DataObjectListener
{
public function onObjectPostUpdate(DataObjectEvent $e): void
{
$obj = $e->getObject();

if ($obj instanceof Product) {
$bundleProducts = $obj->getBundle_products();
$currentPrice = $obj->getBundlePrice();

if (count($bundleProducts) >0) {
$bundlePrice = 0;

foreach($bundleProducts as $product){
$price = $product->getPrice()->getValue();
$bundlePrice += $price;
}

//вычитаем 20% от суммы
$bundlePrice = round($bundlePrice*0.8,2);

//добавляем проверку, чтобы избежать циклических сохранений
if ($bundlePrice != $currentPrice) {
$obj->setBundlePrice($bundlePrice);
$obj->save();
}
}
}
}
}

Давайте проанализируем предыдущий фрагмент кода, чтобы понять ключевые аспекты. Прежде всего, вы можете заметить, что мы определили метод с именем onObjectPostUpdate, который является именем метода, указанным в файле services.yaml.

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

Для каждого продукта, входящего в состав пакета (bundle), мы суммируем цену продукта и в конце уменьшаем эту сумму на 20%. Обратите внимание, что это всего лишь пример, и вы можете определить собственное правило.

Чтобы избежать циклического зацикливания при сохранении продукта, мы сохраняем продукт только в тех случаях, когда новая вычисленная цена отличается от предыдущей. Если опустить эту проверку, операция сохранения, выполненная в этом сервисе, снова вызовет это же событие.

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

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

Управление разными типами продуктов

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

С помощью Objectbricks нам достаточно создать небольшие наборы атрибутов для описания специфических полей и позволить нашему классу динамически добавлять эти bricks. Как следует из названия, объекты класса могут состоять из одного или нескольких bricks, добавленных к общим атрибутам. Перед созданием определений Objectbrick необходимо создать атрибут в классе Product, который будет вмещать различные bricks. Чтобы создать этот атрибут, нужно щелкнуть правой кнопкой мыши по компоненту панели внутри определения класса и выбрать Add Data Component | Structured | Objectbricks, указать имя атрибута и нажать кнопку Save, чтобы применить изменения. На следующем скриншоте вы можете увидеть пример этого шага.

Создание атрибута

Как видно на предыдущем скриншоте, мы создали атрибут типа Objectbricks. В качестве необязательного параметра можно задать максимальное количество bricks, которые могут быть привязаны к каждому объекту. Теперь, когда атрибут определен в классе Product, мы можем определить один или несколько Objectbricks. Для этого перейдите в Settings | Data Objects | Objectbricks. Чтобы добавить новое определение Objectbrick, нужно нажать кнопку Add, ввести имя brick и нажать кнопку OK. На следующем скриншоте вы можете увидеть пример созданного Objectbrick:

Создание атрибута2

Как видно на предыдущем скриншоте, панель определения Objectbrick аналогична панели создания класса. Кроме того, для Objectbricks можно выбрать классы и конкретные атрибуты, к которым будет привязан Objectbrick. В этом примере мы привязали Objectbrick к классу Product и выбрали ранее созданный атрибут.

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

Objectbrick в объекте продукта

Как видно на предыдущем скриншоте, в объектах продукта Objectbricks отображаются как отдельная секция. К каждому объекту можно прикрепить один или несколько Objectbricks (в пределах заданного лимита), при этом каждый тип Objectbrick может быть прикреплен к объекту только один раз.

Подводя итог, в этом разделе вы узнали, как управлять разными типами продуктов с помощью Objectbricks. Objectbricks можно рассматривать как подмножества атрибутов, которые можно прикреплять к классам для расширения концепции класса дополнительными атрибутами. Для класса Product, например, можно создавать специфические атрибуты для рубашек, обуви и т.д.

В следующем разделе вы узнаете, как настроить workflow, который позволит пошагово контролировать полноту информации о продукте.

Работа с workflow

Workflow состоит из последовательности процессов и задач, которые описывают работу, необходимую для достижения общей цели. Обычно workflow можно представить в виде графа. Управление workflow в Pimcore позволяет настраивать несколько workflow для Assets, Documents и DataObjects, чтобы поддерживать процессы сопровождения данных, жизненные циклы элементов и различные другие процессы. Workflow в Pimcore основаны на компоненте workflow Symfony и расширяют его специфическими возможностями. Прежде чем приступить к настройке workflow в Pimcore, рассмотрим базовые понятия компонента workflow Symfony:

  1. Тип workflow «Workflow»: это тип workflow по умолчанию, который позволяет моделировать workflow-сеть, являющуюся подклассом сети Петри. Для такого типа workflow элемент может одновременно находиться в нескольких состояниях workflow.
  2. Тип workflow «State Machine»: конечный автомат является подмножеством workflow, и его назначение — хранить состояние модели. Конечный автомат не может одновременно находиться более чем в одном состоянии.
  3. Place (место): место — это шаг в workflow, который описывает характеристику или статус элемента. В зависимости от места элемент может отображаться в определенном представлении — например, с фокусом только на переводы. В следующем разделе мы рассмотрим, как создать пользовательский layout для класса Pimcore.
  4. Marking Store: хранилище маркеров сохраняет текущее место (или места) для каждого элемента.
  5. Transition (переход): переход описывает действие, которое выполняется для перемещения из одного места в другое. Переходы могут быть разрешены или запрещены в зависимости от дополнительных критериев, а также могут требовать ввода пользователем дополнительных заметок и информации.
  6. Transition Guard: определяет критерий, который указывает, разрешен ли переход в текущий момент.

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

Настройка пользовательского layout

Как упоминалось ранее, в различных place workflow мы можем показывать для элемента пользовательское представление. Это достигается за счет настройки пользовательских layout’ов на соответствующем классе. Чтобы создать пользовательский layout, откройте определение класса и нажмите кнопку Configure custom layouts. После нажатия откроется модальное окно, в котором вы сможете добавить новый пользовательский layout или загрузить ранее созданный.

Чтобы создать новый layout, нажмите кнопку Add. В открывшемся модальном окне необходимо указать имя нового layout и его идентификатор (ID), как показано на следующем скриншоте:

создать новый layout

Как видно на предыдущем скриншоте, для добавления нового пользовательского layout достаточно заполнить поля Name и Unique identifier, после чего нажать кнопку OK для подтверждения.

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

Настройка layout

Как видно на предыдущем скриншоте, модальное окно настройки состоит из двух основных панелей. В левой панели отображается ранее определенная структура класса, а в правой панели вы можете перетаскивать атрибуты, которые хотите отобразить в пользовательском layout, из левой панели.

Для каждого атрибута вы можете изменить некоторые свойства, например разрешить или запретить редактирование самого атрибута. После того как вы настроили пользовательский layout, необходимо нажать кнопку Save, чтобы применить изменения.

Теперь, когда вы увидели, как настраиваются пользовательские layouts, давайте рассмотрим, как использовать их в конфигурации workflow.

Конфигурация workflow в Pimcore

В предыдущем разделе вы узнали, как настраивать пользовательские layouts для классов Pimcore. В этом разделе вы узнаете, как настраивать workflow в Pimcore и как использовать ранее созданные пользовательские layouts. Как и многие другие сервисы, workflow в Pimcore должны быть определены в файле config.yaml проекта Pimcore либо в аналогичном файле конкретного бандла. В этом разделе вы узнаете, как корректно сконфигурировать workflow. В следующем фрагменте кода показано, как инициализировать конфигурацию workflow:

pimcore:  
workflows:
product_workflow:
label: 'Product Workflow'

type: 'state_machine'

supports:
- 'Pimcore\Model\DataObject\Product'

marking_store:
type: single_state
arguments:
- marking

Как видно из приведенного выше примера, для инициализации workflow необходимо добавить ключевое слово workflows под ключом pimcore. Затем нужно задать уникальный идентификатор workflow — в нашем примере это product_workflow. На следующем уровне можно определить ряд параметров, которые описаны ниже:

  1. label: заголовок workflow.
  2. type: тип workflow — workflow или state_machine, как описано во введении к главе.
  3. supports: один или несколько классов, к которым применяется данный workflow.
  4. marking_store: для workflow типа state_machine можно указать, какой атрибут класса используется для хранения состояния workflow. Если этот атрибут является picklist-атрибутом, можно позволить классу динамически создавать значения picklist, считывая состояния workflow, указав провайдер опций, как показано на следующем скриншоте:

Поставщик параметров состояний рабочего процесса

Как видно на предыдущем скриншоте, вы можете указать имя класса провайдера опций и задать имя workflow в качестве данных для провайдера опций. Класс провайдера опций автоматически считает состояния workflow, которые вы можете сконфигурировать с помощью ключевого слова places, как показано в следующем фрагменте:

pimcore:  
workflows:
product_workflow:
places:
base_data:
label: 'Base Data'
color: '#ffd700'
permissions:
- objectLayout: basedata

Как видно из приведенного выше примера, под ключевым словом places можно добавить одно или несколько состояний. Для каждого состояния необходимо задать уникальный идентификатор (ID), а также определить label и color, которые будут использоваться для визуального выделения метки состояния. Если вы хотите ограничить отображаемые атрибуты класса для конкретного состояния, можно указать ранее созданный пользовательский layout с помощью ключевого слова objectLayout внутри блока permissions.

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

pimcore:  
workflows:
product_workflow:
transitions:
product_images:
from: [ translations, enrichment ]
to: images
guard: subject.checkTranslationsCompleted()
options:
label: 'Edit Images'
notes:
commentEnabled: true
commentRequired: false
iconClass: 'pimcore_icon_image'

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

Вызывая переходы рабочего процесса

Как вы можете видеть на предыдущем скриншоте, сгруппированная кнопка автоматически вводится в редактор объектов, чтобы вы выбрали доступные переходы.

При необходимости вы также можете определить guard-функцию, которая автоматически вызывается, когда workflow находится в одном из состояний, указанных в from. Если guard-функция возвращает отрицательный результат, применить переход и перейти в целевое состояние будет невозможно.

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

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

Примечания о переходе

Как видно на предыдущем скриншоте, когда пользователь нажимает на действие перехода, открывается модальное окно, в котором он может ввести комментарий. Этот комментарий будет сохранен в разделе Notes & Events внутри объекта, после чего пользователь нажимает кнопку Perform Action для завершения перехода.

В итоге, в этом разделе вы узнали, как настраивать workflow в Pimcore. С помощью пользовательских layout’ов можно пошагово принуждать пользователей заполнять информацию о продукте, обеспечивая полноту данных. В частности, вы научились определять состояния (places) workflow и настраивать переходы (transitions) между ними.

Резюме

В этом руководстве вы изучили концепцию Product Information Management (PIM) на примере определения сущности Product и рассмотрения практических примеров. В первой части главы вы познакомились с тем, что такое PIM и каковы его основные возможности. Затем вы узнали, как создавать и настраивать класс Product в соответствии с вашими требованиями.

После определения класса Product вы научились включать наследование для класса и определять варианты продуктов для уже существующего продукта. На практических примерах кода вы также узнали, как получать варианты в листингах и как создавать новые варианты продуктов. Далее в этом руководстве вы познакомились с концепцией product bundling и узнали, как добавлять новые поля в класс Product, чтобы можно было определять бандл-продукты. Затем вы узнали, как создать обработчик событий, который слушает события, инициируемые объектами, например событие сохранения продукта.

В частности, вы рассмотрели пример кода, показывающий, как вычислять цену бандл-продукта. После этого вы узнали, как использовать Objectbricks для расширения класса Product путем добавления групп атрибутов, полезных для описания конкретных концепций. Это особенно полезно, если требуется представлять разные типы продуктов без создания отдельного класса для каждого типа, чтобы избежать дублирования общих атрибутов.

В последнем разделе вы узнали, как настраивать пользовательские layout’ы для классов Pimcore и как использовать их в конфигурациях workflow Pimcore. На конкретном примере вы научились настраивать workflow для пошагового заполнения информации о продукте.

Узнать больше

Этот материал является примером главы из книги Modernizing Enterprise CMS Using Pimcore (Daniele Fontani, Marco Giudicci, Francesco Minà), опубликованной издательством Packt Publishing (2021). Полную версию книги можно приобрести здесь


Вы можете предложить улучшение документации или задать вопрос в комментариях.
Если вам нужна полноценная консультация — вы можете заказать её на нашем сайте.