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

Conceptional Questions #4

Open
solverat opened this issue Dec 4, 2021 · 0 comments
Open

Conceptional Questions #4

solverat opened this issue Dec 4, 2021 · 0 comments

Comments

@solverat
Copy link

solverat commented Dec 4, 2021

Hello Team,

I had to build some REST Endpoints for pimcore, and I've decided to use API platform just for fun. It turned out that it wasn't that fun.

Then I checked out this repo, but unfortunately I was not able to use it because of some conceptional issues. So I built my own Bridge and how should I say: That was more than inefficient, but I'm now in possession of a full working REST Api for PIMCORE.

Before I'm going to spend more time on this, I wanted to ask you guys about the future and purpose of this repo. The main question I have is: Should it fully support REST principals?

If the answer is yes, I would love to contribute my package, so we can improve this bundle.

Below, my walkthrough, thoughts, and principals I've applied within my package:

Configuration

  • Do not predefine resources, add them to docs and leave it to the integrator, which resources he wants to enable (Even the asset resource, although they probably never change in their way of configuration). Besides, the swagger documentation will be flooded with endpoints I don't need
  • Since we cannot use annotation or attributes in our data objects, I define everything via configuration files:
    • Resource: config/api_platform/product.yaml
    • Serialization: config/serialization/product.yaml
    • Validation: config/validator/product.yaml

Define groups for given operations can be really really hard when it comes to relations, collection:post, item:post, so I made good experiences to keep them in configuration files.

https://github.com/w-vision/PimcoreApiPlatform/tree/master/src/PimcoreApiPlatformBundle/Resources/config/api_resources

Identifier

Probably the biggest issue here. In my case, the id field is not part of the api but the customer wants to push his data with his own identifier. Since it's super easy, to set the identifier via resource configuration, that was not a big thing. But internaly, this bundles claims to all entities to use the idfield:

https://github.com/w-vision/PimcoreApiPlatform/search?q=withidentifier

This is not required and should be part of the configuration layer. By removing all the if($property === 'id') you will benefit from less code and more flexibility.

Data Provider

https://github.com/w-vision/PimcoreApiPlatform/blob/45d9bb89ae904dfe8059d08e14d858bc707ce3a0/src/PimcoreApiPlatformBundle/Bridge/Pimcore/ElementDataProvider.php#L31

This also assumes, that we're only dealing with the id field only. Instead, I used the $id property in getItem which allows getting the elements by given identifiers.

Data Persister

The data persister in this bundle will call the global pimcore configuration node to set required fields like key and parent. It also comes with some expression language helper. That's all nice but in fact unusable when it comes to complex scenarios (the requirement to store it differently because of conditions like variants for example)

Therefor, I introduced a new service called ElementDataPersistor and a new tag called app.api.pimcore.element_processor. It will be determinate the right element processor via suppors but is also able to validate the data before persisting (see quirks/cascading)

Quirks

Now it's going to be "hacky". There are some issues I've struggled with, and I'm still unsure if my approaches are the right way to do.

Unpublished Objects

Loading an object with an unpublished relation will end up as null in the serialization process. I added a DataObject::setHideUnpublishedin the data provider, which is not the right place to do it, I guess.

Cascading

Super exciting! Not! This was probably the biggest challenge for me.
Imaging this:

image

Example 1:

Easy. Pass the IRI to an existing supplier will automatically map the supplier entity to the product.

Example 2:

Also, easy. This is the valid json+ld definition. Load supplier and adopt the changes to the supplier.

Example 3:

Here comes the fun part. This POST will create a product and also a supplier. So far, so easy.

Cascade Persisting

But as soon we're entering the persister layer, pimcore cries like a baby because supplier needs to persisted FIRST. Ok, but there is one more problem. If I'm going to persist my supplier first and I secondly save my product (which also can rise some exceptions) I already have a persisted supplier which is not what I want (And wouldn't be an issue at all if we would deal with real doctrine entities...).

Therefor, my element processors have to implement a validate method, which allows validating their data (I'm doing this recursive of an object incl. relational fields).

Relational Updates

Looking again at example 3: In most cases, the user wants to update the relation entity the same way. But that's not possible in a nice way. I need to do this in my data persister (check if identifier is available and search it in database, THEN map all the properties - EVIL). The solution for me was to decorate the api_platform.serializer.normalizer.item:

There, I'll fetch the entity and pass it via OBJECT_TO_POPULATE.

image

However, I am not sure if this should be an API platform thing. In some ways I think this should be supported by API platform since they already introduced the api_allow_update property to allow updates for relation fields. But they only allow the "@id" and it will throw an exception, if the relation is not existing (which does not exist at the first time of my POST operation.

Filter

I introduced a simple Filter class which allows simple conditions by defining service aliases (similar to the doctrine property filter).

DataObjectPropertyNameCollectionFactory

https://github.com/w-vision/PimcoreApiPlatform/blob/45d9bb89ae904dfe8059d08e14d858bc707ce3a0/src/PimcoreApiPlatformBundle/Bridge/Pimcore/Metadata/Property/DataObjectPropertyNameCollectionFactory.php#L45

As you can see here, you're going to override all the existing collection items of previous decorators. In my case, my data object extends a custom model which contains some additional (virtual) properties. Those of course will never show up. That's why I've added some additional property search:
image

Auth

I've implemented the JWT authorization. Documentation should be enough, i guess.

Type Factories

Love them. Used them. Didn't change anything. Great stuff!

Phew. Let's call this a whitepaper with some burn marks! :) Hopefully you're able to follow my trip to hell and back. Like I said before, it would be great to provide this to the community, but it really needs a lot of discussion. Maybe we should arrange some hackaton videocalls. :)

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

1 participant