zicht/framework-extra-bundle
Zicht Framework Extra Bundle packages reusable Symfony extras—routing conveniences, controller helpers, and service wiring—that cut boilerplate in Zicht web applications. A lightweight set of practical add-ons for everyday Symfony framework work.
What you're actually installing. zicht/framework-extra-bundle is a grab-bag of Symfony utilities rather than a single feature. Think of it the way you'd think of a laravel/framework "Support" package: small traits, base classes, helper services, and compiler passes that save boilerplate. It is not a replacement for Symfony's own FrameworkBundle, and it does not ship a data layer or admin UI. Because the repository has no stars and zero dependents, treat the first hour as an audit, not an integration.
Minimal install:
composer require zicht/framework-extra-bundle
Then register it. On Symfony Flex this is usually automatic via the recipe; if not, add it to config/bundles.php:
return [
// ...
Zicht\Bundle\FrameworkExtraBundle\ZichtFrameworkExtraBundle::class => ['all' => true],
];
Where to look first, in this order:
vendor/zicht/framework-extra-bundle/composer.json — read require and require-dev to learn the minimum Symfony version and which sibling zicht/* packages are assumed. This is the single best compatibility signal.
vendor/zicht/framework-extra-bundle/README.md and any docs/ folder.
vendor/zicht/framework-extra-bundle/src/ — list the directory tree before reading a single file:
find vendor/zicht/framework-extra-bundle/src -maxdepth 2 -type d | sort
The subdirectory names tell you the feature set (typically controller/response helpers, request utilities, form or Doctrine helpers, Twig extensions, and a DependencyInjection/ folder with Configuration.php and compiler passes).
src/DependencyInjection/Configuration.php — the authoritative list of config keys, their defaults, and which are required. Read this before writing any YAML.
tests/ — even a thin test suite shows the intended call signatures. If there are no tests, downgrade your trust accordingly.
First use case. The highest-value, lowest-risk entry point in a bundle like this is the HTTP response/controller helpers: returning JSON, no-content, or redirect responses without hand-rolling new JsonResponse(...) in every action. Open src/Controller/ (if present), find the abstract base controller or the response trait, and migrate exactly one controller to it. That gives you a concrete diff to evaluate before touching anything else.
Extend the base controller, don't replace yours. If the bundle ships an abstract controller, the safe pattern is composition-with-inheritance only where your app has a single, already-abstract AbstractController of its own:
// App\Controller\AbstractAppController extends Zicht's base
abstract class AbstractAppController extends \Zicht\Bundle\FrameworkExtraBundle\Controller\AbstractController
{
// your cross-cutting helpers live here
}
Never let two competing base controllers exist in the same codebase. Pick one and route all controllers through it, otherwise helpers become discoverable only by memory.
Response helpers as a convention. Use the bundle's helpers consistently for the response shapes you return most often — JSON for API endpoints, empty-body 204s for delete verbs, RedirectResponse for post/redirect/get. Consistency is the real payoff: controllers become scannable, and if you later swap the helper implementation for your own, you change one class.
Request helpers for content negotiation. If the bundle provides request helpers (acceptable content types, locale resolution, forwarded-header handling), wire them in at the edge — a kernel request listener or a small service — rather than calling them ad hoc in controllers. Laravel developers should read these as middleware-adjacent concerns.
Service decoration over compiler-pass hacking. Bundles of this vintage often register services with hard-coded IDs and no autowiring aliases. Rather than fighting a compiler pass, decorate the service:
# config/services.yaml
services:
App\Service\MyWrapper:
decorates: zicht_framework_extra.some_service
arguments: ['@.inner']
This survives upstream changes far better than setArgument() in a compiler pass. Reserve passes for genuine compile-time needs (tag collection, class rewriting).
Twig extensions, if present. Register them through the bundle and confirm they appear:
bin/console debug:twig --filter=<extension_name>
Then constrain your usage to one or two filters/functions. Twig extensions are where "convenience bundles" accumulate the most accidental API surface.
Configuration discipline. Publish the bundle's defaults into config/packages/zicht_framework_extra.yaml explicitly, even if you only set one key. An explicit config file documents your dependency on the bundle and makes upgrades diffable. Use bin/console config:dump-reference zicht_framework_extra to get the full tree with defaults.
Testing pattern. If the bundle's helpers are static or container-dependent, isolate them behind your own thin service so unit tests don't need a booted kernel. Anything you can't unit test without a kernel is a candidate for a decorator.
Evaluate the maintenance signal honestly. Zero stars, zero dependents, and no visible release cadence means: no community to catch regressions, no Stack Overflow answers, and no guarantee of a Symfony 7/8 update path. Before adopting, check the composer.json symfony/* constraints and the date of the last commit. On anything long-lived, pin an exact version ("zicht/framework-extra-bundle": "1.4.2") rather than a caret range.
Bundle registration order matters more than you expect. Bundles that manipulate routing or Twig can silently lose to FrameworkBundle or TwigBundle if registered after them. If helpers 404 or extensions don't resolve, reorder in config/bundles.php before debugging anything else.
Check for autowiring aliases. Older Symfony bundles register services with IDs only. Symptom: Cannot autowire service ... argument $foo references interface ... but no such service exists. Fix with an explicit alias in config/services.yaml:
services:
Zicht\Bundle\FrameworkExtraBundle\SomeInterface: '@zicht_framework_extra.some_service'
Empty config is the default and it hides requirements. Configuration.php frequently declares keys with null defaults that blow up at runtime, not compile time. Run config:dump-reference and then bin/console lint:container after enabling the bundle — the latter catches missing arguments that only appear on the first request otherwise.
Method-name collisions on controller helpers. If your own AbstractController also defines json() or redirect(), PHP will fatal on incompatible signatures. Grep your base classes for the helper names the bundle exposes before extending it.
Deprecations are your upgrade map. Run the test suite with the deprecation helper enabled (SYMFONY_DEPRECATIONS_HELPER=weak) and read the log for zicht paths. In an unmaintained bundle, deprecations you see today are removals you'll have to patch yourself tomorrow — capture them in a ticket immediately.
Know your exit ramp. For every helper you adopt, note in the PR description what the two-line inline replacement would be (new JsonResponse($data, $status)). Bundles with this risk profile should be adopted as thin conveniences only, so ripping them out is an afternoon, not a sprint.
Debugging entry points:
bin/console debug:container zicht # what services exist
bin/console debug:router # did the bundle add routes?
bin/console debug:twig # are extensions registered?
bin/console lint:container # argument/alias errors
Extension points to prefer. If the bundle offers interfaces for its helpers, implement them in your app and let the bundle consume yours — that inverts the dependency and makes a future swap trivial. Avoid subclassing concrete bundle classes; that's the pattern that makes removal painful.
How can I help you explore Laravel packages today?