1

Can someone help to set and get a Symfony (Shopware 6) Subscriber class array from a function? Here is my current Subscriber:

<?php declare(strict_types=1);

namespace CustomFilter\Subscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Content\Product\Events\ProductListingResultEvent;
use Shopware\Core\Content\Product\Events\ProductListingCriteriaEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\Context;
use Symfony\Component\HttpFoundation\Request;
use Shopware\Core\Content\Product\SalesChannel\Listing\Filter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\FilterAggregation;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Metric\MaxAggregation;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter;
use Shopware\Core\Framework\Struct\ArrayEntity;

class Subscriber implements EventSubscriberInterface
{
    private $currentProperty1;

    public function __construct(array $currentProperty1)
    {
        $this->currentProperty1 = $currentProperty1;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            ProductListingResultEvent::class => 'handleResult',
            ProductListingCriteriaEvent::class => 'handleRequest'
        ];
    }

    public function handleResult(ProductListingResultEvent $event)
    {
        $properties = $event->getResult();
        $this->currentProperty1 = $properties->getCurrentFilter('properties');
    }

    public function handleRequest(ProductListingCriteriaEvent $event): void
   {
        $event->getCriteria()->addAssociation('properties');
        $event->getCriteria()->addAssociation('properties.group');

        $currentProperty1 = $this->currentProperty1;

        if (in_array('c0d02d1738fd4293a489695787e06b5c', $currentProperty1)) {
            $friteria = $event->getCriteria();

            $friteria->addFilter(new MultiFilter(
                MultiFilter::CONNECTION_OR,
                    [
                        new ContainsFilter('product.properties.name', 'PropertyNameA'),
                        new ContainsFilter('product.properties.name', 'PropertyNameB')
                    ]
                )
            );
        }
   }
}

I can set and retrieve a private variable via __construct, but I can't set it from the function with $this->:

class Subscriber implements EventSubscriberInterface
{
    private $currentProperty1;

    public function handleRequest(ProductListingCriteriaEvent $event): void
   {
        // Here to get
        $array1 = array ($this->currentProperty1);
   }

    public function handleResult(ProductListingResultEvent $event)
    {
        // Here to set
        $this->currentProperty1 = $currentProperty1;
    }
}

--

If $currentProperty1 in __construct, the URL no longer responds.

If $currentProperty1 is removed from __construct, the URL will work. Does anyone know the problem?

private $propertyGroupRepository;
private array $currentProperty1;

public function __construct(EntityRepositoryInterface $propertyGroupRepository, array $currentProperty1)
{
    $this->propertyGroupRepository = $propertyGroupRepository;
    $this->currentProperty1 = $currentProperty1;
}

I tried to do it like this and can't make it work:

class Subscriber implements EventSubscriberInterface {

    public function setCurrent()
    {
        $array1 = array(1 => 'item 1', 2 => 'item 2');
        return $array1;
    }

    private $currentProperty1;

    public function __construct()
    {
        $this->currentProperty1 = new setCurrent();
    }
}

I can access it though from another function, if use a value like '[]' instead of 'new setCurrent()'. I use $currentValue = array ($this->currentProperty1); in function for this.


Your input regarding dynamically updated array was very helpful and now my code looks like this. From it I hope you can see, that I aim to alter Criteria depending on Current selected property option.

Though I still can't get the array into onListingCriteria

<?php // declare(strict_types=1);

namespace CustomFilterBasedOnSelectedOption\Subscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Content\Product\Events\ProductListingResultEvent;
use Shopware\Core\Content\Product\Events\ProductListingCriteriaEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\Context;
use Symfony\Component\HttpFoundation\Request;
use Shopware\Core\Content\Product\SalesChannel\Listing\Filter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Bucket\FilterAggregation;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Metric\MaxAggregation;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter;
use Shopware\Core\Framework\Struct\ArrayEntity;

class Subscriber implements EventSubscriberInterface
{
    private ?array $currentPropertyOptions = null;

    public static function getSubscribedEvents(): array
    {
        return [
            ProductListingCriteriaEvent::class => 'onListingCriteria',
            ProductListingResultEvent::class => 'onListingResult'
        ];
    }

        public function onListingResult(ProductListingResultEvent $event)
    {
        $this->currentPropertyOptions = ['foo', 'bar', 'baz'];

        /*
        $properties = $event->getResult();
        $currentPropertyOptions = $properties->getCurrentFilter('properties');
        */
    }

    public function onListingCriteria(ProductListingCriteriaEvent $event): void
   {
        $event->getCriteria()->addAssociation('properties');
        $event->getCriteria()->addAssociation('properties.group');

        $currentPropertyOptions = $this->currentPropertyOptions;

        /*
        if (in_array('c0d02d1738fd4293a489695787e06b5c', $currentPropertyOptions)) {
            $criteria = $event->getCriteria();

            $criteria->addFilter(new MultiFilter(
                MultiFilter::CONNECTION_OR,
                    [
                        new ContainsFilter('product.properties.name', 'Option1'),
                        new ContainsFilter('product.properties.name', 'Option2')
                    ]
                )
            );
        }
        */

        $event->getContext()->addExtension('currentPropertyOptionsCriteria', new ArrayEntity($currentPropertyOptions));
   }
}
E_net4
  • 27,810
  • 13
  • 101
  • 139
Stacker1427
  • 163
  • 8

1 Answers1

1

What are you trying to achieve? Do you know what to use these events for and when they are dispatched? What is currenProperty? Is it something you get from the event object?

First of all, if you have an argument in the constructor but don't pass it when you instantiate the class, that will cause an error. Services such as event subscribers are instantiated with the arguments defined in the service definition. That's called dependency injection. If you want to set currentProperty dynamically based on some event, you likely don't want to set it in your constructor.

Refer to the documentation on how to listen to events properly.

You need the service definition first, that's where you define the arguments for instantiating the class. For example if you want to inject a repository:

<service id="Swag\BasicExample\Subscriber\MySubscriber">
    <argument type="service" id="property_group_option.repository"/>
    <tag name="kernel.event_subscriber"/>
</service>

In your example you didn't implement getSubscribedEvents in your subscriber. Without it your subscriber does nothing, as it defines which method is called on which event. It should actually cause an error if you fail to implement the method, as it is required by the interface to do so. Not sure if you omitted it by accident or deliberately.

class Subscriber implements EventSubscriberInterface
{
    private EntityRepository $repository;

    private ?array $currentProperties = null;

    public function __construct(EntityRepository $repository)
    {
        $this->repository = $repository;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            ProductListingCriteriaEvent::class => 'onListingCriteria',
            ProductListingResultEvent::class => 'onListingResult',
        ];
    }

    public function onListingCriteria(ProductListingCriteriaEvent $event): void
    {
        $this->currentProperties = ['foo', 'bar', 'baz'];
    }

    public function onListingResult(ProductListingResultEvent $event): void
    {
        $currentProperties = $this->currentProperties;
        // ...
    }
}

When you set a property listening to one event, and get that property on another event, you also have to mind the order in which these events are dispatched. In your example, you get the property on ProductListingCriteriaEvent and set it on ProductListingResultEvent. However, the criteria event should always be dispatched before the result event. That would explain why the property is always uninitialized when you try to retrieve it, as it hasn't been set yet. Familiarize yourself with the existing events and choose them according to your needs.

dneustadt
  • 12,015
  • 1
  • 12
  • 18
  • Thank you for the quick reply @dneustadt I'm not using a repository. I aim to add a filter to existing Criteria depending on a Current filter selected options. I have placed my code idea on top of my input. It all works, I just can't make it exchanging array between handleResult and handleRequest, that's why I set $currentProperty1. – Stacker1427 May 16 '23 at 10:36
  • The explanation still stands. You can't set the property in the constructor from an argument unless you provide the argument in the service definition. Where is `array $currentProperty1` supposed to come from? What should its value be? Also as explained above, `ProductListingResultEvent` is dispatched later in the stack than `ProductListingCriteriaEvent`. You're setting it after you're trying to use it. – dneustadt May 16 '23 at 10:52
  • array $currentProperty1 is supposed to come from `public function handleResult(ProductListingResultEvent $event)` I also have `public function handleRequest(ProductListingCriteriaEvent $event)` later (after handleResult) in my current code – Stacker1427 May 16 '23 at 11:43