123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Swatches\Model\Plugin;
- use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
- use Magento\Framework\App\ObjectManager;
- use Magento\Framework\Exception\InputException;
- use Magento\Framework\Serialize\Serializer\Json;
- use Magento\Swatches\Model\ResourceModel\Swatch as SwatchResource;
- use Magento\Swatches\Model\Swatch;
- /**
- * Plugin model for Catalog Resource Attribute
- */
- class EavAttribute
- {
- const DEFAULT_STORE_ID = 0;
- /**
- * @var SwatchResource
- */
- private $swatchResource;
- /**
- * Base option title used for string operations to detect is option already exists or new
- */
- const BASE_OPTION_TITLE = 'option';
- /**
- * @var \Magento\Swatches\Model\ResourceModel\Swatch\CollectionFactory
- */
- protected $swatchCollectionFactory;
- /**
- * @var \Magento\Swatches\Model\SwatchFactory
- */
- protected $swatchFactory;
- /**
- * @var \Magento\Swatches\Helper\Data
- */
- protected $swatchHelper;
- /**
- * Array which contains links for new created attributes for swatches
- *
- * @var array
- */
- protected $dependencyArray = [];
- /**
- * Swatch existing status
- *
- * @var bool
- */
- protected $isSwatchExists;
- /**
- * Serializer from arrays to string.
- *
- * @var Json
- */
- private $serializer;
- /**
- * @param \Magento\Swatches\Model\ResourceModel\Swatch\CollectionFactory $collectionFactory
- * @param \Magento\Swatches\Model\SwatchFactory $swatchFactory
- * @param \Magento\Swatches\Helper\Data $swatchHelper
- * @param Json|null $serializer
- * @param SwatchResource|null $swatchResource
- */
- public function __construct(
- \Magento\Swatches\Model\ResourceModel\Swatch\CollectionFactory $collectionFactory,
- \Magento\Swatches\Model\SwatchFactory $swatchFactory,
- \Magento\Swatches\Helper\Data $swatchHelper,
- Json $serializer = null,
- SwatchResource $swatchResource = null
- ) {
- $this->swatchCollectionFactory = $collectionFactory;
- $this->swatchFactory = $swatchFactory;
- $this->swatchHelper = $swatchHelper;
- $this->serializer = $serializer ?: ObjectManager::getInstance()->create(Json::class);
- $this->swatchResource = $swatchResource ?: ObjectManager::getInstance()->create(SwatchResource::class);
- }
- /**
- * Set base data to Attribute
- *
- * @param Attribute $attribute
- * @return void
- */
- public function beforeBeforeSave(Attribute $attribute)
- {
- if ($this->swatchHelper->isSwatchAttribute($attribute)) {
- $this->setProperOptionsArray($attribute);
- $this->validateOptions($attribute);
- $this->swatchHelper->assembleAdditionalDataEavAttribute($attribute);
- }
- $this->convertSwatchToDropdown($attribute);
- }
- /**
- * Swatch save operations
- *
- * @param Attribute $attribute
- * @throws \Magento\Framework\Exception\LocalizedException
- * @return void
- */
- public function afterAfterSave(Attribute $attribute)
- {
- if ($this->swatchHelper->isSwatchAttribute($attribute)) {
- $this->processSwatchOptions($attribute);
- $this->saveDefaultSwatchOptionValue($attribute);
- $this->saveSwatchParams($attribute);
- }
- }
- /**
- * Substitute suitable options and swatches arrays
- *
- * @param Attribute $attribute
- * @return void
- */
- protected function setProperOptionsArray(Attribute $attribute)
- {
- $canReplace = false;
- if ($this->swatchHelper->isVisualSwatch($attribute)) {
- $canReplace = true;
- $defaultValue = $attribute->getData('defaultvisual');
- $optionsArray = $attribute->getData('optionvisual');
- $swatchesArray = $attribute->getData('swatchvisual');
- } elseif ($this->swatchHelper->isTextSwatch($attribute)) {
- $canReplace = true;
- $defaultValue = $attribute->getData('defaulttext');
- $optionsArray = $attribute->getData('optiontext');
- $swatchesArray = $attribute->getData('swatchtext');
- }
- if ($canReplace == true) {
- if (!empty($optionsArray)) {
- $attribute->setData('option', $optionsArray);
- }
- if (!empty($defaultValue)) {
- $attribute->setData('default', $defaultValue);
- } else {
- $attribute->setData('default', [0 => $attribute->getDefaultValue()]);
- }
- if (!empty($swatchesArray)) {
- $attribute->setData('swatch', $swatchesArray);
- }
- }
- }
- /**
- * Prepare attribute for conversion from any swatch type to dropdown
- *
- * @param Attribute $attribute
- * @throws \Magento\Framework\Exception\LocalizedException
- * @return void
- */
- protected function convertSwatchToDropdown(Attribute $attribute)
- {
- if ($attribute->getData(Swatch::SWATCH_INPUT_TYPE_KEY) == Swatch::SWATCH_INPUT_TYPE_DROPDOWN) {
- $additionalData = $attribute->getData('additional_data');
- if (!empty($additionalData)) {
- $additionalData = $this->serializer->unserialize($additionalData);
- if (is_array($additionalData) && isset($additionalData[Swatch::SWATCH_INPUT_TYPE_KEY])) {
- $option = $attribute->getOption() ?: [];
- $this->cleanEavAttributeOptionSwatchValues($option);
- unset($additionalData[Swatch::SWATCH_INPUT_TYPE_KEY]);
- $attribute->setData('additional_data', $this->serializer->serialize($additionalData));
- }
- }
- }
- }
- /**
- * Creates array which link new option ids
- *
- * @param Attribute $attribute
- * @return Attribute
- * @throws \Magento\Framework\Exception\LocalizedException
- */
- protected function processSwatchOptions(Attribute $attribute)
- {
- $optionsArray = $attribute->getData('option');
- if (!empty($optionsArray) && is_array($optionsArray)) {
- $optionsArray = $this->prepareOptionIds($optionsArray);
- $attributeSavedOptions = $attribute->getSource()->getAllOptions();
- $this->prepareOptionLinks($optionsArray, $attributeSavedOptions);
- }
- return $attribute;
- }
- /**
- * Get options array without deleted items
- *
- * @param array $optionsArray
- * @return array
- */
- protected function prepareOptionIds(array $optionsArray)
- {
- if (isset($optionsArray['value']) && is_array($optionsArray['value'])) {
- foreach (array_keys($optionsArray['value']) as $optionId) {
- if (isset($optionsArray['delete']) && isset($optionsArray['delete'][$optionId])
- && $optionsArray['delete'][$optionId] == 1
- ) {
- unset($optionsArray['value'][$optionId]);
- }
- }
- }
- return $optionsArray;
- }
- /**
- * Create links for non existed swatch options
- *
- * @param array $optionsArray
- * @param array $attributeSavedOptions
- * @return void
- */
- protected function prepareOptionLinks(array $optionsArray, array $attributeSavedOptions)
- {
- $dependencyArray = [];
- if (is_array($optionsArray['value'])) {
- $optionCounter = 1;
- foreach (array_keys($optionsArray['value']) as $baseOptionId) {
- $dependencyArray[$baseOptionId] = $attributeSavedOptions[$optionCounter]['value'];
- $optionCounter++;
- }
- }
- $this->dependencyArray = $dependencyArray;
- }
- /**
- * Save all Swatches data
- *
- * @param Attribute $attribute
- * @return void
- */
- protected function saveSwatchParams(Attribute $attribute)
- {
- if ($this->swatchHelper->isVisualSwatch($attribute)) {
- $this->processVisualSwatch($attribute);
- $attributeOptions = $attribute->getOptiontext() ?: [];
- $this->cleanTextSwatchValuesAfterSwitch($attributeOptions);
- } elseif ($this->swatchHelper->isTextSwatch($attribute)) {
- $this->processTextualSwatch($attribute);
- }
- }
- /**
- * Save Visual Swatch data
- *
- * @param Attribute $attribute
- * @return void
- */
- protected function processVisualSwatch(Attribute $attribute)
- {
- $swatchArray = $attribute->getData('swatch/value');
- if (isset($swatchArray) && is_array($swatchArray)) {
- foreach ($swatchArray as $optionId => $value) {
- $optionId = $this->getAttributeOptionId($optionId);
- $isOptionForDelete = $this->isOptionForDelete($attribute, $optionId);
- if ($optionId === null || $isOptionForDelete) {
- //option was deleted by button with basket
- continue;
- }
- $swatch = $this->loadSwatchIfExists($optionId, self::DEFAULT_STORE_ID);
- $swatchType = $this->determineSwatchType($value);
- $this->saveSwatchData($swatch, $optionId, self::DEFAULT_STORE_ID, $swatchType, $value);
- $this->isSwatchExists = null;
- }
- }
- }
- /**
- * Clean swatch option values after switching to the dropdown type.
- *
- * @param array $attributeOptions
- * @param int|null $swatchType
- * @throws \Magento\Framework\Exception\LocalizedException
- */
- private function cleanEavAttributeOptionSwatchValues(array $attributeOptions, int $swatchType = null)
- {
- if (count($attributeOptions) && isset($attributeOptions['value'])) {
- $optionsIDs = array_keys($attributeOptions['value']);
- $this->swatchResource->clearSwatchOptionByOptionIdAndType($optionsIDs, $swatchType);
- }
- }
- /**
- * Cleaning the text type of swatch option values after switching.
- *
- * @param array $attributeOptions
- * @throws \Magento\Framework\Exception\LocalizedException
- */
- private function cleanTextSwatchValuesAfterSwitch(array $attributeOptions)
- {
- $this->cleanEavAttributeOptionSwatchValues($attributeOptions, Swatch::SWATCH_TYPE_TEXTUAL);
- }
- /**
- * @param string $value
- * @return int
- */
- private function determineSwatchType($value)
- {
- $swatchType = Swatch::SWATCH_TYPE_EMPTY;
- if (!empty($value) && $value[0] == '#') {
- $swatchType = Swatch::SWATCH_TYPE_VISUAL_COLOR;
- } elseif (!empty($value) && $value[0] == '/') {
- $swatchType = Swatch::SWATCH_TYPE_VISUAL_IMAGE;
- }
- return $swatchType;
- }
- /**
- * Save Textual Swatch data
- *
- * @param Attribute $attribute
- * @return void
- */
- protected function processTextualSwatch(Attribute $attribute)
- {
- $swatchArray = $attribute->getData('swatch/value');
- if (isset($swatchArray) && is_array($swatchArray)) {
- foreach ($swatchArray as $optionId => $storeValues) {
- $optionId = $this->getAttributeOptionId($optionId);
- $isOptionForDelete = $this->isOptionForDelete($attribute, $optionId);
- if ($optionId === null || $isOptionForDelete) {
- //option was deleted by button with basket
- continue;
- }
- $defaultSwatchValue = reset($storeValues);
- foreach ($storeValues as $storeId => $value) {
- if (!$value) {
- $value = $defaultSwatchValue;
- }
- $swatch = $this->loadSwatchIfExists($optionId, $storeId);
- $swatch->isDeleted($isOptionForDelete);
- $this->saveSwatchData(
- $swatch,
- $optionId,
- $storeId,
- \Magento\Swatches\Model\Swatch::SWATCH_TYPE_TEXTUAL,
- $value
- );
- $this->isSwatchExists = null;
- }
- }
- }
- }
- /**
- * Get option id. If it not exist get it from dependency link array
- *
- * @param integer $optionId
- * @return int
- */
- protected function getAttributeOptionId($optionId)
- {
- if (substr($optionId, 0, 6) == self::BASE_OPTION_TITLE) {
- $optionId = isset($this->dependencyArray[$optionId]) ? $this->dependencyArray[$optionId] : null;
- }
- return $optionId;
- }
- /**
- * Check if is option for delete
- *
- * @param Attribute $attribute
- * @param integer $optionId
- * @return bool
- */
- protected function isOptionForDelete(Attribute $attribute, $optionId)
- {
- $isOptionForDelete = $attribute->getData('option/delete/' . $optionId);
- return isset($isOptionForDelete) && $isOptionForDelete;
- }
- /**
- * Load swatch if it exists in database
- *
- * @param int $optionId
- * @param int $storeId
- * @return Swatch
- */
- protected function loadSwatchIfExists($optionId, $storeId)
- {
- $collection = $this->swatchCollectionFactory->create();
- $collection->addFieldToFilter('option_id', $optionId);
- $collection->addFieldToFilter('store_id', $storeId);
- $collection->setPageSize(1);
- $loadedSwatch = $collection->getFirstItem();
- if ($loadedSwatch->getId()) {
- $this->isSwatchExists = true;
- }
- return $loadedSwatch;
- }
- /**
- * Save operation
- *
- * @param Swatch $swatch
- * @param integer $optionId
- * @param integer $storeId
- * @param integer $type
- * @param string $value
- * @return void
- */
- protected function saveSwatchData($swatch, $optionId, $storeId, $type, $value)
- {
- if ($this->isSwatchExists) {
- $swatch->setData('type', $type);
- $swatch->setData('value', $value);
- } else {
- $swatch->setData('option_id', $optionId);
- $swatch->setData('store_id', $storeId);
- $swatch->setData('type', $type);
- $swatch->setData('value', $value);
- }
- $swatch->save();
- }
- /**
- * Save default swatch value using Swatch model instead of Eav model
- *
- * @param Attribute $attribute
- * @return void
- */
- protected function saveDefaultSwatchOptionValue(Attribute $attribute)
- {
- if (!$this->swatchHelper->isSwatchAttribute($attribute)) {
- return;
- }
- $defaultValue = $attribute->getData('default/0');
- if (!empty($defaultValue)) {
- /** @var \Magento\Swatches\Model\Swatch $swatch */
- $swatch = $this->swatchFactory->create();
- // created and removed on frontend option not exists in dependency array
- if (substr($defaultValue, 0, 6) == self::BASE_OPTION_TITLE &&
- isset($this->dependencyArray[$defaultValue])
- ) {
- $defaultValue = $this->dependencyArray[$defaultValue];
- }
- $swatch->getResource()->saveDefaultSwatchOption($attribute->getId(), $defaultValue);
- }
- }
- /**
- * Validate that attribute options exist
- *
- * @param Attribute $attribute
- * @return bool
- * @throws InputException
- */
- protected function validateOptions(Attribute $attribute)
- {
- $options = null;
- if ($this->swatchHelper->isVisualSwatch($attribute)) {
- $options = $attribute->getData('optionvisual');
- } elseif ($this->swatchHelper->isTextSwatch($attribute)) {
- $options = $attribute->getData('optiontext');
- }
- if ($options && !$this->isOptionsValid($options, $attribute)) {
- throw new InputException(__('Admin is a required field in each row'));
- }
- return true;
- }
- /**
- * Check if attribute options are valid
- *
- * @param array $options
- * @param Attribute $attribute
- * @return bool
- */
- protected function isOptionsValid(array $options, Attribute $attribute)
- {
- if (!isset($options['value'])) {
- return false;
- }
- foreach ($options['value'] as $optionId => $option) {
- // do not validate options marked as deleted
- if ($this->isOptionForDelete($attribute, $optionId)) {
- continue;
- }
- if (!isset($option[0]) || $option[0] === '') {
- return false;
- }
- }
- return true;
- }
- /**
- * @param Attribute $attribute
- * @param bool $result
- * @return bool
- */
- public function afterUsesSource(Attribute $attribute, $result)
- {
- if ($this->swatchHelper->isSwatchAttribute($attribute)) {
- return true;
- }
- return $result;
- }
- }
|