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

Rokka Imagine Bundle Laravel Package

liip/rokka-imagine-bundle

Symfony bundle integrating LiipImagineBundle with Rokka, so image transformations and delivery run through Rokka’s image CDN. Configure Rokka as the Imagine loader/cache to serve optimized, responsive images without local processing.

View on GitHub
Deep Wiki
Context7

Getting Started

This bundle is effectively unmaintained, but no longer frozen: release 1.2.0 added Symfony 5 support after a multi-year gap. Read that narrowly — one dependency-constraint bump is a compatibility patch, not resumed maintenance. There is no roadmap, no PHP 8.x statement, no LiipImagineBundle major bump, and no Flex recipe. Treat it as legacy code you are inheriting, not something to reach for on greenfield work.

Before writing any application code, do three things in order:

  1. Confirm it resolves against your exact stack. 1.2.0 targets Symfony 5, which in practice means liip/imagine-bundle 2.x — a different config shape from the Symfony 3/4-era README. Run composer why-not against your symfony/framework-bundle, liip/imagine-bundle, and php versions. If you are on Symfony 6 or 7, expect a conflict; stop and go straight to the migration path in Gotchas. If you are on Symfony 5, note that 5.4 left security support in November 2024, so "supported" here means "compatible with an EOL major."
  2. Read two files first. DependencyInjection/Configuration.php is the authoritative source for config keys — check whether the 1.2.0 tree moved to the resolvers/loaders naming or kept the older cache/data_loader keys, because the README examples likely predate the change. Cache/Resolver/RokkaResolver.php (or similarly named) tells you exactly what URL it emits and whether it uploads, caches, or just links. Also check whether the resolver implements Liip's newer ResolverInterface signature (getBrowserPath) or the legacy resolve — that determines whether it works at all on Liip 2.x.
  3. Get one image through end-to-end before touching filter sets. Register the bundle (config/bundles.php), set ROKKA_API_KEY / ROKKA_ORGANIZATION, point the Liip cache resolver at the rokka implementation, and render a single known-good hash from a stack you created in the rokka dashboard. Do this on a scratch branch so a failed install doesn't block anything.

The first genuinely useful use case is unchanged: you already have LiipImagineBundle filter sets (thumbnails, banners, responsive variants) and you want the rendered imagine_filter URLs to point at rokka's CDN instead of web/media/cache. That's the whole value proposition. Everything else — stack management, uploads, invalidation — you will still wire up yourself.

# config/packages/liip_imagine.yaml (shape varies by Liip version — verify)
liip_imagine:
    resolvers:
        rokka:
            rokka:
                api_key: '%env(ROKKA_API_KEY)%'
                organization: '%env(ROKKA_ORGANIZATION)%'
                stack: 'thumbnails'
    cache: rokka
    filter_sets:
        thumb:
            filters:
                thumbnail: { size: [120, 90], mode: outbound }

Twig usage is unchanged — that's the point:

<img src="{{ image.hash|imagine_filter('thumb') }}">

If the generated URL is not https://<org>.rokka.io/<stack>/<hash>?..., you are not actually hitting the resolver; debug the service wiring before debugging anything else.

Implementation Patterns

One stack per filter set. Rokka stacks are named, ordered lists of operations. The cleanest mapping is a 1:1 relationship between a Liip filter set and a rokka stack name, so thumb → stack thumb, banner → stack banner. This keeps the Liip config readable, makes cache invalidation per-variant obvious, and lets you change a stack's operations server-side without redeploying the application.

Keep filter definitions minimal and let rokka do the work. The 1.2.0 bump did not add filter translation, so the converter is still the old one. Anything exotic (custom Imagine filters, PHP closures, watermarking with local fonts) either won't translate or will silently diverge from what you'd get locally. Prefer standard operations — resize, crop, autoformat, dpr, quality — and verify visual output against a local Imagine render at least once per filter set.

Treat upload as a separate concern. A resolver emits URLs; it does not put bytes in rokka. The data loader path is fragile (hash must match rokka's SHA-1-of-contents expectation), and on Liip 2.x the data-loader interface changed, so don't count on it. In practice, upload explicitly on write:

final class UploadImageToRokka
{
    public function __construct(private RokkaClient $client) {}

    public function __invoke(string $localPath, string $name): string
    {
        $image = $this->client->getImageService()
            ->upload($localPath, $name, $this->client->getUser(), true /* override */);

        return $image->getHash();
    }
}

Do this from a Doctrine listener, a Messenger handler, or an admin action — anywhere that has the file and can fail loudly. Store the returned hash on the entity and render from that. This is the single most important integration decision: do not depend on the bundle to upload lazily.

Persist hashes, not paths. Once images live in rokka, the hash is your stable identifier. Entity columns like image_hash (string, indexed) let you build URLs without filesystem access, which is what makes the CDN worth using. Keep the original filename in a separate column for humans.

Invalidate by hash, not by URL. Rokka caches by content hash and stack. Changing a stack's operations changes the URL query string, so old variants naturally fall out of use. To force a real purge, use the rokka client directly (deleteImage, or re-upload with override), not the bundle's cache-removal command — the Liip liip:imagine:cache:remove semantics assume a filesystem resolver and may no-op.

Test the URL contract, not the image. Unit tests should assert the resolver produces the expected https://<org>.rokka.io/<stack>/<hash> shape for given config. Visual regression against live rokka is flaky and rate-limited; run it in a scheduled job, not in CI on every PR.

Scope the API key. Rokka keys can be read-only or read-write per organization. Create two: a write key for the upload path (server, never exposed) and a read-only key for anything that only generates URLs. Rotate via secret manager, not .env committed to the repo.

Gotchas and Tips

  • "1.2.0 supports Symfony 5" is a narrow claim — test the edges yourself. The release note names one framework major and nothing else. Verify what happened to the php constraint (a Symfony 5 bump often rides on ^7.2, which would leave PHP 8.2/8.3 untested), and whether the rokka/client and liip/imagine-bundle constraints moved with it. Pin exact versions once you've found a working combination and vendor the diff if you need a one-line fix — one release does not mean a maintainer is watching the issue tracker.
  • The Symfony 5 bump almost certainly pairs with LiipImagineBundle 2.x, and the README hasn't caught up. Liip renamed resolvers/loaders/cache/data_loader across majors and changed the resolver and data-loader interfaces. If you copy README config onto Liip 2.x, keys get silently ignored and you'll get local filesystem cache paths while assuming the resolver is broken. Read Configuration.php and mirror the method signatures in the resolver class.
  • No path off Symfony 5 inside the bundle. Symfony 5.4 is out of security support. If your platform is 6.2+/7.x, 1.2.0 doesn't help you and won't be patched to — plan the exit now rather than waiting for a 1.3.0.
  • 401/404 taxonomy. 401 almost always means wrong API key or wrong organization. 404 on an image means the hash doesn't exist in that org — usually because the upload path wasn't run, or the hash was computed locally and differs. Log the full URL and the hash on failure; the bundle's exceptions are terse.
  • Hash mismatch from re-encoding. If you generate the hash from a processed (resized, stripped) file but upload the original, URLs will 404. Hash the exact bytes you upload, or take the hash the upload response returns — never compute it yourself.
  • Filter sets that "work locally" but render differently on rokka. EXIF orientation, color profile handling, and mode: outbound semantics differ between Imagine and rokka operations. Pick one source of truth per filter and spot-check portrait-oriented photos with EXIF rotation.
  • liip:imagine:cache:resolve / :remove are misleading here. They're built around Liip's filesystem cache. With the rokka resolver they may partially work or no-op. Don't build deploy pipelines around them; pre-warm by hitting the URLs or uploading explicitly.
  • Private images need signed URLs. Neither the 2020 release nor 1.2.0 addresses rokka's private-image support. If you need signed/expiring URLs, bypass the resolver and sign with the rokka client yourself in a Twig extension — treat the bundle as a URL formatter only.
  • No Flex recipe, no auto-wiring. Register the bundle manually in config/bundles.php and confirm the service IDs (liip_imagine.cache.resolver.rokka or similar) with debug:container --show-private | grep -i rokka. If the service isn't registered, the config was wrong upstream — most often a key name from the Liip 1.x era.
  • Extension points are thin but usable. The cleanest customization is to decorate the resolver service (Symfony decorates:) and rewrite the returned URL — for example to add format negotiation (?fm=auto) or signing. A compiler pass can also replace the stack-operation converter if you need a filter it doesn't understand.
  • Watch stack drift. Stacks are configured on rokka's side; if someone edits a stack in the dashboard, production behavior changes with no deploy. Export stack definitions to versioned YAML and apply them via the rokka client in a deploy step so infrastructure is reproducible.
  • The migration path is now the default recommendation for new work. Either a ~100-line custom Liip cache resolver that talks to the rokka SDK directly, or move URL generation into a Twig extension and drop LiipImagine for these paths. Both are smaller than they sound, both work on Symfony 6/7, and both remove a dependency whose only recent activity was a single compatibility bump.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package