Sylius Guide d'installation
Ajoutez un assistant chat à votre Sylius boutique avec synchronisation automatique des produits.
Vous êtes dans le guide d'installation développeur. Vous cherchez un aperçu de ce que Emporiqa offre à votre Sylius boutique ? Voir la page d'intégration.
Démarrage rapide
Pour les développeurs déployant le plugin. Installation Composer, configuration YAML, synchronisation initiale.
DémarrerDocumentation développeur
Architecture, payload webhook, décoration de service et écouteurs d'événements Symfony.
LireDémarrage rapide
Installez le plugin avec Composer, déposez une petite configuration YAML, intégrez le widget dans votre mise en page, lancez une commande de synchronisation. Le widget de chat est en direct sur votre vitrine.
Ce qu'il vous faut
- Sylius 1.12+ ou 2.0+ sur Symfony 6.4+ / 7.x avec PHP 8.1 ou supérieur
-
Accès shell pour exécuter
composeretbin/console - Un compte Emporiqa — créer un compte gratuit si vous n'en avez pas encore
Installez le plugin
Récupérez le plugin via Composer. Il est publié sur Packagist. Packagist.
composer require emporiqa/sylius-plugin
Enregistrez le plugin
Ajoutez l'entrée du plugin à config/bundles.php. Symfony Flex peut le faire automatiquement pour vous.
// config/bundles.php
return [
// ... other bundles
Emporiqa\SyliusPlugin\EmporiqaPlugin::class => ['all' => true],
];
Configurez les identifiants
Créez config/packages/emporiqa.yaml avec les paramètres minimaux requis. Votre ID de boutique et Secret de connexion proviennent tous deux de votre tableau de bord Emporiqa sous Paramètres de boutique → Intégration.
# config/packages/emporiqa.yaml
emporiqa:
store_id: '%env(EMPORIQA_STORE_ID)%'
webhook_url: '%env(EMPORIQA_WEBHOOK_URL)%'
webhook_secret: '%env(EMPORIQA_WEBHOOK_SECRET)%'
enabled_languages: ['en_US', 'de_DE']
Then add the matching env vars to your .env (or .env.local).
# .env.local
EMPORIQA_STORE_ID=your_store_id
EMPORIQA_WEBHOOK_URL=https://emporiqa.com/webhooks/sync/
EMPORIQA_WEBHOOK_SECRET=your_connection_secret
Intégrez le widget
Ajoutez la fonction Twig avant </body> dans votre mise en page de boutique. La fonction emporiqa_cart_widget enregistre l'intégration plus le gestionnaire de panier en chat.
{# templates/bundles/SyliusShopBundle/Layout/base.html.twig #}
{{ emporiqa_cart_widget() }}
Enregistrez les routes
Créez config/routes/emporiqa.yaml pour monter l'API de panier du plugin et le point de terminaison de suivi de commande.
# config/routes/emporiqa.yaml
emporiqa:
resource: '@EmporiqaPlugin/config/routes.yaml'
Installez les assets et lancez la synchronisation initiale
Installez les assets du bundle, videz le cache, puis poussez vos produits et pages. La commande de synchronisation diffuse par lots et affiche la progression.
bin/console assets:install
bin/console cache:clear
bin/console emporiqa:test-connection
bin/console emporiqa:sync:all
Open your storefront. The chat bubble appears in the bottom-right corner. Click it, ask a product question, and the salesperson answers from your catalog.
C'est en direct
- Les mises à jour de produits se synchronisent automatiquement via les événements de ressources Sylius
- Les clients peuvent poser des questions sur les produits, les politiques et leurs commandes
- Ajouter au panier dans le chat ; les clients procèdent au paiement de votre boutique
Need to sync custom page entities, decorate the order provider, or react to cart events?
See Developer DocsDeveloper Documentation
Architecture, configuration reference, webhook payload schema, service decoration points, and Symfony events.
Architecture
The plugin is a Symfony bundle that hooks into Sylius via resource events (sylius.product.post_update, sylius.product_variant.post_update, etc.), Doctrine lifecycle events for page entities, and the Sylius checkout workflow (Symfony Workflow on 2.x, Winzou state machine on 1.x) for conversion tracking.
Webhook events use deferred delivery: event subscribers queue payloads in an in-memory WebhookEventQueue with deduplication, and the queue flushes to Emporiqa on kernel.terminate after the HTTP response is sent. Saving a product or completing checkout completes synchronously while the outbound HTTP call runs asynchronously.
All translations and channels for a single product or page consolidate into one webhook event. Variant changes cascade to the parent: editing a variant triggers a parent re-sync so search always sees the full catalog state. Every service that formats, resolves, looks up, or transports data ships behind an interface so you can decorate any part of the pipeline without patching plugin code.
Requirements
| Sylius | 1.12+ or 2.0+ |
| Symfony | 6.4+ or 7.x |
| PHP | 8.1 or higher |
| Emporiqa account | Free Emporiqa account ($25 credit on signup) |
Configuration Reference
Configuration lives in config/packages/emporiqa.yaml. Only store_id, webhook_url, and webhook_secret are required. Everything else has sensible defaults.
Settings
| Key | Type | Default | Description |
|---|---|---|---|
store_id |
string | required | Store identifier from the Emporiqa dashboard. |
webhook_url |
string | required | Emporiqa webhook endpoint. |
webhook_secret |
string | required | HMAC-SHA256 signing key for webhooks and order tracking. |
base_url |
string | '' |
Absolute base URL for link generation when no HTTP request is available (used by console commands). |
media_base_path |
string | /media/image/ |
Base path for product image URLs. Override for CDN, S3, or LiipImagine setups. |
brand_attribute_code |
string | brand |
Product attribute code that holds brand/manufacturer data. |
enabled_languages |
string[] | ['en_US', 'de_DE'] |
Sylius locale codes to sync. Codes are passed as-is; no truncation. |
sync.products |
bool | true |
Enable automatic product synchronization. |
sync.pages |
bool | true |
Enable automatic page synchronization (only fires for classes in page_entity_classes). |
page_entity_classes |
string[] | [] |
FQCNs of your page entities. Page sync is opt-in — if this list is empty, no page events fire and the PageFormatter, PageDoctrineListener, and PageUrlResolver services are not registered. |
order_tracking.enabled |
bool | true |
Mounts the order tracking API controller. Disable to remove the endpoint entirely. |
cart.enabled |
bool | true |
Mounts the cart API and order completion subscriber. Disable to remove both. |
Full configuration example
# config/packages/emporiqa.yaml
emporiqa:
store_id: '%env(EMPORIQA_STORE_ID)%'
webhook_url: '%env(EMPORIQA_WEBHOOK_URL)%'
webhook_secret: '%env(EMPORIQA_WEBHOOK_SECRET)%'
base_url: 'https://myshop.com'
media_base_path: '/media/image/'
brand_attribute_code: 'brand'
enabled_languages: ['en_US', 'de_DE']
sync:
products: true
pages: true
page_entity_classes:
- App\Entity\StaticPage
- App\Entity\BlogPost
order_tracking:
enabled: true
cart:
enabled: true
Page sync is opt-in. Sylius has no built-in page entity, so the plugin does not auto-detect your CMS pages. To sync policies, FAQ entries, or blog posts, implement Emporiqa\SyliusPlugin\Model\PageInterface on your entity and list its FQCN under page_entity_classes. See Customization.
Channels
The plugin uses the Sylius channel code directly as the Emporiqa channel identifier — no mapping configuration needed. Each channel's pricing, availability, and translations are consolidated into the same webhook event.
Features
Real-time Webhooks
Products sync via Sylius resource events (post_create, post_update, pre_delete). Variant changes cascade to the parent. Events are deduplicated in-request and flushed after the response via kernel.terminate.
Chat Widget
Embed via {{ emporiqa_cart_widget() }}. Locale, currency, channel, and a signed user_id token are injected from Sylius contexts. Anonymous pages are safe for Varnish/CDN caching.
Multi-Channel & Multi-Language
A single product event carries every channel's pricing, availability, and all translations for each configured locale. Sylius locale codes (en_US, de_DE) are passed through unchanged.
Cart & Checkout API
REST endpoints under /emporiqa/api/cart for add, update, remove, clear, view, and checkout URL. The chat widget calls these via window.EmporiqaCartHandler. CSRF-protected for authenticated users.
Order Tracking
HMAC-signed endpoint at POST /emporiqa/api/order/tracking. Resolves Sylius payment and shipping states into a normalized order status response. Replay-protected (5 minute window).
Console Commands
emporiqa:sync:all, emporiqa:sync:products, emporiqa:sync:pages, and emporiqa:test-connection. Memory-efficient batching with session-based reconciliation.
Conversion Tracking
order.completed webhooks on checkout completion. Subscribes to Symfony Workflow (Sylius 2.x) or Winzou state machine (Sylius 1.x). Reads the emporiqa_sid cookie for chat-to-purchase attribution.
Extensible by Design
Every formatter, resolver, provider, and sender ships behind an interface. Decorate them with your own implementation, or listen to PostFormatEvent, CartOperationEvent, and others for fine-grained control.
Webhook Payload
Webhook requests are POSTed to the configured webhook_url with an X-Webhook-Signature header containing the HMAC-SHA256 signature of the raw body. Each request carries a batch of events.
Batch envelope
{
"events": [
{"type": "product.updated", "data": {...}},
{"type": "product.updated", "data": {...}}
]
}
Event types
| Event | Trigger |
|---|---|
product.created / product.updated / product.deleted |
Sylius product or variant lifecycle events |
page.created / page.updated / page.deleted |
Doctrine lifecycle events on classes listed in page_entity_classes |
sync.start / sync.complete |
Console sync commands (reconciliation sessions) |
order.completed |
Sylius checkout workflow completes |
Product event
{
"type": "product.updated",
"data": {
"identification_number": "product-123",
"sku": "PROD-123",
"channels": ["", "b2b"],
"names": {"": {"en_US": "Product Name", "de_DE": "Produktname"}, "b2b": {"en_US": "Product Name"}},
"descriptions": {"": {"en_US": "Description...", "de_DE": "Beschreibung..."}},
"links": {"": {"en_US": "https://store.com/en_US/products/product-name", "de_DE": "https://store.com/de_DE/products/produktname"}},
"categories": {"": {"en_US": ["Electronics"], "de_DE": ["Elektronik"]}, "b2b": {"en_US": ["Electronics"]}},
"attributes": {"": {"en_US": {"Color": "Blue"}, "de_DE": {"Farbe": "Blau"}}},
"brands": {"": "Brand Name", "b2b": "Brand Name"},
"prices": {"": [{"currency": "EUR", "current_price": 79.99, "regular_price": 99.99}], "b2b": [{"currency": "USD", "current_price": 69.99, "regular_price": 89.99}]},
"images": {"": ["https://store.com/media/image/product.jpg"]},
"availability_statuses": {"": "available", "b2b": "available"},
"stock_quantities": {"": 25, "b2b": 25},
"parent_sku": null,
"is_parent": false,
"variation_attributes": {}
}
}
Field structure
| Type | Pattern | Fields |
|---|---|---|
| Translatable | {channel: {locale: value}} |
names, descriptions, links, categories, attributes, variation_attributes |
| Shared | {channel: value} |
brands, prices, images, availability_statuses, stock_quantities |
| Flat | Direct value | identification_number, sku, channels, is_parent, parent_sku |
For variable products, the parent ships with is_parent: true and variation_attributes containing translated option names. Each variant ships separately with parent_sku set and variation_attributes: {}. Delete events contain only identification_number.
Customization
Every behavior customization goes through standard Symfony mechanisms: service decoration for replacing pipeline components, and event listeners for reacting to the sync lifecycle. No plugin code gets patched.
Service interfaces you can decorate
| Interface | What you can change |
|---|---|
ProductFormatterInterface |
Product and variant payload shape, custom attributes, price logic. |
PageFormatterInterface |
Page payload shape and custom fields. |
PageUrlResolverInterface |
URL generation for page entities. Default returns '' — decorate this to emit real URLs. |
OrderProviderInterface |
Order lookup logic, response format, verification fields. |
WebhookSenderInterface |
HTTP transport, retry policy, logging. |
Symfony events you can listen to
| Event | When it fires |
|---|---|
PreSyncEvent |
Before each entity is formatted. Cancel sync for specific entities. |
PostFormatEvent |
After the payload is formatted. Mutate the formatted events before they're queued. |
PreWebhookSendEvent |
Right before a batch is POSTed. Filter or adjust the batch. |
CartOperationEvent |
Before add, update, remove, or clear. Cancel or enforce business rules. |
OrderTrackingEvent |
Before the order tracking response returns. Add custom fields or redact data. |
Example: decorate the order provider
namespace App\Service;
use Emporiqa\SyliusPlugin\Service\OrderProviderInterface;
class CustomOrderProvider implements OrderProviderInterface
{
public function __construct(
private OrderProviderInterface $inner,
) {}
public function findOrder(string $identifier, ?string $userId, array $verificationFields): ?array
{
$order = $this->inner->findOrder($identifier, $userId, $verificationFields);
if ($order !== null) {
$order['loyalty_points'] = $this->pointsFor($identifier);
}
return $order;
}
}
# config/services.yaml
services:
App\Service\CustomOrderProvider:
decorates: Emporiqa\SyliusPlugin\Service\OrderProviderInterface
Example: mutate product data before send
namespace App\EventListener;
use Emporiqa\SyliusPlugin\Event\PostFormatEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener(event: PostFormatEvent::NAME)]
class ExtraProductFieldsListener
{
public function __invoke(PostFormatEvent $event): void
{
$events = $event->getFormattedEvents();
foreach ($events as &$webhookEvent) {
$webhookEvent['data']['sustainability_score'] = 'A+';
}
$event->setFormattedEvents($events);
}
}
Page sync setup
Implement Emporiqa\SyliusPlugin\Model\PageInterface on your page entity (the translations must expose getTitle(), getContent(), getSlug(), getLocale()), list its FQCN under page_entity_classes, and decorate PageUrlResolverInterface to emit real URLs. Without the decorator, the default resolver returns an empty string.
Full decoration examples for PageUrlResolverInterface, ProductFormatterInterface, and more live in the plugin README on GitLab.
Console Commands
All commands use memory-efficient batching and can run in reconciliation sessions — items not seen during the session can be marked deleted on the Emporiqa side.
| Command | What it does |
|---|---|
emporiqa:sync:all |
Full reconciliation of products and pages. |
emporiqa:sync:products |
Sync only products (and their variants). |
emporiqa:sync:pages |
Sync only page entities listed in page_entity_classes. |
emporiqa:test-connection |
Sends a real product in dry-run mode. Reports signature validation, detected languages, field coverage, and warnings. |
Shared flags
| Flag | Description |
|---|---|
--batch-size=50 |
Events per webhook request (default 50). |
--dry-run |
Format data without sending anything. Useful for validating output. |
--no-session |
Skip sync.start/sync.complete events. Use for incremental updates where you don't want deletion reconciliation. |
# Full reconciliation
bin/console emporiqa:sync:all
# Products only, small batches, no session markers
bin/console emporiqa:sync:products --batch-size=25 --no-session
# Validate payload without sending
bin/console emporiqa:sync:products --dry-run
# End-to-end connection test
bin/console emporiqa:test-connection -v
Order Tracking
Emporiqa can look up order status on behalf of customers during chat conversations. The plugin mounts a signed endpoint that Sylius responds to when a customer asks about their order.
Endpoint
POST /emporiqa/api/order/tracking
Flow
- Customer asks "Where is my order #000001234?" in the chat.
- Emporiqa POSTs a signed payload (HMAC-SHA256, via
X-Emporiqa-Signature) to the endpoint. - The
OrderProviderlooks up the order through Sylius'sOrderRepositoryInterface, verifies anyverification_fields(e.g. billing email), and maps payment/shipping state into a normalized status. - The salesperson presents the order information to the customer.
Setup
The endpoint is active as soon as the plugin is installed (unless order_tracking.enabled is false). To let Emporiqa call it, set the Order Tracking API URL in your dashboard (Store Settings → Integration) to:
https://your-store.com/emporiqa/api/order/tracking
Status mapping
| Sylius state | Returned status |
|---|---|
| Awaiting payment | pending_payment |
| Paid, not shipped | processing |
| Partially shipped | partially_shipped |
| Shipped | shipped |
| Refunded | refunded |
| Cancelled | cancelled |
Requests older than 300 seconds are rejected (replay protection). To customize lookup logic, decorate OrderProviderInterface — see Customization. See the Webhook Setup Guide for the full request/response schema.
Order tracking is optional. Disable it with order_tracking.enabled: false or leave the dashboard URL blank — order questions then route to the Customer Support agent instead.
Troubleshooting
Connection test fails
- Run
bin/console emporiqa:test-connection -vfor a verbose report. - Verify Store ID, webhook URL, and connection secret match the Emporiqa dashboard.
- Confirm your server can reach the webhook URL via outbound HTTPS.
- Check Symfony logs (
var/log/{env}.log) for HMAC or transport errors.
Products not syncing
- Confirm
sync.products: trueinconfig/packages/emporiqa.yaml. - Verify the product is enabled and has at least one enabled variant with a price.
- Trigger a full sync:
bin/console emporiqa:sync:products. - Clear cache after config changes:
bin/console cache:clear.
Pages not syncing
- Confirm your entity class FQCN is listed in
page_entity_classes. - Ensure the entity implements
Emporiqa\SyliusPlugin\Model\PageInterfaceand its translations exposegetTitle(),getContent(),getSlug(),getLocale(). - Run
bin/console emporiqa:sync:pagesand check the output. - If page URLs are empty, decorate
PageUrlResolverInterface.
Widget not appearing
- Confirm
{{ emporiqa_cart_widget() }}is in your shop layout before</body>. - Run
bin/console assets:installsoemporiqa-cart.jsis published topublic/bundles/emporiqa/js/. - View page source and look for the
<script async src="...emporiqa.com/chat/embed/...">tag. - Check the browser console for JavaScript errors.
Cart operations fail with 403
- Authenticated users need a CSRF token — fetch one from
GET /emporiqa/api/csrf-tokenand send it asX-CSRF-Token. - Confirm
cart.enabled: truein config. - Check that routes are imported via
config/routes/emporiqa.yaml.
Related Articles
Chat for Sylius
Symfony bundle architecture, event subscribers, service decoration.
Multilingual Chat Support
Cross-language search, 65+ languages, how translations sync.
5 Questions Before You Pay
Evaluation checklist: data sync, handoff, pricing, languages, attribution.
Conversion Tracking
Prove chat ROI: session to cart to purchase attribution.