a536baabd6
Adds the complete project foundation: - BMAD BMM workflow tooling (_bmad/) - Claude slash commands, skills, and project memories (.claude/) - ESP32 firmware scaffold (PlatformIO + Waveshare e-ink driver) - .gitignore excluding _bmad-output/ and .pio/ build artifacts Planning artifacts (PRD, architecture, epics) are intentionally not tracked — they live in _bmad-output/ per project convention. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
343 lines
10 KiB
Markdown
343 lines
10 KiB
Markdown
---
|
|
name: oro-api-dev
|
|
description: OroCommerce API development reference covering JSON:API configuration, entity exposure, processors, filters, actions, authentication, CORS, batch API, storefront API, and testing. Use when configuring API resources, creating custom API processors, exposing entities via REST, building API integrations, or testing API endpoints in OroCommerce.
|
|
---
|
|
|
|
# OroCommerce API Developer
|
|
|
|
Reference for API development on OroCommerce.
|
|
Docs: https://doc.oroinc.com/backend/api/
|
|
|
|
## Overview
|
|
|
|
Oro API implements REST conforming to [JSON:API specification](http://jsonapi.org/).
|
|
Built on `OroApiBundle` using ChainProcessor, EntitySerializer, and Symfony Forms.
|
|
|
|
Base URL: `https://{host}/api/{resource}`
|
|
Sandbox: `https://{host}/api/doc`
|
|
|
|
By default, only custom entities, dictionaries, and enumerations are accessible.
|
|
Other entities must be explicitly enabled.
|
|
|
|
## Enabling an Entity
|
|
|
|
Create `Resources/config/oro/api.yml`:
|
|
|
|
```yaml
|
|
api:
|
|
entities:
|
|
Acme\Bundle\ExampleBundle\Entity\Example: ~
|
|
```
|
|
|
|
Then run:
|
|
```bash
|
|
php bin/console oro:api:cache:clear
|
|
php bin/console oro:api:doc:cache:clear # rebuild sandbox docs
|
|
```
|
|
|
|
## Configuration Reference
|
|
|
|
```yaml
|
|
api:
|
|
entities:
|
|
Acme\Bundle\ExampleBundle\Entity\Example:
|
|
# Exclude entity entirely
|
|
exclude: false
|
|
|
|
# Field configuration
|
|
fields:
|
|
id: ~
|
|
name:
|
|
description: "Human-readable name"
|
|
secretField:
|
|
exclude: true # hide from API
|
|
renamedField:
|
|
property_path: original_name # map API name to entity property
|
|
computedField:
|
|
data_type: string
|
|
property_path: _ # custom processor handles this
|
|
|
|
# Filter configuration
|
|
filters:
|
|
fields:
|
|
name: ~
|
|
status:
|
|
allow_array: true # ?filter[status][]=a&filter[status][]=b
|
|
createdAt:
|
|
exclude: false
|
|
|
|
# Sorter configuration
|
|
sorters:
|
|
fields:
|
|
name: ~
|
|
createdAt: ~
|
|
|
|
# Action configuration
|
|
actions:
|
|
get: true
|
|
get_list: true
|
|
create: true
|
|
update: true
|
|
delete: false # disable delete
|
|
delete_list: false # disable bulk delete
|
|
|
|
# Subresources
|
|
subresources:
|
|
relatedItems:
|
|
actions:
|
|
get_subresource:
|
|
exclude: false
|
|
```
|
|
|
|
## Actions
|
|
|
|
Standard CRUD actions available per entity:
|
|
|
|
| Action | HTTP | URL | Description |
|
|
|---|---|---|---|
|
|
| get | GET | /api/{resource}/{id} | Single resource |
|
|
| get_list | GET | /api/{resource} | Collection |
|
|
| create | POST | /api/{resource} | Create |
|
|
| update | PATCH | /api/{resource}/{id} | Update |
|
|
| delete | DELETE | /api/{resource}/{id} | Delete |
|
|
| delete_list | DELETE | /api/{resource} | Bulk delete |
|
|
| get_subresource | GET | /api/{resource}/{id}/{sub} | Subresource |
|
|
| get_relationship | GET | /api/{resource}/{id}/relationships/{rel} | Relationship |
|
|
| update_relationship | PATCH | /api/{resource}/{id}/relationships/{rel} | Set relationship |
|
|
| add_relationship | POST | /api/{resource}/{id}/relationships/{rel} | Add to relationship |
|
|
| delete_relationship | DELETE | /api/{resource}/{id}/relationships/{rel} | Remove from relationship |
|
|
|
|
## Adding Filters to Existing Oro Entities
|
|
|
|
Expose custom fields on Oro core entities as API filters without modifying core code:
|
|
|
|
```yaml
|
|
api:
|
|
entities:
|
|
Oro\Bundle\CustomerBundle\Entity\CustomerUser:
|
|
filters:
|
|
fields:
|
|
erp_contact_id:
|
|
data_type: integer
|
|
property_path: erp_contact_id
|
|
Oro\Bundle\CustomerBundle\Entity\Customer:
|
|
filters:
|
|
fields:
|
|
erp_customer_id:
|
|
data_type: integer
|
|
property_path: erp_customer_id
|
|
```
|
|
|
|
This lets API consumers query: `GET /api/customerusers?filter[erp_contact_id]=12345`
|
|
|
|
The `property_path` must match an extended field added via migration (`oro_options`).
|
|
|
|
## Custom Processors
|
|
|
|
Processors modify request/response handling in the API pipeline.
|
|
Each action has a chain of processors executed in order.
|
|
|
|
```php
|
|
namespace Acme\Bundle\ExampleBundle\Api\Processor;
|
|
|
|
use Oro\Bundle\ApiBundle\Processor\CustomizeLoadedData\CustomizeLoadedDataContext;
|
|
use Oro\Component\ChainProcessor\ContextInterface;
|
|
use Oro\Component\ChainProcessor\ProcessorInterface;
|
|
|
|
class ComputeExampleField implements ProcessorInterface
|
|
{
|
|
public function process(ContextInterface $context): void
|
|
{
|
|
/** @var CustomizeLoadedDataContext $context */
|
|
$data = $context->getData();
|
|
// modify $data
|
|
$data['computedField'] = 'computed_value';
|
|
$context->setData($data);
|
|
}
|
|
}
|
|
```
|
|
|
|
Register with tag:
|
|
|
|
```yaml
|
|
services:
|
|
acme.api.processor.compute_example_field:
|
|
class: Acme\Bundle\ExampleBundle\Api\Processor\ComputeExampleField
|
|
tags:
|
|
- name: oro.api.processor
|
|
action: customize_loaded_data
|
|
class: Acme\Bundle\ExampleBundle\Entity\Example
|
|
```
|
|
|
|
### Processor Tag Attributes
|
|
|
|
| Attribute | Purpose | Example Values |
|
|
|---|---|---|
|
|
| `action` | Which API action to hook | `get`, `get_list`, `create`, `update`, `delete`, `customize_loaded_data` |
|
|
| `group` | Pipeline stage within the action | `initialize`, `normalize_input`, `security_check`, `transform_data`, `normalize_result` |
|
|
| `priority` | Execution order (higher = earlier) | `-10` (after defaults), `250` (early) |
|
|
| `class` | Entity class to scope processor to | `Oro\Bundle\CustomerBundle\Entity\CustomerUser` |
|
|
|
|
### Intercepting Create/Update with FormContext
|
|
|
|
Use `FormContext` to modify request data during create/update (e.g., resolve a custom field to a relationship):
|
|
|
|
```php
|
|
use Oro\Bundle\ApiBundle\Model\Error;
|
|
use Oro\Bundle\ApiBundle\Processor\FormContext;
|
|
use Oro\Bundle\ApiBundle\Request\Constraint;
|
|
use Oro\Component\ChainProcessor\ContextInterface;
|
|
use Oro\Component\ChainProcessor\ProcessorInterface;
|
|
|
|
class ResolveCustomerFromErpId implements ProcessorInterface
|
|
{
|
|
public function __construct(private ManagerRegistry $registry) {}
|
|
|
|
public function process(ContextInterface|FormContext $context): void
|
|
{
|
|
$requestData = $context->getRequestData();
|
|
$erpId = $requestData['erp_customer_id'] ?? null;
|
|
|
|
if ($erpId === null) {
|
|
return;
|
|
}
|
|
|
|
$customer = $this->registry
|
|
->getRepository(Customer::class)
|
|
->findOneBy(['erp_customer_id' => $erpId]);
|
|
|
|
if ($customer !== null) {
|
|
$requestData['customer'] = [
|
|
'class' => Customer::class,
|
|
'id' => $customer->getId()
|
|
];
|
|
$context->setRequestData($requestData);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Register on the `create` action in `transform_data` group:
|
|
|
|
```yaml
|
|
services:
|
|
acme.api.processor.resolve_customer:
|
|
class: Acme\Bundle\ExampleBundle\Api\Processor\ResolveCustomerFromErpId
|
|
arguments: ['@doctrine']
|
|
tags:
|
|
- { name: oro.api.processor, action: create, group: transform_data, priority: -40, class: Oro\Bundle\CustomerBundle\Entity\CustomerUser }
|
|
```
|
|
|
|
## Authentication
|
|
|
|
API supports OAuth 2.0:
|
|
|
|
```bash
|
|
# Get token
|
|
curl -X POST https://host/oauth2-token \
|
|
-d "grant_type=client_credentials" \
|
|
-d "client_id=<id>" \
|
|
-d "client_secret=<secret>"
|
|
|
|
# Use token
|
|
curl -H "Authorization: Bearer <token>" \
|
|
-H "Accept: application/vnd.api+json" \
|
|
https://host/api/examples
|
|
```
|
|
|
|
Configure OAuth apps in admin: System > User Management > OAuth Applications.
|
|
|
|
## Filters
|
|
|
|
```
|
|
GET /api/examples?filter[name]=test
|
|
GET /api/examples?filter[status][]=active&filter[status][]=pending
|
|
GET /api/examples?filter[createdAt]>2024-01-01
|
|
GET /api/examples?page[number]=2&page[size]=10
|
|
GET /api/examples?sort=-createdAt,name
|
|
GET /api/examples?include=relatedEntity
|
|
GET /api/examples?fields[examples]=name,status
|
|
```
|
|
|
|
## Batch API
|
|
|
|
For bulk operations:
|
|
|
|
```
|
|
POST /api/examples/batch
|
|
Content-Type: application/vnd.api+json
|
|
|
|
{ "data": [ { "type": "examples", "attributes": { ... } }, ... ] }
|
|
```
|
|
|
|
Async batch API processes large datasets via MQ.
|
|
Docs: https://doc.oroinc.com/api/batch-api/
|
|
|
|
## Storefront API
|
|
|
|
Separate API for storefront (customer-facing).
|
|
Configure in `Resources/config/oro/api_frontend.yml`:
|
|
|
|
```yaml
|
|
api:
|
|
entities:
|
|
Acme\Bundle\ExampleBundle\Entity\Example:
|
|
# storefront-specific config
|
|
```
|
|
|
|
Different authentication (customer user tokens).
|
|
Docs: https://doc.oroinc.com/backend/api/storefront/
|
|
|
|
## CORS Configuration
|
|
|
|
```yaml
|
|
# config/config.yml
|
|
oro_api:
|
|
cors:
|
|
preflight_max_age: 600
|
|
allow_origins:
|
|
- 'https://frontend.example.com'
|
|
allow_headers:
|
|
- 'Content-Type'
|
|
- 'Authorization'
|
|
expose_headers:
|
|
- 'X-Include'
|
|
allow_credentials: true
|
|
```
|
|
|
|
## Testing API
|
|
|
|
Oro provides base test cases for API functional tests:
|
|
|
|
```php
|
|
use Oro\Bundle\ApiBundle\Tests\Functional\RestJsonApiTestCase;
|
|
|
|
class ExampleApiTest extends RestJsonApiTestCase
|
|
{
|
|
public function testGetList(): void
|
|
{
|
|
$response = $this->cget(['entity' => 'examples']);
|
|
$this->assertResponseContains('expected_response.yml', $response);
|
|
}
|
|
|
|
public function testCreate(): void
|
|
{
|
|
$response = $this->post(['entity' => 'examples'], 'create_request.yml');
|
|
$this->assertResponseContains('create_response.yml', $response);
|
|
}
|
|
}
|
|
```
|
|
|
|
Test fixtures in `Tests/Functional/Api/DataFixtures/`.
|
|
Expected responses in `Tests/Functional/Api/responses/`.
|
|
|
|
## CLI Commands
|
|
|
|
```bash
|
|
php bin/console oro:api:cache:clear # clear API config cache
|
|
php bin/console oro:api:doc:cache:clear # rebuild sandbox docs
|
|
php bin/console oro:api:config:dump-reference # dump config structure
|
|
php bin/console oro:api:debug # list available API resources
|
|
php bin/console oro:api:debug --sub-resources Example # show entity subresources
|
|
```
|