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

Platform Bundle Laravel Package

softspring/platform-bundle

Softspring Platform Bundle provides the shared core layer for Softspring CMS applications: common services, configuration, and integration points that let modules and extensions build on a consistent, maintainable PHP foundation.

View on GitHub
Deep Wiki
Context7

Getting Started

First, resolve the framework question — it determines everything else. softspring/platform-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no config/*.php to publish, no artisan command. The -bundle suffix plus the Softspring CMS/e-commerce family means a *Bundle class, a DI extension, services.yaml compilation, and Symfony's event dispatcher. In a Laravel app, composer require softspring/platform-bundle will pull symfony/dependency-injection, symfony/http-kernel, and friends into your vendor tree without ever booting them — cost with zero benefit.

So pick your path before installing anything:

  • Path A — Symfony host app. The bundle is a plausible candidate. Proceed below.
  • Path B — Laravel app with the bundle in a sidecar. A small Symfony service owns the integration; Laravel calls it over HTTP or a queue. Viable, but evaluate it as a service decision, not a library adoption.
  • Path C — Laravel app, borrow the pattern. Read the source, port the adapter shape into a Laravel service class. This is what most teams will actually do, and it is the lowest-risk outcome.

If you have a Symfony host

  1. Install and register:

    composer require softspring/platform-bundle
    

    With Flex this is automatic. Otherwise add to config/bundles.php:

    Softspring\PlatformBundle\SoftspringPlatformBundle::class => ['all' => true],
    
  2. Read these four things, in this order. Documentation is thin at this adoption level, so the source is the spec:

    • composer.json — PHP range, Symfony range, transitive dependency count. This is your fastest fail-fast check.
    • src/DependencyInjection/Configuration.php — the config tree. Authoritative list of every option, its type, default, and required flag. Everything downstream is defined here.
    • src/DependencyInjection/*Extension.php — what services get registered, aliased, or left public.
    • src/Resources/config/ — service wiring, routes, and tagged services (event subscribers, handlers, commands).
  3. First use case — boot, don't integrate. Before touching a real platform:

    bin/console lint:container
    bin/console debug:config softspring_platform
    bin/console debug:container --tag=<whatever tags the bundle declares>
    

    If the container compiles and you can dump the resolved config, you have de-risked the majority of the unknowns. Only then attempt one read-only call against a sandbox account.

If you have a Laravel host

Your first use case is a timeboxed spike in a throwaway Symfony skeleton, not an install into your app. Two to five days: does the bundle boot, resolve its dependency tree, and reach one external platform end-to-end for read? If it fails at dependency resolution — the single most likely failure — you have your answer for a fraction of the cost of a port.

Implementation Patterns

Pattern 1 — Config as the integration contract. The bundle's real day-to-day value in a Symfony app is that platform credentials, endpoints, and sync settings live in one validated config tree instead of scattered env() calls. Lean on it:

# config/packages/softspring_platform.yaml
softspring_platform:
    platforms:
        acme:
            credentials:
                key: '%env(ACME_KEY)%'
                secret: '%env(ACME_SECRET)%'

Keep secrets in env, never in the tree. The win is that a missing or mistyped option fails at container compile / cache warmup, not at 3am in a queue worker.

Pattern 2 — Sidecar boundary for Laravel. If you go Path B, make the seam an explicit, versioned contract rather than an RPC to "whatever the bundle does":

// Laravel side — one interface, N thin implementations
interface PlatformConnector {
    public function push(Order $order): SyncResult;
    public function pull(string $cursor): SyncBatch;
}

The Symfony service exposes POST /sync/push, GET /sync/pull?cursor=…. Laravel owns the domain model; Symfony owns nothing but the outbound call and credential handling. Log the correlation ID on both sides or you will never debug a partial sync.

Pattern 3 — Port the adapter shape (the pattern you actually want). Steal the structure even if you don't take the dependency:

final class AcmeConnector implements PlatformConnector
{
    public function __construct(
        private readonly ClientInterface $http,
        private readonly AcmeCredentials $creds,
        private readonly LoggerInterface $log,
    ) {}

    public function push(Order $order): SyncResult
    {
        $idempotencyKey = hash('sha256', $order->id.':'.$order->updatedAt);

        return retry(3, 200, fn () => $this->http->post('/orders', [
            'headers' => ['Idempotency-Key' => $idempotencyKey],
            'json'    => $this->map($order),
        ]));
    }
}

Three things this buys you that the bundle may or may not: a credential object that isn't a stringly-typed array, idempotency keys derived from domain state, and retry with backoff at the boundary rather than sprayed through call sites.

Pattern 4 — Queue boundary in front of every connector. Regardless of which path you take, the connector should be a job payload, not an inline call in a controller. External platform latency and rate limits become worker-tier problems instead of user-facing ones, and retries get a natural home.

Pattern 5 — Tagged services for platform fan-out. If the bundle (or your port) registers connectors as tagged services, adding a platform is a class + a tag, and the dispatcher iterates the tag. In Laravel the equivalent is a container tag plus tagged() / a registry keyed by a supports() method. This is the pattern that converts N bespoke integrations into one loop.

Day-to-day workflows to establish:

  • Config changes go through the config tree; never read env() directly inside integration code.
  • debug:container and debug:config before debug:dump-style guesswork.
  • A sandbox account per platform, wired to a CI job that exercises one read and one write per connector per night.
  • Integration tests that assert idempotency (same input twice → one remote record), not just happy-path success.

Gotchas and Tips

The framework mismatch is the headline gotcha, not a footnote. Do not spend a sprint trying to make a Symfony bundle behave in Laravel. The failure modes — container bootstrapping conflicts, duplicate PSR-11 registrations, Symfony kernel lifecycle assumptions — are structural, not configurable. Decide sidecar vs. port vs. no-go up front and write the decision down.

Container visibility bites. Services registered by a third-party bundle are often private by design. If you need to pull one out in a controller or command, prefer injecting it over $container->get(); if you must, alias it explicitly in your own services.yaml and note it, because the next minor release can change visibility without warning.

Config defaults hide required-ness. A Configuration.php with generous defaults means a misconfigured platform fails deep inside a request rather than at compile time. Skim the tree for options with no default and no isRequired() guard — those are your runtime landmines. Add your own guard in a small compiler pass or a boot-time validator rather than trusting documentation.

Flex auto-registration is silent. If the bundle registers via Flex, it's in config/bundles.php and you may not notice it until it starts wiring event subscribers. When debugging "why is this listener firing," check bin/console debug:event-dispatcher before blaming your own code.

Version matrix is unknown, so pin aggressively. With no stated BC policy and no dependents to crowdsource compatibility knowledge, treat every composer update as a managed event. Pin to a tagged release (or commit SHA if there are no tags), commit composer.lock, and name an internal owner. Verify there are actually tagged releases before assuming this is possible.

Credential handling needs a security review before production. The bundle is in the credential-handling domain. Confirm where tokens land — secret store, env, or plaintext config — and whether they are logged on failure. Symfony's #[Autowire(env: ...)] and secrets: vault are the sane answers; a var_dump in an exception handler is the common one.

Idempotency and duplicate webhooks are yours, not the bundle's. Assume webhook delivery is at-least-once and that the bundle does not deduplicate. Derive an idempotency key from your own domain state and store a processed-set. Silent partial-sync failures are the nastiest class of bug here — if the bundle logs and swallows, you get quiet data divergence. Assert row counts or checksums after each sync.

Upstream schema drift will outpace the package. When the external platform versions its API, a zero-adoption bundle has no crowd to file the issue. Keep the mapping layer (map() above) isolated in one class per platform so a version bump is a targeted change, not a sweep.

Exit plan from day one. Given the adoption signal, write down: the fork URL, the pinned commit, the named owner, and the criteria under which you abandon the dependency. If there is no commercial support from Softspring, escalation is GitHub issues with no SLA — unacceptable on a revenue or compliance path without a documented fallback.

Debugging order that actually works: lint:container → debug:config softspring_platform → debug:container --show-private → read the extension class → add an X-Correlation-Id header at the boundary and log it on both ends. Most "the bundle isn't doing anything" reports terminate at step two.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin