Custom fields management in Admin API#111
Conversation
|
This pull request seems to contain new translation strings. I have summarized them below to ease up review:
(Note: this is an automated message, but answering it will reach a real human) |
e0c0920 to
cf9310f
Compare
jolelievre
left a comment
There was a problem hiding this comment.
thanks for the contribution, but I belive I laready explained on the OS slacks The admin API is not made to be extended.
Each endpoint today is built on CQRS commands that reflect the business logic of the Core, and only the core. If you have extra fields, they should be handled separately by your modules in a dedicated endpoint. But the core API contract shouldn't be modified in anyway by external modules.
This is what ensures the API stability and consistency across any shop.
|
I understand your point of view. At the same time, in the real day-to-day life of agencies, every project requires custom fields. As a result, the native endpoints will almost never be used as-is, because they don’t meet the actual needs. Each developer, in every agency, will therefore need to reimplement the native logic / adapt it to handle some new fields. This leads to:
If I take the example of my current customer project alone, I already need to redefine the endpoints for customers, groups, features, feature values, attributes groups, attribute, specific prices, products, combinations, cetegories and addresses. This is why I think having a standard extension mechanism is important. Without one, every agency will implement its own version of the Admin API, resulting in a fragmented ecosystem with hundreds of incompatible implementations. The proposal in this PR has the advantage of relying directly on native code, and of following PrestaShop's principles of interacting with the processing flow through hooks. Hooks are already used everywhere in Prestashop. Why not in the Admin API? To avoid altering the native API contract, custom fields could even be isolated under a dedicated key, such as My personal though is that if the Admin API does not support the reality of actual shops, developers simply won't be able to use it in real projects. This proposal aims to allow safe, controlled extensibility without changing the native API structur, by using the hook system already widely used in others parts of the software. |
By example, you have 3 modules that add extra datas to customer : a reference, a paiement preferences, ... |
|
The need is real, it’s also about APIs performance, number of requests needed, etc. I think we should try to think this through. |
|
I absolutely get the need, but I think the topic of custom fields reaches beyond the API context. The need is real and almost every ERP I got my hands on has to ability to create them. Or even CMS systems like Processwire. For example, in our ERP system, for every entity, I can define custom fields. It's name, data type, value, default value, note. After creation, it automatically creates a new column in the database for a given entity. Then I can use it like any of the default fields in all listing tables, in filters, I can export/import it, print templates, anywhere. |
|
Let's take a look at this, it's native in sylius: |
|
Maybe we will need to transform thois PR into a discussion. But, first of all : thanks and wow, @Jeremie-Kiwik. You take the time to analyze, comment, make the dev and document it with example. It's a good things and a real thanks from me, really. PrestaShop is made to be customized. It's always be there and done like this. Maybe we want to remove override but you all know that PrestaShop will and will be customized with third party modules and/or development. It's a fact. It's not only one merchant, it's not only one agency. It's all the PrestaShop instances. This new Admin API is made to be more robust, more easier than the legacy webservice. PrestaShop gives already somes hooks but the CQRS is not so easy to handle to extend it. Admin API need to be more flexible, as I think, to handle theses customs sides. It's the powerfull of PrestaShop and of this Admin API. I hope that, as a open source side of the project, this will be discuss as it. (My english is not so good, but I hope it's understood there.) |
|
The way Jérémie implemented the handling of custom fields respects, in my opinion, the PrestaShop philosophy. Modules declare their fields and manage persistence using hooks. As already mentioned, in a real-world scenario, we need to minimize web service calls as much as possible for performance reasons. Otherwise, this API will not be usable or used. @jolelievre : What is blocking this PR? |
|
Hello everyone, I'm joining the discussion as both this API and custom fields are subjects that matter a lot. Custom fields in this new API will indeed be very useful and a serious improvement for day-to-day workflows. In other APIs I use, you can add parameters to "embed" additional data in the response. If not through the proposed approach, perhaps we could have a way to extend standard responses by adding "links" to simplify discovery. For example, for products: |
|
Hello @jolelievre Could this be discussed with the @PrestaShop/tech-council? |
|
I know this project is being discussed, so I'm putting this PR into the "Draft". |
Description
This PR adds support for custom fields in API Resources entities, allowing modules to extend entities with additional data that is automatically serialized/deserialized and persisted in dedicated SQL tables. The implementation integrates seamlessly into the existing API Platform serialization flow without requiring modifications to core entity classes.
EDIT (2025-11-25)
Important change in persistence mechanism:
The persistence and retrieval of custom fields data is no longer handled by the core module itself. Instead, it is now the responsibility of modules implementing the hooks. This change provides better separation of concerns and allows modules to have full control over their custom data storage.
New hooks added:
loadApiResourcesCustomFields: Called when serializing entities to load custom fields datapersistApiResourcesCustomFields: Called after entity creation/update to persist custom fields dataThe core module now only handles:
addApiResourcesCustomFieldshookExperimental Work & Community Contribution
This PR represents a Proof of Concept (POC) for custom fields support in the Admin API. This is experimental work that I'm sharing with the community to gather feedback and encourage collaboration.
I would love receive feedback on:
I would be delighted if this PR could be picked up, improved, or modified by other contributors. My hope is that, with community feedback and collaboration, we can turn this experimental POC into a robust and efficient mechanism for handling custom fields in the Admin API.
Custom Field Types
Custom fields can be of three types:
Base fields: Stored in custom tables (e.g.,
{entity}_extra). These fields are directly accessible in JSON at the root level:"stringField": "value".Localized fields: Stored in custom tables with language support (e.g.,
{entity}_lang_extra), using locale-based JSON format:Shop-specific fields: Stored in custom tables with shop support (e.g.,
{entity}_shop_extra), using shop ID-based JSON format:Table Naming
Note that table names are completely free - the
_extra,_lang_extra, and_shop_extrasuffixes are just naming conventions suggested in the example module, but modules can use any table names they want.How It Works
Modules declare custom fields via the
addApiResourcesCustomFieldshook, and implement two additional hooks to handle persistence:loadApiResourcesCustomFields: Load custom fields data from database when serializing entitiespersistApiResourcesCustomFields: Persist custom fields data to database after entity creation/updateThe core module handles:
This feature enables modules to extend any API Resources entity without modifying core code, with full control over their data storage.
Hook Implementation Example
A complete example module implementation is available in
modules/ps_apiresources/docs/custom-fields-hook-example.php. This file contains a fully functional module demonstrating all three hooks with base fields, localized fields, and shop-specific fields.Here's a minimal example of how to implement the three hooks:
How to test
Step-by-Step Testing Procedure
Step 1: Create the example module
Create the module directory:
Copy the example file:
Step 2: Install the module
Step 3: Verify database tables creation
Connect to your database
Check that the following tables have been created:
You should see:
ps_attribute_group_extra(base fields)ps_attribute_group_lang_extra(localized fields)ps_attribute_group_shop_extra(shop-specific fields)Step 4: Test POST request (Create entity with custom fields)
/admin-api/attribute-groupswith the following JSON body:{ "names": { "fr-FR": "Couleurs", "en-GB": "Colors" }, "publicNames": { "fr-FR": "Couleurs", "en-GB": "Colors" }, "type": "select", "shopIds": [1], "position": 0, "stringField": "My custom string", "intField": 42, "boolField": true, "enumField": "value1", "attributeGroupLangExtra": { "stringLangField": { "fr-FR": "Champ personnalisé en français", "en-GB": "Custom field in English" } }, "attributeGroupShopExtra": { "intShopField": { "1": 100 } } }Verify the response:
201 Createdstatus codeattributeGroupIdfrom the response (you'll need it for the next steps)Verify database persistence:
You should see:
ps_attribute_group_extrawith the base custom fieldsps_attribute_group_lang_extra(one for fr-FR, one for en-GB)ps_attribute_group_shop_extra(for shop ID 1)Step 5: Test GET request (Retrieve entity with custom fields)
Retrieve the created entity via
GET /admin-pi/attribute-groups/{id}(replace{id}with theattributeGroupIdfrom Step 4).Verify the response:
stringField,intField,boolField,enumField) should appear at the root level"attributeGroupLangExtra": {"stringLangField": {"fr-FR": "...", "en-GB": "..."}}"attributeGroupShopExtra": {"intShopField": {"1": 100}}Step 6: Test PATCH request (Partial update)
PATCH /api/attribute-groups/{id}:{ "attributeGroupLangExtra": { "stringLangField": { "fr-FR": "Nouvelle valeur française" } } }Verify the response:
fr-FRlocale) should be updateden-GB) should be preservedVerify database update:
You should see:
fr-FRrow should have the new value "Nouvelle valeur française"en-GBrow should still have the original value "Custom field in English"