/** * consume. * * @param array $options Parameters sent to the processor */ public function consume(array $options = array()) { if (null !== $this->logger) { $this->logger->debug(sprintf('Start consuming queue %s.', $this->messageProvider->getQueueName())); } $this->optionsResolver->setDefaults(array('poll_interval' => 50000)); if ($this->processor instanceof ConfigurableInterface) { $this->processor->setDefaultOptions($this->optionsResolver); } $options = $this->optionsResolver->resolve($options); if ($this->processor instanceof InitializableInterface) { $this->processor->initialize($options); } while (true) { while (null !== ($message = $this->messageProvider->get())) { if (false === $this->processor->process($message, $options)) { break 2; } } if ($this->processor instanceof SleepyInterface) { if (false === $this->processor->sleep($options)) { break; } } usleep($options['poll_interval']); } if ($this->processor instanceof TerminableInterface) { $this->processor->terminate($options); } }
/** * {@inheritdoc} */ public function addAttributeFilter(AttributeInterface $attribute, $operator, $value, $locale = null, $scope = null, $options = []) { try { $options = $this->resolver->resolve($options); } catch (\Exception $e) { throw InvalidArgumentException::expectedFromPreviousException($e, $attribute->getCode(), 'filter', 'option'); } $this->checkLocaleAndScope($attribute, $locale, $scope, 'option'); $field = $options['field']; if (Operators::IS_EMPTY !== $operator) { $this->checkValue($field, $value); } $joinAlias = $this->getUniqueAlias('filter' . $attribute->getCode(), true); // prepare join value condition $optionAlias = $joinAlias . '.option'; if (Operators::IS_EMPTY === $operator) { $this->qb->leftJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $this->prepareAttributeJoinCondition($attribute, $joinAlias, $locale, $scope)); $this->qb->andWhere($this->qb->expr()->isNull($optionAlias)); } else { // inner join to value $condition = $this->prepareAttributeJoinCondition($attribute, $joinAlias, $locale, $scope); if (FieldFilterHelper::getProperty($field) === FieldFilterHelper::CODE_PROPERTY) { $value = $this->objectIdResolver->getIdsFromCodes('option', $value); } $condition .= ' AND ( ' . $this->qb->expr()->in($optionAlias, $value) . ' ) '; $this->qb->innerJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $condition); } return $this; }
/** * @return array */ public function resolve(array $options) { $this->resolver = $this->optionsResolverFactory->create(); $this->setDefaults(); $this->setNormalizers(); return $this->resolver->resolve($options); }
/** * {@inheritdoc} */ public function create(array $options = []) { $options = $this->optionsResolver->resolve($options); /** @var PromotionInterface $promotion */ $promotion = $this->promotionFactory->createNew(); $promotion->setCode($options['code']); $promotion->setName($options['name']); $promotion->setDescription($options['description']); $promotion->setCouponBased($options['coupon_based']); $promotion->setUsageLimit($options['usage_limit']); $promotion->setExclusive($options['exclusive']); $promotion->setPriority($options['priority']); if (isset($options['starts_at'])) { $promotion->setStartsAt(new \DateTime($options['starts_at'])); } if (isset($options['ends_at'])) { $promotion->setEndsAt(new \DateTime($options['ends_at'])); } foreach ($options['channels'] as $channel) { $promotion->addChannel($channel); } foreach ($options['rules'] as $rule) { /** @var PromotionRuleInterface $promotionRule */ $promotionRule = $this->promotionRuleExampleFactory->create($rule); $promotion->addRule($promotionRule); } foreach ($options['actions'] as $action) { /** @var PromotionActionInterface $promotionAction */ $promotionAction = $this->promotionActionExampleFactory->create($action); $promotion->addAction($promotionAction); } return $promotion; }
/** * {@inheritdoc} */ public function addAttributeFilter(AttributeInterface $attribute, $operator, $value, $locale = null, $scope = null, $options = []) { try { $options = $this->resolver->resolve($options); } catch (\Exception $e) { throw InvalidArgumentException::expectedFromPreviousException($e, $attribute->getCode(), 'filter', 'string'); } $this->checkLocaleAndScope($attribute, $locale, $scope, 'string'); if (Operators::IS_EMPTY !== $operator && Operators::IS_NOT_EMPTY !== $operator) { $this->checkValue($options['field'], $value); } $joinAlias = $this->getUniqueAlias('filter' . $attribute->getCode()); $backendField = sprintf('%s.%s', $joinAlias, $attribute->getBackendType()); if (Operators::IS_EMPTY === $operator) { $this->qb->leftJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $this->prepareAttributeJoinCondition($attribute, $joinAlias, $locale, $scope)); $this->qb->andWhere($this->prepareCriteriaCondition($backendField, $operator, $value)); } else { $condition = $this->prepareAttributeJoinCondition($attribute, $joinAlias, $locale, $scope); if (Operators::IS_NOT_EMPTY === $operator) { $condition .= sprintf('AND (%s AND %s)', $this->qb->expr()->isNotNull($backendField), $this->qb->expr()->neq($backendField, $this->qb->expr()->literal(''))); $this->qb->innerJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $condition); } elseif (Operators::DOES_NOT_CONTAIN === $operator) { $whereCondition = $this->prepareCondition($backendField, $operator, $value) . ' OR ' . $this->prepareCondition($backendField, Operators::IS_NULL, null); $this->qb->leftJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $condition); $this->qb->andWhere($whereCondition); } else { $condition .= ' AND ' . $this->prepareCondition($backendField, $operator, $value); $this->qb->innerJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $condition); } } return $this; }
/** * {@inheritdoc} */ public function addAttributeFilter(AttributeInterface $attribute, $operator, $value, $locale = null, $scope = null, $options = []) { try { $options = $this->resolver->resolve($options); } catch (\Exception $e) { throw InvalidArgumentException::expectedFromPreviousException($e, $attribute->getCode(), 'filter', 'options'); } $this->checkLocaleAndScope($attribute, $locale, $scope, 'options'); if ($operator != Operators::IS_EMPTY) { $this->checkValue($options['field'], $value); } $joinAlias = $this->getUniqueAlias('filter' . $attribute->getCode()); $joinAliasOpt = $this->getUniqueAlias('filterO' . $attribute->getCode()); $backendField = sprintf('%s.%s', $joinAliasOpt, 'id'); if (Operators::IS_EMPTY === $operator) { $this->qb->leftJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $this->prepareAttributeJoinCondition($attribute, $joinAlias, $locale, $scope)); $this->qb->leftJoin($joinAlias . '.' . $attribute->getBackendType(), $joinAliasOpt)->andWhere($this->qb->expr()->isNull($backendField)); } else { if (FieldFilterHelper::getProperty($options['field']) === FieldFilterHelper::CODE_PROPERTY) { $value = $this->objectIdResolver->getIdsFromCodes('option', $value); } $this->qb->innerJoin($this->qb->getRootAlias() . '.values', $joinAlias, 'WITH', $this->prepareAttributeJoinCondition($attribute, $joinAlias, $locale, $scope))->innerJoin($joinAlias . '.' . $attribute->getBackendType(), $joinAliasOpt, 'WITH', $this->qb->expr()->in($backendField, $value)); } return $this; }
/** * {@inheritdoc} */ public function create(array $options = []) { $options = $this->optionsResolver->resolve($options); /** @var ChannelInterface $channel */ $channel = $this->channelFactory->createNamed($options['name']); $channel->setCode($options['code']); $channel->setHostname($options['hostname']); $channel->setEnabled($options['enabled']); $channel->setColor($options['color']); $channel->setTaxCalculationStrategy($options['tax_calculation_strategy']); $channel->setThemeName($options['theme_name']); $channel->setDefaultLocale($options['default_locale']); foreach ($options['locales'] as $locale) { $channel->addLocale($locale); } $channel->setDefaultCurrency($options['default_currency']); foreach ($options['currencies'] as $currency) { $channel->addCurrency($currency); } foreach ($options['payment_methods'] as $paymentMethod) { $channel->addPaymentMethod($paymentMethod); } foreach ($options['shipping_methods'] as $shippingMethod) { $channel->addShippingMethod($shippingMethod); } return $channel; }
/** * @param string $commandName * @param array $commandOptions * * @return Definition */ private function generateCommandDefinition($commandName, array $commandOptions) { $commandOptions = $this->optionsResolver->resolve($commandOptions); $definition = new Definition('PhpZone\\Docker\\Console\\Command\\DockerComposeCommand'); $definition->setArguments(array($commandName, $commandOptions, new Reference('phpzone.docker.script_builder.docker_compose'))); $definition->addTag('command'); return $definition; }
/** * @param string $commandName * @param array $commandOptions * * @return Definition * * @throws MissingOptionsException * @throws InvalidOptionsException */ private function generateCommandDefinition($commandName, array $commandOptions) { $resolvedCommandOptions = $this->optionsResolver->resolve($commandOptions); $definition = new Definition('PhpZone\\Shell\\Console\\Command\\BatchScriptCommand'); $definition->setArguments(array($commandName, $resolvedCommandOptions)); $definition->addTag('command'); return $definition; }
/** * {@inheritdoc} */ public function resolveFilters(array $filters) { $result = array(); foreach ($filters as $field => $filter) { $result[$field] = $this->filtersResolver->resolve($filter); } return $result; }
/** * {@inheritdoc} */ public function create(array $options = []) { $options = $this->optionsResolver->resolve($options); /** @var PromotionRuleInterface $promotionRule */ $promotionRule = $this->promotionRuleFactory->createNew(); $promotionRule->setType($options['type']); $promotionRule->setConfiguration($options['configuration']); return $promotionRule; }
/** * @param array $options * string $code code to print * string $type type of barcode * string $format output format * int $width Minimum width of a single bar in user units. * int $height Height of barcode in user units. * string $color Foreground color (in SVG format) for bar elements (background is transparent). * * @return mixed */ public function generate($options = array()) { $options = $this->resolver->resolve($options); if (Type::getDimension($options['type']) == '2D') { return call_user_func_array(array($this->dns2d, $this->formatFunctionMap[$options['format']]), array($options['code'], $options['type'], $options['width'], $options['height'], $options['color'])); } else { return call_user_func_array(array($this->dns1d, $this->formatFunctionMap[$options['format']]), array($options['code'], $options['type'], $options['width'], $options['height'], $options['color'])); } }
/** * {@inheritdoc} */ public function create(array $options = []) { $options = $this->optionsResolver->resolve($options); /** @var ProductAssociationTypeInterface $productAssociationType */ $productAssociationType = $this->productAssociationTypeFactory->createNew(); $productAssociationType->setName($options['name']); $productAssociationType->setCode($options['code']); return $productAssociationType; }
/** * {@inheritdoc} */ public function create(array $options = []) { $options = $this->optionsResolver->resolve($options); /** @var CustomerGroupInterface $customerGroup */ $customerGroup = $this->customerGroupFactory->createNew(); $customerGroup->setCode($options['code']); $customerGroup->setName($options['name']); return $customerGroup; }
/** * {@inheritdoc} */ public function create(array $options = []) { $options = $this->optionsResolver->resolve($options); /** @var Route $route */ $route = $this->routeFactory->createNew(); $route->setName($options['name']); $route->setContent($options['content']); return $route; }
/** * {@inheritdoc} */ public function create(array $options = []) { $options = $this->optionsResolver->resolve($options); /** @var TaxCategoryInterface $taxCategory */ $taxCategory = $this->taxCategoryFactory->createNew(); $taxCategory->setCode($options['code']); $taxCategory->setName($options['name']); $taxCategory->setDescription($options['description']); return $taxCategory; }
/** * {@inheritdoc} */ public function createFromArray(array $themeData) { /** @var ThemeInterface $theme */ $theme = new $this->themeClassName(); $themeData = $this->optionsResolver->resolve($themeData); foreach ($themeData as $attributeKey => $attributeValue) { $this->propertyAccessor->setValue($theme, $this->normalizeAttributeKey($attributeKey), $attributeValue); } return $theme; }
public function relativeDate(\DateTimeInterface $date, \DateTimeInterface $baseDate = null, array $options = []) { $options = $this->optionsResolver->resolve($options); if (!isset($this->calculators[$options['calculator']])) { throw new \InvalidArgumentException(sprintf('Undefined date diff calculator "%s"', $options['calculator'])); } $calculator = $this->calculators[$options['calculator']]; $this->formatter->setDateFormat($options['date_format']); return $this->formatter->format($calculator->compute($date, $baseDate)); }
/** * {@inheritdoc} */ public function create(array $options = []) { $options = $this->optionsResolver->resolve($options); /** @var StaticContent $staticContent */ $staticContent = $this->staticContentFactory->createNew(); $staticContent->setTitle($options['title']); $staticContent->setName($options['name']); $staticContent->setBody($options['body']); $staticContent->setPublishable($options['publishable']); return $staticContent; }
/** * {@inheritdoc} */ public function addBuilder(BuilderInterface $builder, $parameters = []) { if (!$this->query && !(array_key_exists('bool_type', $parameters) && !empty($parameters['bool_type']))) { $this->setBuilder($builder); } else { $parameters = $this->resolver->resolve(array_filter($parameters)); $this->isBool() ?: $this->convertToBool(); $this->query->add($builder, $parameters['bool_type']); } return $this; }
/** * {@inheritdoc} */ public function load(array $options) { $options = $this->optionsResolver->resolve($options); $this->taxonFixture->load(['custom' => [['code' => 'category', 'name' => 'Category', 'children' => [['code' => 'stickers', 'name' => 'Stickers']]]]]); $this->productAttributeFixture->load(['custom' => [['name' => 'Sticker paper', 'code' => 'sticker_paper', 'type' => TextAttributeType::TYPE], ['name' => 'Sticker resolution', 'code' => 'sticker_resolution', 'type' => TextAttributeType::TYPE]]]); $this->productOptionFixture->load(['custom' => [['name' => 'Sticker size', 'code' => 'sticker_size', 'values' => ['sticker_size-3' => '3"', 'sticker_size_5' => '5"', 'sticker_size_7' => '7"']]]]); $products = []; for ($i = 0; $i < $options['amount']; ++$i) { $products[] = ['name' => sprintf('Sticker "%s"', $this->faker->word), 'code' => $this->faker->uuid, 'main_taxon' => 'stickers', 'taxons' => ['stickers'], 'product_attributes' => ['sticker_paper' => sprintf('Paper from tree %s', $this->faker->randomElement(['Wung', 'Tanajno', 'Lemon-San', 'Me-Gusta'])), 'sticker_resolution' => $this->faker->randomElement(['JKM XD', '476DPI', 'FULL HD', '200DPI'])], 'images' => ['main' => sprintf('%s/../Resources/fixtures/%s', __DIR__, 'stickers.jpg'), 'thumbnail' => sprintf('%s/../Resources/fixtures/%s', __DIR__, 'stickers.jpg')]]; } $this->productFixture->load(['custom' => $products]); }
/** * {@inheritdoc} */ public function load(array $options) { $options = $this->optionsResolver->resolve($options); $this->taxonFixture->load(['custom' => [['code' => 'category', 'name' => 'Category', 'children' => [['code' => 'books', 'name' => 'Books']]]]]); $this->productAttributeFixture->load(['custom' => [['name' => 'Book author', 'code' => 'BOOK-AUTHOR', 'type' => TextAttributeType::TYPE], ['name' => 'Book ISBN', 'code' => 'BOOK-ISBN', 'type' => TextAttributeType::TYPE], ['name' => 'Book pages', 'code' => 'BOOK-PAGES', 'type' => IntegerAttributeType::TYPE]]]); $products = []; for ($i = 0; $i < $options['amount']; ++$i) { $name = $this->faker->name; $products[] = ['name' => sprintf('Book "%s" by %s', $this->faker->word, $name), 'code' => $this->faker->uuid, 'main_taxon' => 'books', 'taxons' => ['books'], 'product_attributes' => ['BOOK-AUTHOR' => $name, 'BOOK-ISBN' => $this->faker->isbn13, 'BOOK-PAGES' => $this->faker->numberBetween(42, 1024)], 'images' => [sprintf('%s/../Resources/fixtures/%s', __DIR__, 'books.jpg')]]; } $this->productFixture->load(['custom' => $products]); }
/** * {@inheritdoc} */ public function filterValue($value) { $return = array(); $actions = $this->getOption('actions'); foreach ($actions as $name => $options) { $options = $this->actionOptionsResolver->resolve((array) $options); $return[$name] = array(); $parameters = array(); $urlAttributes = $options['url_attr']; $content = $options['content']; if (isset($options['parameters_field_mapping'])) { foreach ($options['parameters_field_mapping'] as $parameterName => $mappingField) { if ($mappingField instanceof \Closure) { $parameters[$parameterName] = $mappingField($value, $this->getIndex()); } else { $parameters[$parameterName] = $value[$mappingField]; } } } if (isset($options['additional_parameters'])) { foreach ($options['additional_parameters'] as $parameterValueName => $parameterValue) { $parameters[$parameterValueName] = $parameterValue; } } if ($options['redirect_uri'] !== false) { if (is_string($options['redirect_uri'])) { $parameters['redirect_uri'] = $options['redirect_uri']; } if ($options['redirect_uri'] === true) { $parameters['redirect_uri'] = $this->container->get('request')->getRequestUri(); } } if ($urlAttributes instanceof \Closure) { $urlAttributes = $urlAttributes($value, $this->getIndex()); if (!is_array($urlAttributes)) { throw new UnexpectedTypeException('url_attr option Clousure must return new array with url attributes.'); } } $url = $this->router->generate($options['route_name'], $parameters, $options['absolute']); if (!isset($urlAttributes['href'])) { $urlAttributes['href'] = $url; } if (isset($content) && $content instanceof \Closure) { $content = (string) $content($value, $this->getIndex()); } // $return[$name]['url'] is deprecated since 1.0 and will be removed in version 1.2 $return[$name]['url'] = $url; $return[$name]['content'] = isset($content) ? $content : $name; $return[$name]['field_mapping_values'] = $value; $return[$name]['url_attr'] = $urlAttributes; } return $return; }
/** * {@inheritdoc} */ public function load(array $options) { $options = $this->optionsResolver->resolve($options); $this->taxonFixture->load(['custom' => [['code' => 'category', 'name' => 'Category', 'children' => [['code' => 'mugs', 'name' => 'Mugs']]]]]); $this->productAttributeFixture->load(['custom' => [['name' => 'Mug material', 'code' => 'mug_material', 'type' => TextAttributeType::TYPE]]]); $this->productOptionFixture->load(['custom' => [['name' => 'Mug type', 'code' => 'mug_type', 'values' => ['mug_type_medium' => 'Medium mug', 'mug_type_double' => 'Double mug', 'mug_type_monster' => 'Monster mug']]]]); $products = []; for ($i = 0; $i < $options['amount']; ++$i) { $products[] = ['name' => sprintf('Mug "%s"', $this->faker->word), 'code' => $this->faker->uuid, 'main_taxon' => 'mugs', 'taxons' => ['mugs'], 'product_attributes' => ['mug_material' => $this->faker->randomElement(['Invisible porcelain', 'Banana skin', 'Porcelain', 'Centipede'])], 'images' => [sprintf('%s/../Resources/fixtures/%s', __DIR__, 'mugs.jpg')]]; } $this->productFixture->load(['custom' => $products]); }
/** * {@inheritdoc} */ public function create(array $options = []) { $options = $this->optionsResolver->resolve($options); /** @var ProductAssociationInterface $productAssociation */ $productAssociation = $this->productAssociationFactory->createNew(); $productAssociation->setType($options['type']); $productAssociation->setOwner($options['owner']); foreach ($options['associated_products'] as $associatedProduct) { $productAssociation->addAssociatedProduct($associatedProduct); } return $productAssociation; }
/** * {@inheritdoc} */ public function create(array $options = []) { $options = $this->optionsResolver->resolve($options); /** @var ReviewInterface $productReview */ $productReview = $this->productReviewFactory->createForSubjectWithReviewer($options['product'], $options['author']); $productReview->setTitle($options['title']); $productReview->setComment($options['comment']); $productReview->setRating($options['rating']); $options['product']->addReview($productReview); $this->applyReviewTransition($productReview, $options['status'] ? $options['status'] : $this->getRandomStatus()); return $productReview; }
/** * Check and add user to list * * @param array|UserInterface $user */ public function add($user) { if (!$user instanceof UserInterface) { $user = $this->resolver->resolve($user); $user = new User($user['username'], $user['password'], isset($user['roles']) ? $user['roles'] : [], isset($user['salt']) ? $user['salt'] : null); } $username = strtolower($user->getUsername()); if (array_key_exists($username, $this->users)) { throw new \LogicException('Another user with the same username already exists.'); } $this->users[$username] = $user; }
/** * {@inheritdoc} */ public function load(array $options) { $options = $this->optionsResolver->resolve($options); $this->productAssociationTypeFixture->load(['custom' => [['code' => 'similar_products', 'name' => 'Similar products']]]); $products = $this->productRepository->findAll(); $products = $this->faker->randomElements($products, $options['amount']); $productAssociations = []; /** @var ProductInterface $product */ foreach ($products as $product) { $productAssociations[] = ['type' => 'similar_products', 'owner' => $product->getCode(), 'associated_products' => $this->getAssociatedProductsAsArray($product)]; } $this->productAssociationFixture->load(['custom' => $productAssociations]); }
/** * {@inheritdoc} */ public function create(array $options = []) { $options = $this->optionsResolver->resolve($options); /** @var ProductOptionInterface $productAttribute */ $productAttribute = $this->productAttributeFactory->createTyped($options['type']); $productAttribute->setCode($options['code']); foreach ($this->getLocales() as $localeCode) { $productAttribute->setCurrentLocale($localeCode); $productAttribute->setFallbackLocale($localeCode); $productAttribute->setName($options['name']); } return $productAttribute; }
/** * {@inheritdoc} */ public function load(array $options) { $options = $this->optionsResolver->resolve($options); $this->taxonFixture->load(['custom' => [['code' => 'category', 'name' => 'Category', 'children' => [['code' => 't_shirts', 'name' => 'T-Shirts', 'children' => [['code' => 'mens_t_shirts', 'name' => 'Men'], ['code' => 'womens_t_shirts', 'name' => 'Women']]]]]]]); $this->productAttributeFixture->load(['custom' => [['name' => 'T-Shirt brand', 'code' => 't_shirt_brand', 'type' => TextAttributeType::TYPE], ['name' => 'T-Shirt collection', 'code' => 't_shirt_collection', 'type' => TextAttributeType::TYPE], ['name' => 'T-Shirt material', 'code' => 't_shirt_material', 'type' => TextAttributeType::TYPE]]]); $this->productOptionFixture->load(['custom' => [['name' => 'T-Shirt color', 'code' => 't_shirt_color', 'values' => ['t_shirt_color_red' => 'Red', 't_shirt_color_black' => 'Black', 't_shirt_color_white' => 'White']], ['name' => 'T-Shirt size', 'code' => 't_shirt_size', 'values' => ['t_shirt_size_s' => 'S', 't_shirt_size_m' => 'M', 't_shirt_size_l' => 'L', 't_shirt_size_xl' => 'XL', 't_shirt_size_xxl' => 'XXL']]]]); $products = []; for ($i = 0; $i < $options['amount']; ++$i) { $categoryTaxonCode = $this->faker->randomElement(['mens_t_shirts', 'womens_t_shirts']); $products[] = ['name' => sprintf('T-Shirt "%s"', $this->faker->word), 'code' => $this->faker->uuid, 'main_taxon' => $categoryTaxonCode, 'taxons' => [$categoryTaxonCode], 'product_attributes' => ['t_shirt_brand' => $this->faker->randomElement(['Nike', 'Adidas', 'JKM-476 Streetwear', 'Potato', 'Centipede Wear']), 't_shirt_collection' => sprintf('Sylius %s %s', $this->faker->randomElement(['Summer', 'Winter', 'Spring', 'Autumn']), mt_rand(1995, 2012)), 't_shirt_material' => $this->faker->randomElement(['Centipede', 'Wool', 'Centipede 10% / Wool 90%', 'Potato 100%'])], 'product_options' => ['t_shirt_color', 't_shirt_size'], 'images' => ['main' => sprintf('%s/../Resources/fixtures/%s', __DIR__, 't-shirts.jpg'), 'thumbnail' => sprintf('%s/../Resources/fixtures/%s', __DIR__, 't-shirts.jpg')]]; } $this->productFixture->load(['custom' => $products]); }