agencednd/criteo-connector-bundle
Installation
composer require agencednd/criteo-connector-bundle
Ensure your Akeneo PIM version is 2.x (as per composer.json constraints).
Enable the Bundle
Add to config/bundles.php:
return [
// ...
AgenceDnd\CriteoConnectorBundle\AgenceDndCriteoConnectorBundle::class => ['all' => true],
];
Configuration
Override default settings in config/packages/agence_dnd_criteo_connector.yaml:
agence_dnd_criteo_connector:
client_id: '%env(CRITEO_CLIENT_ID)%'
client_secret: '%env(CRITEO_CLIENT_SECRET)%'
api_endpoint: 'https://api.criteo.com/2024-01/' # Adjust per Criteo API version
product_mapping:
- { akeneo_field: 'reference', criteo_field: 'product_id' }
- { akeneo_field: 'price', criteo_field: 'price' }
First Export Trigger via CLI:
php bin/console agence-dnd:criteo:export --products=100
Verify output in var/log/criteo_export.log.
sku → product_id, price → price).ProductExporter service to fetch and transform data:
$exporter = $container->get('agence_dnd_criteo_connector.exporter.product');
$products = $exporter->export(['reference' => 'PROD-001'], ['price', 'name']);
CriteoClient:
$client = $container->get('agence_dnd_criteo_connector.client');
$response = $client->postProducts($products);
Cron Job Integration
Schedule exports via crontab (e.g., daily at 2 AM):
0 2 * * * php /path/to/your/project/bin/console agence-dnd:criteo:export --products=all --dry-run=false
Batch Processing For large catalogs, chunk exports:
# config/packages/agence_dnd_criteo_connector.yaml
agence_dnd_criteo_connector:
batch_size: 500 # Default: 100
Event-Driven Updates
Listen to Akeneo’s ProductUpdateEvent to trigger incremental exports:
// src/EventListener/CriteoSyncListener.php
use AgenceDnd\CriteoConnectorBundle\Event\ProductExportedEvent;
class CriteoSyncListener {
public function onProductUpdate(ProductUpdateEvent $event) {
$this->dispatch(new ProductExportedEvent($event->getProduct()));
}
}
Akeneo PIM Flows
Extend the bundle’s ExportProductsFlow to add pre-processing:
<!-- config/flows/export_to_criteo.xml -->
<flow name="export_to_criteo">
<step name="filter_active_products" type="pim_connector.export.filter">
<argument name="filter" type="service" id="pim_catalog.filter.product" />
</step>
<step name="map_to_criteo" type="agence_dnd_criteo_connector.map" />
</flow>
Custom Field Mappings Override the default mapper:
// src/Service/CriteoProductMapper.php
class CustomCriteoProductMapper extends AbstractProductMapper {
public function map($product, array $fields) {
$data = parent::map($product, $fields);
$data['custom_field'] = $product->getCustomAttribute(); // Extend logic
return $data;
}
}
Register as a service:
services:
agence_dnd_criteo_connector.mapper.product:
class: App\Service\CustomCriteoProductMapper
tags: ['agence_dnd_criteo_connector.mapper']
API Rate Limits
429 Too Many Requests.CriteoClient:
if ($response->getStatusCode() === 429) {
sleep(2 ** $retryCount); // Exponential delay
}
Field Name Mismatches
product_id), but Akeneo uses camelCase (e.g., reference).ProductExporter:
if (!preg_match('/^[a-z_]+$/', $criteoField)) {
throw new \InvalidArgumentException("Criteo field '$criteoField' must be snake_case.");
}
Missing Required Fields
product_id, price, and availability. Skip invalid products:
// In ProductExporter.php
if (empty($product['product_id']) || empty($product['price'])) {
$this->logger->warning('Skipping invalid product: ' . $product['reference']);
continue;
}
Enable Verbose Logging
php bin/console agence-dnd:criteo:export --verbose
Check var/log/dev.log for raw API responses.
Dry Run Mode Test mappings without sending data:
php bin/console agence-dnd:criteo:export --dry-run=true
Custom Validation
Add pre-export validation via the ProductExporter interface:
public function validateProduct(array $product): bool {
return $product['price'] > 0 && $product['availability'] === 'in_stock';
}
Webhook Integration
Listen for Criteo’s product_update webhooks to sync changes back to Akeneo:
// src/EventListener/CriteoWebhookListener.php
use Symfony\Component\HttpKernel\Event\RequestEvent;
class CriteoWebhookListener {
public function onKernelRequest(RequestEvent $event) {
if ($event->isMainRequest() && $event->getRequest()->getPathInfo() === '/criteo/webhook') {
$data = json_decode($event->getRequest()->getContent(), true);
$this->handleCriteoUpdate($data);
}
}
}
Multi-Store Support Extend the bundle to handle store-specific feeds:
# config/packages/agence_dnd_criteo_connector.yaml
agence_dnd_criteo_connector:
stores:
- { name: 'US Store', channel: 'us', currency: 'USD' }
- { name: 'EU Store', channel: 'eu', currency: 'EUR' }
How can I help you explore Laravel packages today?