FriendsOfSymfony / FOSElasticaBundle

Elasticsearch PHP integration for your Symfony project using Elastica.
http://friendsofsymfony.github.io
MIT License
1.25k stars 793 forks source link

Update parent when changing nested child (works only after a 2nd query) #1399

Closed sandoche closed 6 years ago

sandoche commented 6 years ago

I know this issue have been opened and closed many time. In short my problem is the following: I want to update the parent when the nested child are changed. It almost works!

I followed the code from this gist, after reading all the previous issues about this problem: https://gist.github.com/Nightbr/ddb586394d95877dde8ed7445c51d973

Here is what is happening, and the reason why I say it is almost working.

  1. I have a parent (user) and a nested child (answers)
  2. I create a first answer (A), the parent is not updated
  3. I create a second answer (B), the parent is updated and contains the nested answer A but not the second B.

Do you know what can be the reason? I would like to have it working before step 3. Actually I am not sure if I put the right object_persister names. You can see the details below.

My event subscriber is almost the same than here: https://gist.github.com/Nightbr/ddb586394d95877dde8ed7445c51d973

I just made the following changes in it. I made this change in order to match the persister name: fos_elastica.object_persister.learners_user.user (the parent).

#SearchListener.php which is extended by AnswerListener.php
$classPoped = array_slice(explode('\\',$asso["targetEntity"]), -1);
$className = strtolower(array_pop($classPoped));
$objectPersisterName = 'fos_elastica.object_persister.learners_'.$className.'.'.$className;

Here is the services.yaml part (I am using Symfony4)

#services.yaml
    App\Listener\AnswerListener:
        arguments:
            - '@fos_elastica.object_persister.learners_answer.answer'
            - '@fos_elastica.indexable'
            - {"indexName" : "learners_answer", "typeName": "answer"}
        calls:
            - [ setContainer, [ '@service_container' ] ]
        tags:
            - { name: 'doctrine.event_subscriber' }

And finally here is the mapping: I had to create an extra mapping learners_answer to make this (partly) work.

# fos_elastica.yaml
fos_elastica:
    indexes:

    #...

        learners_user:
            index_name: learners_user
            types:
                user:
                    properties:
                        id: ~
                        username: ~
                        firstname: ~
                        lastname: ~
                        email: ~
                        photo: ~
                        jobrole:
                            type: "nested"
                            properties:
                                name: ~
                        answers:
                            type: "nested"
                            properties:
                            # ... some more nested propreties

        learners_answer:
            index_name: learners_answer
            types:
                answer:
                    properties:
                        id: ~
                    persistence:
                        driver: orm
                        model: App\Entity\Answer
                        provider: ~
                        finder: ~
sandoche commented 6 years ago

I finally managed using another event from API-Platform. For the people interested here is my code

final class NestedUserAnswersElasticPersister implements EventSubscriberInterface
{

    private $objectPersister;

    private $em;

    public function __construct(ObjectPersisterInterface $objectPersister) {
        $this->objectPersister = $objectPersister;
    }

    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::VIEW => ['updateElastic', EventPriorities::POST_WRITE],
        ];
    }

    public function updateElastic(GetResponseForControllerResultEvent $event)
    {
        $answer = $event->getControllerResult();
        $method = $event->getRequest()->getMethod();

        if (!$answer instanceof Answer || Request::METHOD_POST !== $method) {
            return;
        }

        $user = $answer->getTeacher();
        $user->addAnswer($answer); // the most important line of code, it didn't work without this!

        $objectPersister = $this->objectPersister;
        $objectPersister->deleteOne($user);
        $objectPersister->insertOne($user);
    }
}