Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Econt Pickup Point Bundle Laravel Package

answear/econt-pickup-point-bundle

Econt pickup point integration for Symfony: fetch offices and cities from the Econt nomenclatures API via simple command classes. Install with Composer, add credentials in config, and query offices or cities in a few lines of PHP.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with Composer and let Symfony Flex register the bundle (it adds Answear\EcontBundle\AnswearEcontBundle::class => ['all' => true] to config/bundles.php). Then supply credentials:

# config/packages/answear_econt.yaml
answear_econt:
    user: '%env(ECONT_USER)%'
    password: '%env(ECONT_PASSWORD)%'

Those two values are handed to \Answear\EcontBundle\ConfigProvider, which is the only real "configuration surface" in the package — everything else is code.

Where to look first, in this order:

  1. src/Command/GetCities.php and src/Command/GetOffices.php — these are the two public entry points, and both are plain constructor-injected services. Read their constructors to see exactly which collaborators (Guzzle client, ConfigProvider, serializer) you must supply if you ever instantiate them by hand.
  2. src/Request/GetCitiesRequest.php and src/Request/GetOfficesRequest.php — the only inputs. Note the cityId parameter on the offices request; its name was corrected in 3.0.0, so if you pin 2.x the wrong name is in play.
  3. src/Response/ — the DTOs you'll actually persist. OpeningHours is nullable (since 1.2.1), so build your schema accordingly.
  4. DependencyInjection/Configuration.php — confirms the exact config keys and any defaults the README omits (notably the Guzzle timeout added in 2.0.1).

First use case — the one this package exists for — is a pickup-point selector: fetch cities once, let the user choose one, fetch that city's Econt offices, render them as a radio list, and store the chosen office code on the order.

use Answear\EcontBundle\Command\GetCities;
use Answear\EcontBundle\Command\GetOffices;
use Answear\EcontBundle\Request\GetCitiesRequest;
use Answear\EcontBundle\Request\GetOfficesRequest;

public function __construct(
    private readonly GetCities $getCities,
    private readonly GetOffices $getOffices,
) {}

$cities  = $this->getCities->getCities(new GetCitiesRequest());
$offices = $this->getOffices->getOffices(new GetOfficesRequest($cityId));

One caveat before you start: despite the framing, this is not a Laravel package. It requires symfony/http-kernel and symfony/serializer, ships a Bundle class, and has no service provider. In a Laravel app you get nothing from Flex and nothing from the bundle registration — you'd be pulling two Symfony components in solely to construct three classes. For a Laravel codebase, treat this as reference source for the HTTP contract rather than something you install; for a Symfony codebase, the setup above is genuinely all there is.

Version floor for a new integration: ^4.2.1. That's the release that switched the API endpoint from http to https (see Gotchas) — there is no reason to adopt a version that sends Basic-auth credentials in cleartext.

Implementation Patterns

Wrap the commands, don't sprinkle them. Both commands are stateless services with a single method each. Put them behind your own EcontLocations service that owns caching, retries, and mapping, and inject that into controllers/console commands. This gives you one seam to fake in tests and one place to change when (not if) the package's DTOs shift.

Cache nomenclatures hard. Cities and offices change on the order of days-to-weeks, not per request. Two viable shapes:

  • Persist-and-sync (preferred): a scheduled console command calls getCities() and getOffices() per city, upserts into econt_cities / econt_offices, and your UI reads the local tables. Fast, sortable, searchable, works when Econt is down.
  • Cache-and-fetch: Cache::remember('econt.cities', now()->addDay(), fn () => $client->getCities(...)) plus a per-city offices key. Cheaper to build, but every cold key is a synchronous outbound call in a user's request path.

Whichever you pick, never call getOffices() on page render without a cache in front of it.

Sync in chunks, not in one job. Offices are per-city and Econt has thousands of cities. Loop the city list in a queued job with a modest batch size and a RateLimited/WithoutOverlapping middleware, rather than one long-running job that will hit your worker timeout or Econt's throttling.

Map to your own model immediately. Don't let Answear\EcontBundle\Response\* objects leak past your service boundary. Extract the stable identifiers (office code/id, city id, address parts, lat/lng if present, nullable opening hours) into your own DTO/entity, and keep the package objects ephemeral. This is what makes version bumps survivable on a package with one star.

Credentials per environment. ConfigProvider gets whatever you put in answear_econt.user/password. Bind those to %env()% and give each environment its own values — a demo/sandbox pair alongside production means you don't want a hardcoded default anywhere. Basic authorization only arrived in 1.2.2, so confirm the version you install actually authenticates rather than silently returning empty nomenclatures.

Explicit timeouts and one retry. 2.0.1 added a Guzzle timeout; verify the default in Configuration.php is sane for you, and if you construct the client yourself (the Laravel case, or when you want a custom handler stack) set timeout and connect_timeout explicitly and attach a retry middleware for 5xx/connection errors only. Nomenclature GETs are idempotent, so retries are safe.

Test against a MockHandler. Inject a Guzzle client built on MockHandler with recorded fixtures; never let CI hit ee.econt.com. Because the commands take their dependencies via constructor, this is a two-line override in a Symfony test container.

Assert the scheme in your own integration test. One line — $this->assertStringStartsWith('https://', (string) $request->getUri()) on a MockHandler-captured request — locks in the 4.2.1 fix and will fail loudly if a future pin or a hand-built base URI ever regresses to http.

Gotchas and Tips

  • PHP ^8.4 and Symfony ^7.0.3 are hard floors. This is the single biggest adoption blocker — it won't install into a Symfony 6 or PHP 8.2/8.3 app without --ignore-platform-reqs, which is not a fix. On a typical Laravel 10/11 app it's an outright conflict. Check composer.json before promising anyone an integration timeline.
  • Pre-4.2.1 versions called the API over plain HTTP. 4.2.1 ("https instead of http") is the release that moved the base URI to TLS. Given that these requests carry Basic-auth user/password from ConfigProvider, anything older transmits credentials and payloads in cleartext. Pin ^4.2.1 (or explicitly configure an https:// base URI if you hand-build the client in Laravel), and if you're already on 4.1.x or earlier this is a good enough reason to bump on its own. If you own the Guzzle client, also verify you didn't copy an http:// base URI from an older snippet into your own config.
  • Bundle registration does nothing outside Symfony. If you're in Laravel, AnswearEcontBundle is dead weight; you must hand-build ConfigProvider and the Guzzle client and new GetOffices(...) yourself. At that point, weigh whether the two Symfony component dependencies are worth it versus writing the ~50-line HTTP call.
  • cityId naming changed in 3.0.0. The parameter was renamed to match the API docs. Pin ^4.2.1 and don't trust older blog snippets or 2.x examples.
  • OpeningHours can be null. Made nullable in 1.2.1. Guard any rendering path — a pickup-point card that assumes an array will fatal on offices without published hours.
  • getResponse() only exists from 4.1.0. It exposes the underlying response object. Convenient for debugging and for reaching fields the DTOs drop, but it hard-couples your code to the transport. Prefer the DTOs; reach for this only as a last resort, and isolate it in one method.
  • Silent-empty failure mode. With a bad username/password or a rejected request, expect an empty nomenclature rather than an exception in some paths. Log the raw status and response body once at boot-level debugging when offices come back empty — otherwise you'll spend an afternoon on an auth typo.
  • Zero dependents, one star, single maintainer team. There's no community to lean on for answers and no third-party battle-testing. Pin an exact version, read src/ before trusting it, and wrap it behind your own interface so swapping it out is a one-file change. Note that the 4.1.0 → 4.2.1 hop spans an undocumented 4.2.0 as well as the https fix — read the diff between your pinned tag and the one you're moving to, since the notes only describe the final patch.
  • Extension points are thin. The intended path for new behavior is a PR upstream (the README explicitly invites it). Locally, your levers are: decorate the command services, replace ConfigProvider with your own, or skip the package for the raw endpoint. There is no event system, plugin hook, or configurable HTTP middleware.
  • The API it wraps isn't REST-native. Econt's nomenclature service is XML-flavored, and this bundle exists to hide that behind Symfony Serializer. When something looks wrong in a DTO, check whether the serializer's mapping is the culprit before blaming the API — and remember ext-json plus guzzlehttp/psr7: ^2.6 are hard requirements worth verifying in your lockfile when debugging odd transport errors.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style