You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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:
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.
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:
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.
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).
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:
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. :)
The text was updated successfully, but these errors were encountered:
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
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 theid
field: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 ingetItem
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 likekey
andparent
. 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 calledapp.api.pimcore.element_processor
. It will be determinate the right element processor viasuppors
but is also able tovalidate
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 aDataObject::setHideUnpublished
in 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:
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
.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:
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. :)
The text was updated successfully, but these errors were encountered: