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

S3Bundle Laravel Package

reconnect/s3bundle

Reconnect S3Bundle provides a streamlined PHP integration for Amazon S3, making bucket setup, uploads, downloads, and storage operations easier with minimal configuration. It helps Laravel/PHP applications manage S3-backed files without repetitive boilerplate code.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and pin to v3

composer require reconnect/s3bundle:^3.0

v3.0.0 is explicitly flagged as BC-breaking: the bundle class and the root configuration key were both renamed. Pin to ^3.0 (or an exact 3.0.x) rather than a floating constraint — the package has a single recent release and no dependents, so there's no crowd-sourced signal that a 4.0 would be painless.

2. Register the bundle (new class name)

// config/bundles.php
return [
    // ...
    Reconnect\S3Bundle\ReconnectS3Bundle::class => ['all' => true],
];

v2 registered Reconnect\S3Bundle\S3Bundle; v3 uses ReconnectS3Bundle. Watch the casing — class names in PHP are case-insensitive at runtime but composer's autoloader maps them case-sensitively on case-sensitive filesystems, so a copy-pasted ReConnectS3Bundle from older docs/READMEs will fail confusingly on Linux CI while working on macOS.

3. Discover the real config keys before guessing

The README is thin, so let the console tell you the truth:

php bin/console config:dump-reference reconnect_s3

The root key is reconnect_s3 (v2 used reconnect_s3_bundle). The release notes' own example uses bucketHost as the primary option, which is a good hint that the bundle's config shape is host-oriented rather than the bucket/region/key/secret shape you might assume from the AWS SDK:

# config/packages/reconnect_s3.yaml
reconnect_s3:
    bucketHost: '%env(BUCKET_HOST)%'
    # ...remaining keys exactly as revealed by the dump

Add the matching env entries to .env and never commit real credentials.

4. Upgrading an existing app from v2 — three renames

This is the bulk of the v3 work and it is entirely mechanical:

  • Reconnect\S3Bundle\S3Bundle::class → Reconnect\S3Bundle\ReconnectS3Bundle::class in config/bundles.php
  • root key reconnect_s3_bundle: → reconnect_s3: in the YAML
  • file config/packages/reconnect_s3_bundle.yaml → config/packages/reconnect_s3.yaml

Or let Flex do it, which is the documented path:

symfony console recipes:update reconnect/s3bundle

Then grep -rn "reconnect_s3_bundle\|S3Bundle::class" config/ src/ to catch stragglers (test fixtures, config/packages/*/ env overrides, infra repos that template the YAML).

5. Verify what got registered

php bin/console debug:container | grep -i s3
php bin/console debug:autowiring s3

That gives you the service ID (usually the class name, or an alias) to type-hint in a constructor.

6. First use case: store an uploaded file

The day-to-day entry point in Symfony is a controller pushing an UploadedFile at the configured client:

use Symfony\Component\HttpFoundation\File\UploadedFile;

public function store(UploadedFile $file, SomeS3Client $client): Response
{
    $key = 'uploads/' . uniqid('', true) . '.' . $file->guessExtension();
    $client->putObject([
        'Bucket'      => $this->getParameter('app.s3_bucket'),
        'Key'         => $key,
        'Body'        => fopen($file->getPathname(), 'rb'),
        'ContentType' => $file->getMimeType(),
    ]);

    return $this->json(['key' => $key]);
}

If the bundle exposes its own facade service rather than a raw Aws\S3\S3Client, inject that instead — using the configured service is the entire reason to take the dependency.

Implementation Patterns

Inject the configured service; never build clients by hand. The bundle's value is centralized configuration plus a pre-wired client. In a controller:

public function list(S3Client $s3): JsonResponse
{
    $result = $s3->listObjectsV2([
        'Bucket' => $this->bucket,
        'Prefix' => 'uploads/',
    ]);

    return $this->json(array_column($result['Contents'] ?? [], 'Key'));
}

In a service, constructor injection:

final class FileUploader
{
    public function __construct(private S3Client $s3, private string $bucket) {}
}

Bind the bucket in services.yaml:

services:
    App\Service\FileUploader:
        arguments:
            $bucket: '%env(BUCKET_HOST)%'

Presigned URLs are the highest-leverage day-to-day pattern. They offload bandwidth from PHP entirely — the browser or mobile client talks to S3 directly:

$cmd = $this->s3->getCommand('GetObject', [
    'Bucket' => $this->bucket,
    'Key'    => $key,
]);
$request = $this->s3->createPresignedRequest($cmd, '+15 minutes');
return (string) $request->getUri();

For direct uploads, use PutObject with the same pattern and a ContentType constraint in Params so callers can't upload arbitrary MIME types.

Async work with Messenger. Large transfers should happen in a worker that receives the object key plus metadata, not inside the HTTP request. This decouples upload latency from the user's response and gives you retries for transient S3 errors — which v3's rename does not help with, but which matters more than the bundle choice.

Local development. Point the client at MinIO or LocalStack via an env-specific override:

# config/packages/dev/reconnect_s3.yaml
reconnect_s3:
    # keys as revealed by config:dump-reference — e.g. a custom endpoint / path-style flag

If the dump doesn't expose an endpoint key, decorate the client service in dev rather than forking.

Rolling the v3 upgrade across an estate. Do the rename in one commit per repo, config file renamed with git mv so history follows it. Don't try to run a mixed fleet: v2's reconnect_s3_bundle key and v3's reconnect_s3 key are distinct roots, so a shared config template deployed to both is guaranteed to break one of them (Symfony fails container compilation on an unrecognized root key — a loud, safe failure, but a deploy blocker).

Bridging into Laravel. This remains a Symfony bundle and does not auto-register in Laravel. The v3 rename is a non-event for Laravel apps because you shouldn't be loading it there at all: register a service provider that builds the client with your Laravel config, or — usually better — use league/flysystem-aws-s3-v3 directly and skip the DI bridging entirely.

Testing. Use a fake S3Client for unit tests, and LocalStack in CI for round-trip assertions on put/get/presign. After the v3 upgrade, add one test that boots the kernel so a stale reconnect_s3_bundle.yaml or old bundle class fails in CI instead of at deploy.

Gotchas and Tips

  • v3.0.0 is a breaking release, not a feature release. Two renames: bundle class (S3Bundle → ReconnectS3Bundle) and config root (reconnect_s3_bundle → reconnect_s3). The recipe name for recipes:update is still reconnect/s3bundle — the package name did not change, only the class and config key.
  • Delete the old config file after renaming. Flex writes reconnect_s3.yaml and leaves reconnect_s3_bundle.yaml behind. Keeping both means the old file still declares the removed root key and container compilation fails with "There is no extension able to load the configuration for reconnect_s3_bundle".
  • Clear the container cache after the rename. php bin/console cache:clear. A stale compiled container referencing the old service class produces autowiring errors that look like the package is broken.
  • Class name casing. The canonical FQCN is Reconnect\S3Bundle\ReconnectS3Bundle. Older READMEs and blog posts render it ReConnect\S3Bundle\ReConnectS3Bundle; that breaks the composer autoloader on case-sensitive filesystems and on composer dump-autoload -o.
  • bucketHost ≠ bucket. The release notes' config example uses bucketHost, which is not an AWS SDK S3Client option name in the same way. Confirm the exact key set with config:dump-reference reconnect_s3 before copying any snippet, including this one.
  • Env var naming. The SDK's default provider chain picks up AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_DEFAULT_REGION automatically. The bundle's own keys (per the notes, BUCKET_HOST) are separate — don't assume the bundle reads the AWS-standard names.
  • recipes:update requires Symfony Flex and a clean working tree. Commit or stash first; it modifies config/bundles.php and writes a new YAML file, and you want that diff reviewable in isolation.
  • Bucket permissions. 403s are almost always IAM policy, not the client. s3:ListBucket is a bucket-level permission while s3:GetObject / s3:PutObject are object-level — granting only the object actions yields a working putObject and a mysteriously failing listing.
  • ContentType is your responsibility. Uploading without an explicit ContentType (via CLI or a handler that forgets it) leaves objects as application/octet-stream, which browsers download instead of display.
  • Presigned URL expiry caps at 7 days for SigV4. Longer values are silently clamped or rejected.
  • Stream large files. fopen($path, 'rb') for Body; file_get_contents on a multi-GB upload OOMs the worker.
  • Pagination. listObjectsV2 returns at most 1000 keys — use the paginator:
$paginator = $this->s3->getPaginator('ListObjectsV2', ['Bucket' => $this->bucket]);
foreach ($paginator as $page) { /* ... */ }
  • Debugging DI issues. php bin/console debug:autowiring s3, debug:container --env-vars, and debug:container --tag=container.service_subscriber are far faster than grepping compiled container files.
  • Extension points. Add logging, retries, or metrics by decorating the injected service rather than forking the bundle — but confirm the inner service ID first, since the rename suggests service IDs moved too:
services:
    App\S3\LoggingS3Client:
        decorates: <confirm via debug:container>
        arguments: ['@.inner', '@logger']
  • Migration is the whole release. There are no new features here, so the cost/benefit of upgrading is "do I want to stay on a supported version, and can I afford a three-line rename per app?" If you're mid-freeze, v2 keeps working; there's no security driver mentioned in the notes.
  • Fallback option. The underlying AWS SDK is trivial to wire yourself in services.yaml (~20 lines). The bundle's real value is config validation and a shared convention — weigh that against the churn of a major rename in a package with one release and zero dependents.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core