yproximite/ekomi-api-bundle
A Symfony bundle that integrates the Ekomi review platform API into your PHP application, letting you manage customer and product reviews, submit order data, and fetch ratings with a clean, configurable service layer.
1. Register the bundle manually. There is no Flex recipe and the package has effectively no install base, so nothing is auto-discovered:
composer require yproximite/ekomi-api-bundle
// config/bundles.php
return [
// ...
Yproximite\EkomiApiBundle\EkomiApiBundle::class => ['all' => true],
];
Before you write any config, open vendor/yproximite/ekomi-api-bundle/DependencyInjection/Configuration.php. That file is the only reliable documentation for the config key and credential fields — the README is thin and the last release was January 2020. Confirm the service IDs in Resources/config/services.xml and autowirability with:
bin/console debug:container --show-private | grep -i ekomi
bin/console config:dump-reference <bundle_config_key>
2. Configure credentials per environment and keep them out of the repo:
# config/packages/ekomi_api.yaml
<vendor_key>:
# interface/shop ID + API password, per Configuration.php
api_id: '%env(EKOMI_API_ID)%'
api_password: '%env(EKOMI_API_PASSWORD)%'
EKOMI_API_ID=
EKOMI_API_PASSWORD=
3. First use case — pull your latest reviews. Don't start with order submission; start with a read-only call in a console command, because it proves credentials and network reachability without writing data into eKomi:
#[AsCommand(name: 'app:ekomi:reviews')]
final class FetchReviewsCommand extends Command
{
public function __construct(private EkomiClient $client) { parent::__construct(); }
protected function execute(InputInterface $in, OutputInterface $out): int
{
$response = $this->client->getReviews(); // verify method name on the client class
$out->writeln(json_encode($response, JSON_PRETTY_PRINT));
return Command::SUCCESS;
}
}
If autowiring the client class fails, wire the bundle's concrete service ID explicitly in services.yaml. Once the command returns data, you understand the auth, the charset, and the response shape — that's the whole battle with this bundle.
Wrap the client in your own gateway. The bundle is a thin DI shell around the eKomi v3 HTTP client. Injecting the vendor client directly into controllers or listeners couples your app to an archived package. Define one boundary class:
final class EkomiGateway
{
public function __construct(private EkomiClient $client, private LoggerInterface $logger) {}
public function pushOrder(Order $order): void
{
try {
$this->client->putOrder($this->mapOrder($order));
} catch (\Throwable $e) {
$this->logger->error('eKomi order push failed', ['order' => $order->getId(), 'e' => $e]);
throw $e;
}
}
}
When the bundle eventually breaks, you swap the internals of this one class.
Push orders asynchronously, after commit. eKomi order submission is an outbound HTTP call to a third party; never do it inline in the checkout request. Use an event listener on your order-placed domain event that dispatches a Messenger message, and handle it with retries:
#[AsMessageHandler]
final class PushOrderToEkomiHandler
{
public function __construct(private EkomiGateway $ekomi) {}
public function __invoke(PushOrderToEkomi $message): void
{
$this->ekomi->pushOrder($message->orderId);
}
}
This also gives you free retry/backoff and a dead-letter path for eKomi outages, which the bundle itself does not provide.
Cache review reads. Review data changes on the order of hours. Put a thin cache in front of getReviews() keyed by shop + pagination args and use a stale-while-revalidate pattern. This is the single cheapest way to make the package feel production-grade — the bundle has no caching layer.
Normalize responses into your own DTOs at the gateway boundary rather than passing raw arrays/stdClass up the stack. eKomi v3 responses are loosely typed and field names are easy to typo; a DTO plus a fromApi() factory gives you one place to fix when the payload shifts.
Multi-shop / multi-account. If you run several shops, you generally cannot reuse the single auto-configured client. Define named service instances by hand (same class, different constructor args) and inject them by alias, or construct the underlying client directly in a factory service and let the bundle's client serve only the default shop.
Testing. Because the bundle is DI-only, mock at your gateway interface, not at the vendor client. For a genuine integration check, use Symfony's MockHttpClient against the underlying HTTP transport, and put a contract-style test around the response parsing so a vendor change surfaces as a failing test rather than a silent production bug.
composer.json constraints first; on a modern stack the install will likely fail and you should not "fix" it with --ignore-platform-reqs. The realistic options are (a) fork and patch the constraints, or (b) drop the bundle and call the eKomi v3 endpoints from a plain Symfony HttpClient/Guzzle service — the wrapper is small enough that reimplementing it is often cheaper than carrying it.auth parameter before you paste values into env vars. A swapped pair or a stray whitespace/newline in .env produces a generic auth failure with no useful message.null with no context.debug:container --show-private | grep -i ekomi for service IDs, config:dump-reference for config keys, and debug:event-dispatcher to confirm your listener is actually registered. Most "the bundle doesn't work" reports are an unregistered bundle or an unpopulated env var, in that order.decorates: on your gateway or on the vendor client) and compiler-pass overrides of the bundle's service definitions. Avoid subclassing the vendor client; you'll be pinned to the archived API surface."yproximite/ekomi-api-bundle": "1.x.y") and commit the lockfile. With zero dependents and no maintenance, a loose constraint resolving to a different transitive Guzzle/HTTP-client major is a realistic way to break production on an unrelated composer update.How can I help you explore Laravel packages today?