/**
  * {@inheritdoc}
  */
 public function addAttributeFilter(AbstractAttribute $attribute, $operator, $value)
 {
     $joinAlias = 'filter' . $attribute->getCode() . $this->aliasCounter++;
     $joinAliasOpt = 'filterO' . $attribute->getCode() . $this->aliasCounter;
     $backendField = sprintf('%s.%s', $joinAliasOpt, 'id');
     if (in_array('empty', $value)) {
         $this->qb->leftJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $this->prepareAttributeJoinCondition($attribute, $joinAlias));
         $condition = $this->prepareEmptyCondition($backendField, $operator, $value);
         $this->qb->leftJoin($joinAlias . '.' . $attribute->getBackendType(), $joinAliasOpt)->andWhere($condition);
     } else {
         $this->qb->innerJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $this->prepareAttributeJoinCondition($attribute, $joinAlias))->innerJoin($joinAlias . '.' . $attribute->getBackendType(), $joinAliasOpt, 'WITH', $this->qb->expr()->in($backendField, $value));
     }
     return $this;
 }
 /**
  * {@inheritdoc}
  */
 protected function findEntity($class, array $data)
 {
     if (!$this->identifierAttribute) {
         throw new MissingIdentifierException();
     }
     return $this->productManager->getProductRepository()->findByReference($data[$this->identifierAttribute->getCode()]);
 }
 /**
  * {@inheritdoc}
  */
 public function addAttributeFilter(AbstractAttribute $attribute, $operator, $value)
 {
     $backendType = $attribute->getBackendType();
     $joinAlias = 'filter' . $attribute->getCode() . $this->aliasCounter++;
     // join to value
     $condition = $this->prepareAttributeJoinCondition($attribute, $joinAlias);
     if ('EMPTY' === $operator) {
         $this->qb->leftJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $condition);
         // join to price
         $joinAliasPrice = 'filterP' . $attribute->getCode() . $this->aliasCounter;
         $priceData = $joinAlias . '.' . $backendType;
         $this->qb->leftJoin($priceData, $joinAliasPrice);
         // add conditions
         $condition = $this->preparePriceCondition($joinAliasPrice, $operator, $value);
         $exprNull = $this->qb->expr()->isNull($joinAliasPrice . '.id');
         $exprOr = $this->qb->expr()->orX($condition, $exprNull);
         $this->qb->andWhere($exprOr);
     } else {
         $this->qb->innerJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $condition);
         $joinAliasPrice = 'filterP' . $attribute->getCode() . $this->aliasCounter;
         $condition = $this->preparePriceCondition($joinAliasPrice, $operator, $value);
         $this->qb->innerJoin($joinAlias . '.' . $backendType, $joinAliasPrice, 'WITH', $condition);
     }
     return $this;
 }
 /**
  * Set attribute
  *
  * @param AbstractAttribute $attribute
  *
  * @return AbstractProductValue
  * @throws LogicException
  */
 public function setAttribute(AbstractAttribute $attribute = null)
 {
     if (is_object($this->attribute) && $attribute != $this->attribute) {
         throw new \LogicException(sprintf('An attribute (%s) has already been set for this value', $this->attribute->getCode()));
     }
     $this->attribute = $attribute;
     return $this;
 }
 function it_normalizes_family($normalizer, Family $family, AbstractAttribute $sku)
 {
     $sku->getCode()->willReturn('sku');
     $family->getCode()->willReturn('mongo');
     $family->getAttributeAsLabel()->willReturn($sku);
     $normalizer->normalize($family, 'mongodb_json', [])->willReturn(['label' => 'translations']);
     $this->normalize($family, 'mongodb_json', [])->shouldReturn(['code' => 'mongo', 'label' => 'translations', 'attributeAsLabel' => 'sku']);
 }
 function let(SerializerInterface $serializer, AbstractAttribute $simpleAttribute)
 {
     $serializer->implement('Symfony\\Component\\Serializer\\Normalizer\\NormalizerInterface');
     $this->setSerializer($serializer);
     $simpleAttribute->isLocalizable()->willReturn(false);
     $simpleAttribute->isScopable()->willReturn(false);
     $simpleAttribute->getCode()->willReturn('simple');
 }
 function it_adds_a_order_by_on_an_attribute_value_in_the_query(Builder $queryBuilder, AbstractAttribute $sku)
 {
     $sku->getCode()->willReturn('sku');
     $sku->isLocalizable()->willReturn(false);
     $sku->isScopable()->willReturn(false);
     $queryBuilder->sort('normalizedData.sku', 'desc')->willReturn($queryBuilder);
     $this->addAttributeSorter($sku, 'desc');
 }
 function it_adds_a_less_than_or_equals_filter_in_the_query(Builder $queryBuilder, AbstractAttribute $price)
 {
     $price->getCode()->willReturn('price');
     $price->isLocalizable()->willReturn(true);
     $price->isScopable()->willReturn(true);
     $queryBuilder->field('normalizedData.price-en_US-mobile.EUR.data')->willReturn($queryBuilder);
     $queryBuilder->lte(22.5)->willReturn($queryBuilder);
     $this->addAttributeFilter($price, '<=', '22.5 EUR');
 }
 function it_adds_a_less_than_or_equals_filter_in_the_query(Builder $queryBuilder, AbstractAttribute $metric)
 {
     $metric->getCode()->willReturn('weight');
     $metric->isLocalizable()->willReturn(true);
     $metric->isScopable()->willReturn(true);
     $queryBuilder->field('normalizedData.weight-en_US-mobile.baseData')->willReturn($queryBuilder);
     $queryBuilder->lte(22.5)->willReturn($queryBuilder);
     $this->addAttributeFilter($metric, '<=', '22.5');
 }
 function it_adds_a_like_filter_on_an_attribute_value_in_the_query(Builder $queryBuilder, AbstractAttribute $sku)
 {
     $sku->getCode()->willReturn('sku');
     $sku->isLocalizable()->willReturn(false);
     $sku->isScopable()->willReturn(false);
     $queryBuilder->field('normalizedData.sku')->willReturn($queryBuilder);
     $queryBuilder->equals('my-sku')->willReturn($queryBuilder);
     $this->addAttributeFilter($sku, 'LIKE', 'my-sku');
 }
 function it_removes_attribute_indexes_when_a_localizable_and_scopable_attribute_is_removed($collection, AbstractAttribute $title)
 {
     $title->getCode()->willReturn('title');
     $collection->getIndexInfo()->willReturn([["key" => ["_id" => 1]], ["key" => ["normalizedData.title-ecommerce-en_US" => 1]], ["key" => ["normalizedData.title-ecommerce-de_DE" => 1]], ["key" => ["normalizedData.title-mobile-de_DE" => 1]], ["key" => ["normalizedData.manufacturer_title" => 1]], ["key" => ["normalizedData.title_left-en_US" => 1]], ["key" => ["normalizedData.title_left-de_DE" => 1]]]);
     $collection->deleteIndex('normalizedData.title-ecommerce-en_US')->shouldBeCalled();
     $collection->deleteIndex('normalizedData.title-ecommerce-de_DE')->shouldBeCalled();
     $collection->deleteIndex('normalizedData.title-mobile-de_DE')->shouldBeCalled();
     $this->purgeIndexesFromAttribute($title);
 }
 function it_adds_a_in_filter_on_an_attribute_value_in_the_query($qb, AbstractAttribute $color)
 {
     $color->getCode()->willReturn('color');
     $color->isLocalizable()->willReturn(true);
     $color->isScopable()->willReturn(false);
     $qb->field('normalizedData.color-en_US.id')->willReturn($qb);
     $qb->in([1, 2])->willReturn($qb);
     $this->addAttributeFilter($color, 'IN', [1, 2]);
 }
 function it_generates_a_query_to_update_product_scopable_attributes($namingUtility, AbstractAttribute $label, Channel $mobile)
 {
     $namingUtility->getScopableAttributes(false)->willReturn([$label]);
     $label->getCode()->willReturn('label');
     $namingUtility->getLocaleCodes()->willReturn(['fr_FR', 'en_US']);
     $namingUtility->appendSuffixes(['normalizedData.label'], ['fr_FR', 'en_US'])->willReturn(['normalizedData.label-fr_FR', 'normalizedData.label-en_US']);
     $namingUtility->appendSuffixes(['normalizedData.label-fr_FR', 'normalizedData.label-en_US'], ['mobile'])->willReturn(['normalizedData.label-fr_FR-mobile', 'normalizedData.label-en_US-mobile']);
     $mobile->getCode()->willReturn('mobile');
     $this->generateQuery($mobile, '', '', '')->shouldReturn([[['normalizedData.label-fr_FR-mobile' => ['$exists' => true]], ['$unset' => ['normalizedData.label-fr_FR-mobile' => '']], ['multiple' => true]], [['normalizedData.label-en_US-mobile' => ['$exists' => true]], ['$unset' => ['normalizedData.label-en_US-mobile' => '']], ['multiple' => true]]]);
 }
 function it_returns_attribute_informations_from_field_name_with_price_attribute($managerRegistry, AttributeRepository $repository, AbstractAttribute $attribute)
 {
     $attribute->getCode()->willReturn('foo');
     $attribute->isLocalizable()->willReturn(false);
     $attribute->isScopable()->willReturn(false);
     $attribute->getBackendType()->willReturn('prices');
     $repository->findByReference('foo')->willReturn($attribute);
     $managerRegistry->getRepository(self::ATTRIBUTE_CLASS)->willReturn($repository);
     $this->extractAttributeFieldNameInfos('foo-USD')->shouldReturn(['attribute' => $attribute, 'locale_code' => null, 'scope_code' => null, 'price_currency' => 'USD']);
 }
 function it_normalizes_value_with_decimal_support_backend(ProductValueInterface $value, AbstractAttribute $attribute)
 {
     $attribute->getCode()->willReturn('code');
     $attribute->getBackendType()->willReturn('decimal');
     $attribute->isLocalizable()->willReturn(false);
     $attribute->isScopable()->willReturn(false);
     $value->getData()->willReturn('42.42');
     $value->getAttribute()->willReturn($attribute);
     $this->normalize($value, 'mongodb_json', [])->shouldReturn(['code' => 42.42]);
 }
 /**
  * Clean options.
  *
  * @param array             $options
  * @param AbstractAttribute $attribute
  *
  * @throws InvalidItemException If clean doesn't goes well
  */
 protected function cleanOptions(array $options, AbstractAttribute $attribute = null)
 {
     foreach ($options as $optionLabel => $optionValue) {
         if ($attribute !== null && !in_array($attribute->getCode(), $this->getIgnoredAttributes()) && $this->getOption($optionLabel, $attribute) === null) {
             try {
                 $this->handleOptionNotInPimAnymore($optionValue, $attribute->getCode());
             } catch (RestCallException $e) {
                 throw new InvalidItemException($e->getMessage(), [$optionLabel]);
             }
         }
     }
 }
 function it_adds_a_not_between_filter_on_an_attribute_value_in_the_query($queryBuilder, AbstractAttribute $date)
 {
     $date->getCode()->willReturn('release_date');
     $date->isLocalizable()->willReturn(true);
     $date->isScopable()->willReturn(true);
     $queryBuilder->expr()->willReturn($queryBuilder);
     $queryBuilder->addAnd($queryBuilder)->willReturn($queryBuilder);
     $queryBuilder->addOr($queryBuilder)->willReturn($queryBuilder);
     $queryBuilder->addOr($queryBuilder)->willReturn($queryBuilder);
     $queryBuilder->lte(strtotime('2014-03-15'))->willReturn($queryBuilder);
     $queryBuilder->gte(strtotime('2014-03-20 23:59:59'))->willReturn($queryBuilder);
     $this->addAttributeFilter($date, ['from' => '<', 'to' => '>'], ['from' => '2014-03-15', 'to' => '2014-03-20']);
 }
 function it_adds_a_like_filter_in_the_query(QueryBuilder $queryBuilder, AbstractAttribute $sku)
 {
     $sku->getId()->willReturn(42);
     $sku->getCode()->willReturn('sku');
     $sku->getBackendType()->willReturn('varchar');
     $sku->isLocalizable()->willReturn(false);
     $sku->isScopable()->willReturn(false);
     $queryBuilder->expr()->willReturn(new Expr());
     $queryBuilder->getRootAlias()->willReturn('p');
     $condition = "filtersku1.attribute = 42 AND filtersku1.varchar LIKE 'My Sku'";
     $queryBuilder->innerJoin('p.values', 'filtersku1', 'WITH', $condition)->shouldBeCalled();
     $this->addAttributeFilter($sku, 'LIKE', 'My Sku');
 }
 /**
  * {@inheritdoc}
  */
 public function addAttributeSorter(AbstractAttribute $attribute, $direction)
 {
     $aliasPrefix = 'sorter';
     $joinAlias = $aliasPrefix . 'V' . $attribute->getCode() . $this->aliasCounter++;
     $backendType = $attribute->getBackendType();
     // join to value
     $condition = $this->prepareAttributeJoinCondition($attribute, $joinAlias);
     $this->qb->leftJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $condition);
     $joinAliasMetric = $aliasPrefix . 'M' . $attribute->getCode() . $this->aliasCounter;
     $this->qb->leftJoin($joinAlias . '.' . $backendType, $joinAliasMetric);
     $this->qb->addOrderBy($joinAliasMetric . '.baseData', $direction);
     return $this;
 }
Пример #20
0
 /**
  * {@inheritdoc}
  */
 public function addAttributeFilter(AbstractAttribute $attribute, $operator, $value)
 {
     $joinAlias = 'filter' . $attribute->getCode() . $this->aliasCounter++;
     $backendField = sprintf('%s.%s', $joinAlias, $attribute->getBackendType());
     if ($operator === 'EMPTY') {
         $this->qb->leftJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $this->prepareAttributeJoinCondition($attribute, $joinAlias));
         $this->qb->andWhere($this->prepareCriteriaCondition($backendField, $operator, $value));
     } else {
         $condition = $this->prepareAttributeJoinCondition($attribute, $joinAlias);
         $condition .= ' AND ' . $this->prepareCriteriaCondition($backendField, $operator, $value);
         $this->qb->innerJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $condition);
     }
     return $this;
 }
 /**
  * {@inheritdoc}
  */
 public function addAttributeSorter(AbstractAttribute $attribute, $direction)
 {
     $aliasPrefix = 'sorter';
     $joinAlias = $aliasPrefix . 'V' . $attribute->getCode() . $this->aliasCounter++;
     $backendType = $attribute->getBackendType();
     // join to value
     $condition = $this->prepareAttributeJoinCondition($attribute, $joinAlias);
     $this->qb->leftJoin($this->qb->getRootAlias() . '.' . $attribute->getBackendStorage(), $joinAlias, 'WITH', $condition);
     // then to option and option value to sort on
     $joinAliasOpt = $aliasPrefix . 'O' . $attribute->getCode() . $this->aliasCounter;
     $this->qb->leftJoin($joinAlias . '.' . $backendType, $joinAliasOpt);
     $this->qb->addOrderBy($joinAliasOpt . '.code', $direction);
     return $this;
 }
Пример #22
0
 function it_normalizes_a_family_and_its_attributes(Family $family, Collection $attributes, \Iterator $iterator, AbstractAttribute $attribute1)
 {
     $family->getCode()->willReturn('bar');
     $family->getAttributes()->willReturn($attributes);
     $attribute1->getCode()->willReturn('att1');
     $attributes->getIterator()->willReturn($iterator);
     $iterator->rewind()->willReturn($attribute1);
     $iterator->current()->willReturn($attribute1);
     $iterator->next()->willReturn($attribute1);
     $valueCount = 1;
     $iterator->valid()->will(function () use(&$valueCount) {
         return $valueCount-- > 0;
     });
     $this->normalize($family, 'json')->shouldReturn(['code' => 'bar', 'attributes' => ['att1']]);
 }
 function it_adds_a_sorter_in_the_query(QueryBuilder $queryBuilder, AbstractAttribute $sku)
 {
     $sku->getId()->willReturn(42);
     $sku->getCode()->willReturn('sku');
     $sku->getBackendType()->willReturn('varchar');
     $sku->isLocalizable()->willReturn(false);
     $sku->isScopable()->willReturn(false);
     $queryBuilder->expr()->willReturn(new Expr());
     $queryBuilder->getRootAlias()->willReturn('p');
     $queryBuilder->getDQLPart('join')->willReturn([]);
     $queryBuilder->resetDQLPart('join')->shouldBeCalled();
     $condition = "sorterVsku1.attribute = 42";
     $queryBuilder->leftJoin('p.values', 'sorterVsku1', 'WITH', $condition)->shouldBeCalled();
     $queryBuilder->addOrderBy('sorterVsku1.varchar', 'DESC')->shouldBeCalled();
     $this->addAttributeSorter($sku, 'DESC');
 }
Пример #24
0
 /**
  * {@inheritdoc}
  */
 public function addAttributeSorter(AbstractAttribute $attribute, $direction)
 {
     $aliasPrefix = 'sorter';
     $joinAlias = $aliasPrefix . 'V' . $attribute->getCode() . $this->aliasCounter++;
     $backendType = $attribute->getBackendType();
     // join to value and sort on
     $condition = $this->prepareAttributeJoinCondition($attribute, $joinAlias);
     // Remove current join in order to put the orderBy related join
     // at first place in the join queue for performances reasons
     $joinsSet = $this->qb->getDQLPart('join');
     $this->qb->resetDQLPart('join');
     $this->qb->leftJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $condition);
     $this->qb->addOrderBy($joinAlias . '.' . $backendType, $direction);
     // Reapply previous join after the orderBy related join
     $this->applyJoins($joinsSet);
     return $this;
 }
 /**
  * {@inheritdoc}
  */
 public function addAttributeSorter(AbstractAttribute $attribute, $direction)
 {
     $aliasPrefix = 'sorter';
     $joinAlias = $aliasPrefix . 'V' . $attribute->getCode() . $this->aliasCounter++;
     $backendType = $attribute->getBackendType();
     // join to value
     $condition = $this->prepareAttributeJoinCondition($attribute, $joinAlias);
     $this->qb->leftJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $condition);
     // then to option and option value to sort on
     $joinAliasOpt = $aliasPrefix . 'O' . $attribute->getCode() . $this->aliasCounter;
     $condition = $joinAliasOpt . ".attribute = " . $attribute->getId();
     $this->qb->leftJoin($joinAlias . '.' . $backendType, $joinAliasOpt, 'WITH', $condition);
     $joinAliasOptVal = $aliasPrefix . 'OV' . $attribute->getCode() . $this->aliasCounter;
     $condition = $joinAliasOptVal . '.locale = ' . $this->qb->expr()->literal($this->context->getLocaleCode());
     $this->qb->leftJoin($joinAliasOpt . '.optionValues', $joinAliasOptVal, 'WITH', $condition);
     $this->qb->addOrderBy($joinAliasOpt . '.code', $direction);
     $this->qb->addOrderBy($joinAliasOptVal . '.value', $direction);
     return $this;
 }
 function it_sends_attribute_with_group_and_family_to_create_on_magento_webservice($webservice, AbstractAttribute $attribute, AttributeMappingManager $attributeMappingManager, AttributeGroupMappingManager $attributeGroupMappingManager, FamilyMappingManager $familyMappingManager, AttributeGroup $group, Family $family, $magentoMappingMerger, MappingCollection $mapping)
 {
     $attributes = [[$attribute, ['create' => ['attributeName' => 'attribute_code']]]];
     $this->setMagentoUrl(null);
     $this->setWsdlUrl('/api/soap/?wsdl');
     $attribute->getCode()->willReturn('attributeName');
     $attribute->getFamilies()->willReturn([$family]);
     $attribute->getGroup()->willReturn($group);
     $magentoMappingMerger->getMapping()->willReturn($mapping);
     $mapping->getTarget($attribute)->willReturn(12);
     $mapping->getSource(12)->willReturn(12);
     $group->getCode()->willReturn('group_name');
     $familyMappingManager->getIdFromFamily(Argument::any(), '/api/soap/?wsdl')->willReturn(414);
     $webservice->addAttributeGroupToAttributeSet(414, 'group_name')->shouldBeCalled()->willReturn(797);
     $webservice->createAttribute(Argument::any())->willReturn(12);
     $webservice->addAttributeToAttributeSet(12, 414, 797)->shouldBeCalled();
     $attributeGroupMappingManager->registerGroupMapping($group, $family, 797, '/api/soap/?wsdl')->shouldBeCalled();
     $attributeGroupMappingManager->getIdFromGroup($group, $family, '/api/soap/?wsdl')->willReturn(797);
     $attributeMappingManager->registerAttributeMapping($attribute, 12, '/api/soap/?wsdl')->shouldBeCalled();
     $this->write($attributes);
 }
 /**
  * {@inheritdoc}
  */
 public function addAttributeFilter(AbstractAttribute $attribute, $operator, $value)
 {
     $joinAlias = 'filter' . $attribute->getCode() . $this->aliasCounter++;
     // prepare join value condition
     $optionAlias = $joinAlias . '.option';
     if (in_array('empty', $value)) {
         unset($value[array_search('empty', $value)]);
         $expr = $this->qb->expr()->isNull($optionAlias);
         if (count($value) > 0) {
             $exprIn = $this->qb->expr()->in($optionAlias, $value);
             $expr = $this->qb->expr()->orX($expr, $exprIn);
         }
         $this->qb->leftJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $this->prepareAttributeJoinCondition($attribute, $joinAlias));
         $this->qb->andWhere($expr);
     } else {
         // inner join to value
         $condition = $this->prepareAttributeJoinCondition($attribute, $joinAlias);
         $condition .= ' AND ( ' . $this->qb->expr()->in($optionAlias, $value) . ' ) ';
         $this->qb->innerJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $condition);
     }
     return $this;
 }
 /**
  * Get a random option from an attribute
  *
  * @param AbstractAttribute $attribute
  *
  * @return AttributeOption
  */
 protected function getRandomOptionFromAttribute(AbstractAttribute $attribute)
 {
     if (!isset($this->attributeOptions[$attribute->getCode()])) {
         $this->attributeOptions[$attribute->getCode()] = [];
         foreach ($attribute->getOptions() as $option) {
             $this->attributeOptions[$attribute->getCode()][] = $option;
         }
     }
     return $this->faker->randomElement($this->attributeOptions[$attribute->getCode()]);
 }
 /**
  * Verify if the magento attribute id is null else add the attribute to the attribute set.
  *
  * @param integer           $magentoAttributeId
  * @param AbstractAttribute $pimAttribute
  */
 protected function manageAttributeSet($magentoAttributeId, $pimAttribute)
 {
     if ($this->attributeIdMappingMerger->getMapping()->getSource($magentoAttributeId) != $pimAttribute->getCode()) {
         $this->addAttributeToAttributeSet($magentoAttributeId, $pimAttribute);
     }
 }
 function it_normalizes_product_with_a_multiselect_value($filter, $serializer, ProductInterface $product, AbstractAttribute $skuAttribute, AbstractAttribute $colorsAttribute, AbstractProductValue $sku, AbstractProductValue $colors, AttributeOption $red, AttributeOption $blue, Collection $values, Family $family)
 {
     $family->getCode()->willReturn('shoes');
     $skuAttribute->getCode()->willReturn('sku');
     $skuAttribute->getAttributeType()->willReturn('pim_catalog_identifier');
     $skuAttribute->isLocalizable()->willReturn(false);
     $skuAttribute->isScopable()->willReturn(false);
     $sku->getAttribute()->willReturn($skuAttribute);
     $sku->getData()->willReturn('sku-001');
     $colorsAttribute->getCode()->willReturn('colors');
     $colorsAttribute->isLocalizable()->willReturn(false);
     $colorsAttribute->isScopable()->willReturn(false);
     $colors->getAttribute()->willReturn($colorsAttribute);
     $colors->getData()->willReturn([$red, $blue]);
     $product->getIdentifier()->willReturn($sku);
     $product->getFamily()->willReturn($family);
     $product->isEnabled()->willReturn(true);
     $product->getGroupCodes()->willReturn('');
     $product->getCategoryCodes()->willReturn('');
     $product->getAssociations()->willReturn([]);
     $product->getValues()->willReturn($values);
     $filter->filter($values, ['identifier' => $sku, 'scopeCode' => null, 'localeCodes' => []])->willReturn([$sku, $colors]);
     $serializer->normalize($sku, 'flat', Argument::any())->willReturn(['sku' => 'sku-001']);
     $serializer->normalize($colors, 'flat', Argument::any())->willReturn(['colors' => 'red, blue']);
     $this->normalize($product, 'flat', [])->shouldReturn(['sku' => 'sku-001', 'family' => 'shoes', 'groups' => '', 'categories' => '', 'colors' => 'red, blue', 'enabled' => 1]);
 }