<commit_before>How to add a custom catalog promotion scope?
============================================

Adding a new, custom catalog promotion scope to your shop may become a quite helpful extension to your own Catalog Promotions.
You can imagine for instance, that you have some custom way of aggregating products, or any other method of filtering them.
These products that will fulfill your specific scope will become eligible for actions of Catalog Promotion, and as we know
cheaper Products attract more customers.
Let's try implementing the new **Catalog Promotion Scope** in this cookbook, that will work with Products that contains a phrase.

.. note::

    If you are familiar with **Cart Promotions** and you know how **Cart Promotion Rules** work,
    then the Catalog Promotion Scope should look familiar, as the concept of them is quite similar.

Create a new catalog promotion scope
------------------------------------

We should start from creating a provider that will return for us all of eligible product variants. Let's declare the service:

.. code-block:: yaml

    # config/services.yaml

    App\Checker\InByPhraseScopeVariantsChecker:
        arguments:
            - '@sylius.repository.product_variant'
        tags:
            - { name: 'sylius.catalog_promotion.variant_checker', type: 'for_phrase' }

.. note::

    Please take a note on tag of Checker, thanks to it those services are working properly.

And the code for the checker itself:

.. code-block:: php

    <?php

    namespace App\Checker;

    use Sylius\Component\Core\Repository\ProductVariantRepositoryInterface;
    use Webmozart\Assert\Assert;
    use Sylius\Component\Promotion\Model\CatalogPromotionScopeInterface;

    class InByPhraseScopeVariantsChecker implements VariantInScopeCheckerInterface
    {
        public const TYPE = 'for_phrase';

        private ProductVariantRepositoryInterface $productVariantRepository;

        public function __construct(ProductVariantRepositoryInterface $productVariantRepository)
        {
            $this->productVariantRepository = $productVariantRepository;
        }

        public function inScope(CatalogPromotionScopeInterface $scope, ProductVariantInterface $productVariant): bool
        {
            $configuration = $scope->getConfiguration();
            Assert::keyExists($configuration, 'phrase', 'This rule should have configured phrase');

            return in_array(
                $productVariant,
                $this->productVariantRepository->findByPhrase($configuration['phrase'], 'en_US'),
                true
            );
        }
    }

.. note::

    In this example there is hardcoded locale in ``->findByPhrase($configuration['amount'], 'en_US')`` but you can use LocaleContextInterface
    or extend the code from this cookbook to e.g. consume key ``localeCode`` from configuration.

Now the Catalog Promotion should work with your new Scope for programmatically and API created resource.

Prepare a custom validator for the new scope
--------------------------------------------

We can start with configuration, declare our basic validator for this particular scope:

.. code-block:: yaml

    # config/services.yaml

    App\Validator\CatalogPromotionScope\ByPhraseScopeValidator:
        tags:
            - { name: 'sylius.catalog_promotion.scope_validator', key: 'by_phrase' }
<commit_msg>minor #13596 [Cookbook] Minor fixes in custom catalog promotion scope (GSadee)<commit_after>How to add a custom catalog promotion scope?
============================================

Adding a new, custom catalog promotion scope to your shop may become a quite helpful extension to your own Catalog Promotions.
You can imagine for instance, that you have some custom way of aggregating products, or any other method of filtering them.
These products that will fulfill your specific scope will become eligible for actions of Catalog Promotion, and as we know
cheaper Products attract more customers.
Let's try implementing the new **Catalog Promotion Scope** in this cookbook, that will work with Products that contains a phrase.

.. note::

    If you are familiar with **Cart Promotions** and you know how **Cart Promotion Rules** work,
    then the Catalog Promotion Scope should look familiar, as the concept of them is quite similar.

Create a new catalog promotion scope
------------------------------------

We should start from creating a provider that will return for us all of eligible product variants. Let's declare the service:

.. code-block:: yaml

    # config/services.yaml

    App\Checker\InByPhraseScopeVariantsChecker:
        arguments:
            - '@sylius.repository.product_variant'
        tags:
            - { name: 'sylius.catalog_promotion.variant_checker', type: 'by_phrase' }

.. note::

    Please take a note on tag of Checker, thanks to it those services are working properly.

And the code for the checker itself:

.. code-block:: php

    <?php

    namespace App\Checker;

    use Sylius\Component\Core\Repository\ProductVariantRepositoryInterface;
    use Webmozart\Assert\Assert;
    use Sylius\Component\Promotion\Model\CatalogPromotionScopeInterface;

    class InByPhraseScopeVariantsChecker implements VariantInScopeCheckerInterface
    {
        public const TYPE = 'by_phrase';

        private ProductVariantRepositoryInterface $productVariantRepository;

        public function __construct(ProductVariantRepositoryInterface $productVariantRepository)
        {
            $this->productVariantRepository = $productVariantRepository;
        }

        public function inScope(CatalogPromotionScopeInterface $scope, ProductVariantInterface $productVariant): bool
        {
            $configuration = $scope->getConfiguration();
            Assert::keyExists($configuration, 'phrase', 'This scope should have configured phrase');

            return str_contains($productVariant->getName(), $configuration['phrase']);
        }
    }

Now the Catalog Promotion should work with your new Scope for programmatically and API created resource.

Prepare a custom validator for the new scope
--------------------------------------------

We can start with configuration, declare our basic validator for this particular scope:

.. code-block:: yaml

    # config/services.yaml

    App\Validator\CatalogPromotionScope\ByPhraseScopeValidator:
        tags:
            - { name: 'sylius.catalog_promotion.scope_validator', key: 'by_phrase' }

{'fraction_non_alphanumeric': 0.10163985148514851, 'fraction_numerical': 0.0007735148514851486, 'mean_word_length': 3.2449113591595533, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2494107', 'n_tokens_mistral': 1561, 'n_tokens_neox': 1502, 'n_words': 609}