Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chained data persisters #1313

Closed
fullbl opened this issue Mar 15, 2021 · 6 comments
Closed

Chained data persisters #1313

fullbl opened this issue Mar 15, 2021 · 6 comments

Comments

@fullbl
Copy link

fullbl commented Mar 15, 2021

Hi, I'm noticing a strange behaviour with data persisters.

I would like to have a UserDataPersister to send e-mails and handling invitation, registration, activation and so on.
Then a ProductDataPersister that will handle some business logic about our product.
After that, I would like to have a SoftDeleteableDataPersister that handle the deletions of Users, Products and everything else that implement a specific interface.
Clearly, UserDataPersister supports $data instanceof User::class, ProductDataPersister supports $data instanceof Product::class and SoftDeleteableDataPersister supports $data instanceof SoftDeleteableInterface

This is my services.yml:

    App\DataPersister\ProductDataPersister:
            decorates: 'api_platform.doctrine.orm.data_persister'

    App\DataPersister\UserDataPersister:
            decorates: 'api_platform.doctrine.orm.data_persister'

    App\DataPersister\DeleteableDataPersister:
            decorates: 'api_platform.doctrine.orm.data_persister'

after doing that, I found a DeleteableDataPersister that decorates UserDataPersister that decorates ProductDataPersister that decorates Doctrine\Common\DataPersister. Shouldn't chained mean that they should run in parallel?
Also, if one of those is a DataPersister while the others are ContextAwareDataPersisters, autocomposition won't work (nothing could work like this!

To get the right thing, I have to do something like this, which is not documented:

    App\DataPersister\ProductDataPersister:
        arguments:
            $decorated: '@api_platform.doctrine.orm.data_persister'

    App\DataPersister\UserDataPersister:
        arguments:
            $decorated: '@api_platform.doctrine.orm.data_persister'

    App\DataPersister\DeleteableDataPersister:
        arguments:
            $decorated: '@api_platform.doctrine.orm.data_persister'

Also, I would like the Deleteable to be checked first and be resumable, and the others to be executed later and not be resumable, because I don't want Doctrine Data Persister to be executed. As asked in #540 is it possible to set an order for data persisters?

@soyuka
Copy link
Member

soyuka commented Mar 15, 2021

For the priority just use the tag priority (https://symfony.com/doc/current/service_container/tags.html#tagged-services-with-priority)

Also indeed decoration on the doctrine persister makes sense but the ChainDataPersister chains your data persisters you don't need them to decorate themselves. See https://github.com/api-platform/core/blob/main/src/DataPersister/ChainDataPersister.php.

Feel free to improve our documentation it'd be greatly appreciated!

@fullbl
Copy link
Author

fullbl commented Mar 15, 2021

Many thanks, I would like to improve the documentation, but I still don't understand if ChainDataPersister should work as I described.
If I wtite my services as documented (using the "decorates" attribute), every DataPersister will decorate another DataPersister written by me(so if I use something like $this->decorated->supports into my supports function, it will check if every DataPersister support this operation), so the chain is "vertical" (DeleatableDataPersister decorates UserDataPersister, which decorates ProductDataPersister, which decorates Doctrine\Common\DataPersister) instead of being "horizontal" (every DataPersister decorates Doctrine\Common\DataPersister).

In order to get things as documented, I have to manually inject the Doctrine DataPersister into every argument.

Is it right?

@soyuka
Copy link
Member

soyuka commented Mar 15, 2021

Mhh I would not do this. A common DataPersister that you can decorate is the DoctrineDataPersister as it manages some logic that could be shared with other data persisters. Your DeleatableDataPersister, UserDataPersister, ProductDataPersister are on various domains and do not share such logic. The supports function is 3 lines you can totally rewrite it IMHO.

@fullbl
Copy link
Author

fullbl commented Mar 15, 2021

Sorry, I don't know if you got the point on what happens and what my question is.

If I use the decorates parameter in services.yml, api platform make each one decorate each other. Is it wanted?

@soyuka
Copy link
Member

soyuka commented Mar 15, 2021

it's not api platform it's symfony and the decoration pattern: https://symfony.com/doc/current/service_container/service_decoration.html maybe start by there and things will get clearer.

If I use the decorates parameter in services.yml, api platform make each one decorate each other. Is it wanted?

No it should only decorate the doctrine ORM data persister (https://github.com/api-platform/core/blob/main/src/Bridge/Doctrine/Common/DataPersister.php).

@fullbl
Copy link
Author

fullbl commented Mar 15, 2021

Now I get it, didn't know it was a Symfony feature!
Seems that what I describe is the right behaviour, I'll try to provide a better documentation, thanks for the patience :D

fullbl added a commit to fullbl/docs that referenced this issue Mar 15, 2021
- explain how to avoid "vertical" DataPersisters chaining (api-platform#1313)
- explicit how to use tag priority (api-platform#540)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants