acquia_commercemanager-8.x-1.122/modules/acm_sku/src/SKUFieldsManager.php
modules/acm_sku/src/SKUFieldsManager.php
<?php namespace Drupal\acm_sku; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Psr\Log\LoggerInterface; /** * Class SKUFieldsManager. * * @package Drupal\acm_sku */ class SKUFieldsManager { const BASE_FIELD_ADDITIONS_CONFIG = 'acm_sku.base_field_additions'; /** * The Config Factory service. * * @var \Drupal\Core\Config\ConfigFactoryInterface */ private $configFactory; /** * The Module Handler service. * * @var \Drupal\Core\Extension\ModuleHandlerInterface */ private $moduleHandler; /** * The Entity Type Manager service. * * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ private $entityTypeManager; /** * The Entity Definition Update Manager service. * * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface */ private $entityDefinitionUpdateManager; /** * The logger. * * @var \Psr\Log\LoggerInterface */ private $logger; /** * SKUFieldsManager constructor. * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The Config Factory service. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The Module Handler service. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The Entity Type Manager service. * @param \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface $entity_definition_update_manager * The Entity Definition Update Manager service. * @param \Psr\Log\LoggerInterface $logger * The Logger. */ public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, EntityTypeManagerInterface $entity_type_manager, EntityDefinitionUpdateManagerInterface $entity_definition_update_manager, LoggerInterface $logger) { $this->configFactory = $config_factory; $this->moduleHandler = $module_handler; $this->entityTypeManager = $entity_type_manager; $this->entityDefinitionUpdateManager = $entity_definition_update_manager; $this->logger = $logger; } /** * Function to add all new field definitions from custom modules to SKU Base. */ public function addFields() { $this->logger->info('addFields() invoked to add newly added base fields to SKU.'); // Get all the additional fields from all custom modules. $fields = $this->getAllCustomFields(); // Store the fields in config. $config = $this->configFactory->getEditable(self::BASE_FIELD_ADDITIONS_CONFIG); $existing_fields = $config->getRawData(); $fields = array_diff_key($fields, $existing_fields); $existing_fields = array_merge($existing_fields, $fields); $config->setData($existing_fields)->save(); if ($fields) { $this->logger->info('Adding new fields %fields.', [ '%fields' => json_encode($fields), ]); // Apply entity updates, we will read from config and add/update fields. $this->entityDefinitionUpdateManager->applyUpdates(); // Allow other modules to take some action after the fields are added. $this->moduleHandler->invokeAll('acm_sku_base_fields_updated', [$fields, 'add']); } else { $this->logger->warning('No new fields found to add.'); } } /** * Remove base field from SKU entity. * * Note: Calling function needs to take care of clearing data. * * @param string $field_code * Field code to remove. */ public function removeField($field_code) { $config = $this->configFactory->getEditable(self::BASE_FIELD_ADDITIONS_CONFIG); $fields = $config->getRawData(); if (!isset($fields[$field_code])) { return; } $field = $fields[$field_code]; unset($fields[$field_code]); $config->setData($fields)->save(); $this->entityTypeManager->clearCachedDefinitions(); $this->entityDefinitionUpdateManager->applyUpdates(); $fields_removed = [ $field_code => $field, ]; $this->moduleHandler->invokeAll('acm_sku_base_fields_updated', [$fields_removed, 'remove']); } /** * Function to update field definitions for the additional SKU base fields. * * This will not update the actual field but only additional information used * in custom code like field is configurable or not, indexable or not. * * It will not do anything except updating the config. Be very careful when * using this. * * @param string $field_code * Field code. * @param array $field * Field definition. * * @throws \Exception * Throws exception if field doesn't exist in config. */ public function updateFieldMetaInfo($field_code, array $field) { $config = $this->configFactory->getEditable(self::BASE_FIELD_ADDITIONS_CONFIG); $existing_fields = $config->getRawData(); if (empty($existing_fields[$field_code])) { throw new \Exception('Field not available, try adding it first.'); } // Checks to avoid errors. $field_structure_info = [ 'type', 'cardinality', ]; foreach ($field_structure_info as $info) { if (isset($field[$info]) && $field['type'] != $existing_fields[$field_code]['type']) { throw new \Exception('Can not modify field structure.'); } } // Need to apply entity updates for following. $apply_updates = FALSE; $field_labels_info = [ 'label', 'description', 'visible_view', 'visible_form', 'weight', ]; foreach ($field_labels_info as $info) { if (isset($field[$info]) && $field['type'] != $existing_fields[$field_code]['type']) { $apply_updates = TRUE; break; } } $existing_fields[$field_code] = array_replace($existing_fields[$field_code], $field); $config->setData($existing_fields)->save(); if ($apply_updates) { $this->entityDefinitionUpdateManager->applyUpdates(); } } /** * Get all existing field additions. * * @return array * Existing field additions. */ public function getFieldAdditions() { return $this->configFactory->get(self::BASE_FIELD_ADDITIONS_CONFIG)->getRawData(); } /** * Get all fields defined in custom modules. * * @return array * All fields defined in custom modules. */ private function getAllCustomFields() { $fields = []; $this->moduleHandler->alter('acm_sku_base_field_additions', $fields); foreach ($fields as $field_code => $field) { $fields[$field_code] = $this->applyDefaults($field_code, $field); } return $fields; } /** * Function to apply defaults and complete field definition. * * @param string $field_code * Field code. * @param array $field * Field definition. * * @return array * Field definition with all defaults applied. */ private function applyDefaults($field_code, array $field) { $defaults = $this->getDefaults(); if (empty($field['source'])) { $field['source'] = $field_code; } // We will always have label, still we do a check to avoid errors. if (empty($field['label'])) { $field['label'] = $field_code; } // Add description if empty. if (empty($field['description'])) { $field['description'] = str_replace('[label]', $field['label'], $defaults['description']); } // Merge all other defaults. $field += $defaults; return $field; } /** * Returns an associative [] containing all required fields with defaults set. * * @returns array * Associative array containing all required values with defaults set. */ private function getDefaults() { return [ // (Required) Label to be used for admin forms and display. 'label' => '', // Soruce field code to use for reading from product data. 'source' => '', // Description of the field to be used in admin forms. 'description' => '[label] attribute for the product.', // (Required) Parent key in the array where to look for data. 'parent' => 'attributes', // Type of the field. 'type' => 'attribute', // Number of values allowed to be stored. 'cardinality' => 1, // (Optional) Should the data be stored as serialized. 'serialize' => 0, // Whether the field is part of configurable options. 'configurable' => 0, // Default weight of the field in form and display. 'weight' => NULL, // Whether the field should be visible while viewing content. 'visible_view' => 0, // Whether the field should be visible in form. 'visible_form' => 1, ]; } }