API decorators

The API decorators allow you to register a decorator to be called before and after each method call to any API.

  • If called before an API call, you can modify the data passed to the API or perform additional actions.
  • If called after an API call, you can modify or enrich the result or just react to the API response.

We provide the API decorators natively for all our APIs, this means they're available right now for the following APIs:

  • AccountAPI
  • CartAPI
  • ContentAPI
  • ProductAPI
  • WishlistAPI

Process

We use Symfony service container tags to allow you to register new decorators. Each API has its own tags and as soon as your decorator is registered and contains methods following a certain schema, those methods will be called. The service tags are:

  • AccountAPI: accountApi.lifecycleEventListener
  • CartAPI: cartApi.lifecycleEventListener
  • ContentAPI: contentApi.lifecycleEventListener
  • ProductAPI: productApi.lifecycleEventListener
  • WishlistAPI: wishlistApi.lifecycleEventListener

Below is an example of how you'd register a custom decorator in your service.xml file:

<service id="Acme\ProductBundle\Domain\AcmeDecorator">
		<!-- service arguments... -->

		<tag name="productApi.lifecycleEventListener" />
	</service>

The method naming scheme is the following:

  • Each method from each API will be prefixed with before and after respectively
  • Frontastic will check if such a method is defined in your decorator
  • Frontastic calls the decorator with the aggregated API and all original arguments for the before call
  • Frontastic aggregates and returns the value for the after call

If we take a look at ProductApi::getProduct(ProductQuery $query): ?Product this means you can define the following methods in your decorator:

public function beforeGetProduct(ProductApi $api, ProductQuery $query)
{
	// Do something before a product is retrieved, for example,
	// adding additional filters to the query.
}
public function afterGetProduct(ProductApi $api, ?Product $result): ?Product
{
	// Modify the returned product, for example, with different prices or
	// additional data.
}

We don't provide an interface for the decorators because we want to allow you to only define a subset of these methods, while an interface would force you to implement all of them. At some point, we might provide you with base classes for all decorators which provide empty default implementations of all available methods.

This works for all methods in all API interfaces.