From 31a9d94972fdc4e923fd0d3444d5f470d63e05de Mon Sep 17 00:00:00 2001 From: Paula Date: Wed, 8 Jul 2026 15:20:50 +0200 Subject: [PATCH] docs: restructure usage examples and add context7 configuration Restructure the README usage section into task-oriented headings so each example is self-contained: full request lines for locale selection, a response example for ?groups[]=translations, a merge patch example (the recommended way to edit translations, previously the only edit example showed the PUT payload), and an explicit full-replace PUT section. Add a limitations section covering the two known gaps (no filtering by translated fields, one translation query per collection item) and normalize code fence info strings. Add context7.json so the Context7 documentation index used by AI coding agents serves curated content: README, changelog and upgrade guides only, usage rules stating the bundle's conventions and footguns, versioned docs for the 1.4 and 2.0 tags, and the verification key linking the index entry to this repository. --- README.md | 130 +++++++++++++++++++++++++++++++++++++++++--------- context7.json | 36 ++++++++++++++ 2 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 context7.json diff --git a/README.md b/README.md index 4ddb2ae..f89c8e7 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Implementation: - Add virtual fields for all translatable fields; their getters and setters delegate to the translation object Example: -``` php +```php use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\GetCollection; @@ -149,7 +149,7 @@ class Article extends AbstractTranslatable - Add the `translations` serialization group to all fields, plus your usual read/write groups Example: -``` php +```php use Doctrine\ORM\Mapping as ORM; use Locastic\ApiPlatformTranslationBundle\Model\AbstractTranslation; use Locastic\ApiPlatformTranslationBundle\Model\TranslatableInterface; @@ -202,7 +202,7 @@ class ArticleTranslation extends AbstractTranslation For `PUT` you **must** disable API Platform's `standard_put`, either per operation (`extraProperties: ['standard_put' => false]`, as above) or once for the whole API: -``` yaml +```yaml # config/packages/api_platform.yaml api_platform: defaults: @@ -215,43 +215,114 @@ With `standard_put` on, API Platform deserializes into a brand-new object and co Usage: ------ -**Language param for displaying a single translation:** +### Request a single locale + +Pass the locale as a query parameter: -`?locale=de` +``` +GET /articles/1?locale=de +``` -**Or use the Accept-Language http header** +Or use the `Accept-Language` HTTP header: -`Accept-Language: de` +``` +GET /articles/1 +Accept-Language: de +``` + +Translatable fields are returned in the requested locale; when no translation +exists for it, the fallback locale is used. **Restricting locales:** if [`framework.enabled_locales`](https://symfony.com/doc/current/reference/configuration/framework.html#enabled-locales) or the bundle's own `enabled_locales` option (which takes precedence) is configured, only those locales are accepted: a `?locale=` value outside the list and non-matching `Accept-Language` headers fall back to the default locale. When neither is configured (Symfony's default), any requested locale is accepted. -**Serialization group for displaying all translations:** +### Return all translations in a response + +Add the `translations` serialization group through the `translation.groups` +filter (registered by this bundle, enabled on the resource via +`filters: ['translation.groups']`): + +``` +GET /articles/1?groups[]=translations +``` -`?groups[]=translations` +The group is added on top of the operation's normalization groups, so the +response contains the single-locale virtual fields plus the full collection: -**POST translations example** -``` json +```json { - "datetime":"2017-10-10", + "@id": "/articles/1", + "title": "test", "translations": { - "en":{ - "title":"test", - "content":"test", - "locale":"en" + "en": { + "id": 2, + "title": "test", + "content": "test", + "locale": "en" }, - "de":{ - "title":"test de", - "content":"test de", - "locale":"de" + "de": { + "id": 3, + "title": "test de", + "content": "test de", + "locale": "de" } } } ``` -**EDIT translations example** +### Create a resource with translations (POST) + +Submit `translations` as an object keyed by locale; each entry must repeat its +`locale` field: -Send the `id` of each existing translation so it is updated instead of replaced: -``` json +```json +{ + "datetime": "2017-10-10", + "translations": { + "en": { + "title": "test", + "content": "test", + "locale": "en" + }, + "de": { + "title": "test de", + "content": "test de", + "locale": "de" + } + } +} +``` + +### Update translations (PATCH, recommended) + +A merge patch updates only the submitted locales and leaves the others +untouched; existing translation rows are updated in place, no `id` needed: + +``` +PATCH /articles/1 +Content-Type: application/merge-patch+json +``` + +```json +{ + "translations": { + "de": { + "title": "test edit de", + "locale": "de" + } + } +} +``` + +Here the `de` title is updated while the `en` translation is left as is. + +### Replace all translations (PUT) + +`PUT` is a full replace: locales absent from the payload are removed. It +requires `standard_put` to be disabled (see the editing notes under +[Implementation](#implementation) above). Send the `id` of each +existing translation so it is updated instead of replaced: + +```json { "datetime": "2017-10-10T00:00:00+02:00", "translations": { @@ -271,6 +342,19 @@ Send the `id` of each existing translation so it is updated instead of replaced: } ``` +Limitations: +------------ + +- **Filtering and ordering by translated fields is not supported.** The + translated values live on the translation entity and are exposed through + virtual getters, so built-in API Platform filters (`SearchFilter`, + `OrderFilter`, ...) cannot target them on the resource. Filtering on + translation fields requires a custom filter joining the translation entity. +- **Collections issue one translation query per item.** The `translations` + association is `EXTRA_LAZY` and resolved per entity, so listing N resources + triggers N additional queries for their translations; there is no built-in + eager loading yet. + ## Contribution If you have an idea on how to improve this bundle, feel free to contribute. If you have problems or you found some bugs, please open an issue. diff --git a/context7.json b/context7.json new file mode 100644 index 0000000..4ab7a8b --- /dev/null +++ b/context7.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://context7.com/schema/context7.json", + "url": "https://context7.com/locastic/apiplatformtranslationbundle", + "public_key": "pk_6nLhWveJT5eYd7x7e5w1o", + "projectTitle": "Locastic ApiPlatformTranslationBundle", + "description": "API-first entity translations for API Platform (Symfony): per-locale translation entities exposed as embedded API objects, with the active locale resolved from each request.", + "folders": [], + "excludeFolders": [ + "src", + "tests", + "config", + "bin", + "vendor" + ], + "excludeFiles": [ + "CONTRIBUTING.md", + "SECURITY.md" + ], + "rules": [ + "A translatable resource extends AbstractTranslatable, implements createTranslation(), and maps translations as OneToMany with indexBy: 'locale', cascade: ['persist'], orphanRemoval: true.", + "The translation entity extends AbstractTranslation and holds all translatable fields plus a writable locale field.", + "Virtual getters and setters on the translatable delegate to getTranslation(), which resolves the current locale and falls back to the fallback locale.", + "Put the 'translations' serialization group on the translations collection, on every field of the translation entity, and in the normalizationContext of POST, PUT, and PATCH operations so writes return all translation objects.", + "In write payloads, translations is an object keyed by locale, and each entry must repeat its 'locale' field: {\"translations\": {\"de\": {\"title\": \"...\", \"locale\": \"de\"}}}.", + "Prefer PATCH (application/merge-patch+json) for editing translations: it updates only the submitted locales. PUT is a full replace and removes locales absent from the payload.", + "PUT requires API Platform's standard_put to be disabled, either per operation via extraProperties: ['standard_put' => false] or globally under api_platform.defaults.extra_properties.", + "When editing via PUT, include each existing translation's 'id' so its row is updated in place instead of replaced.", + "Clients select the locale with the ?locale= query parameter or the Accept-Language header, and can request all translations at once with ?groups[]=translations (the bundle's 'translation.groups' filter).", + "Restrict accepted locales with framework.enabled_locales or the bundle's api_platform_translation.enabled_locales option (which takes precedence); unlisted locales fall back to the default locale.", + "Version 2.x requires PHP >= 8.2, API Platform ^3.4 || ^4.0, and Doctrine ORM ^3.0; use PHP 8 attributes, not Doctrine annotations, in mappings." + ], + "previousVersions": [ + "v2.0.1", + "v1.4.1" + ] +}