PHPackages                             survos/kit-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. survos/kit-bundle

ActiveSymfony-bundle[Framework](/categories/framework)

survos/kit-bundle
=================

Base classes and traits for consistent Survos bundle development — auto-registers twig namespaces, asset mapper paths, and doctrine mappings by convention

2.7.2(3w ago)0104↑83.3%20MITPHPPHP ^8.4

Since May 19Pushed 2d agoCompare

[ Source](https://github.com/survos/kit-bundle)[ Packagist](https://packagist.org/packages/survos/kit-bundle)[ GitHub Sponsors](https://github.com/kbond)[ RSS](/packages/survos-kit-bundle/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (2)Dependencies (14)Versions (20)Used By (20)

survos/kit-bundle
=================

[](#survoskit-bundle)

Convention-based base class for Symfony 8 bundle development.

The goal is simple: if a Survos bundle has conventional Symfony code, installing the bundle should make that code available without a recipe full of repeated wiring.

- `src/Command/` classes with `#[AsCommand]` should become console commands.
- `src/Controller/` classes with `#[Route]` should be routable when the bundle opts into route loading.
- `templates/` should be available as a Twig namespace.
- `assets/` should be available to AssetMapper when the bundle declares an asset package.
- `src/Entity/` can be mapped into Doctrine when the bundle explicitly opts in.

Doctrine deserves special attention: when a bundle maps entities, installing that bundle changes what Doctrine considers part of the application model. That is useful for bundles like `key-value-bundle`, but it means the app developer must review and apply schema changes.

---

Two audiences
-------------

[](#two-audiences)

This document distinguishes two roles:

- **Bundle author** — writes the bundle (extends `AbstractSurvosBundle` or `AbstractUxBundle`, ships it as a package)
- **App developer** — installs and configures the bundle in their Symfony application

---

For bundle authors
------------------

[](#for-bundle-authors)

### Extend `AbstractSurvosBundle`

[](#extend-abstractsurvosbundle)

```
use Survos\Kit\AbstractSurvosBundle;
use Survos\Kit\Traits\HasDoctrineEntities;

final class SurvosMyBundle extends AbstractSurvosBundle
{
    use HasDoctrineEntities;

    protected function doctrineAlias(): string
    {
        return 'My';
    }

    public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
    {
        parent::loadExtension($config, $container, $builder); // auto-scans Command/, Controller/

        // Only register services that need non-default arguments
        $container->services()
            ->set(MyService::class)
            ->arg('$config', $config);
    }
}
```

### What the base class handles automatically

[](#what-the-base-class-handles-automatically)

ConventionWhat is registered`src/Command/` existsCommand classes are loaded as services; `#[AsCommand]` is auto-configured`src/Controller/` existsController classes are loaded as services`src/Controller/` exists + `HasConfigurableRoutes`Controller routes are loaded via attribute scanning`templates/` existsRegistered as a Twig namespace — `SurvosMyBundle` → `@SurvosMy``assets/` exists + `ASSET_PACKAGE` const definedPath registered with Symfony AssetMapper`src/Entity/` exists + `HasDoctrineEntities`ORM mapping is registered; see Doctrine section below### Declaring the bundle dependency

[](#declaring-the-bundle-dependency)

Use `#[RequiredBundle]` to declare that your bundle needs kit-bundle active. Symfony enforces this automatically — no recipe, no documentation note, no forgotten `bundles.php` entry:

```
use Symfony\Component\DependencyInjection\Kernel\RequiredBundle;
use Survos\Kit\SurvosKitBundle;

#[RequiredBundle(SurvosKitBundle::class)]
final class SurvosMyBundle extends AbstractSurvosBundle { ... }
```

### Doctrine Entity Mapping

[](#doctrine-entity-mapping)

`HasDoctrineEntities` opts a bundle into Doctrine ORM mapping. It registers the bundle's `src/Entity/` directory as attribute-mapped Doctrine entities during container prepending.

This is not a passive convenience. For app developers, installing a bundle that uses `HasDoctrineEntities` means Doctrine may discover new mapped classes and therefore new tables, columns, indexes, or relations.

Expected app workflow:

```
composer require survos/key-value-bundle
php bin/console doctrine:schema:update --dump-sql
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
```

Bundle author opt-in:

```
use Survos\Kit\AbstractSurvosBundle;
use Survos\Kit\Traits\HasDoctrineEntities;

final class SurvosMyBundle extends AbstractSurvosBundle
{
    use HasDoctrineEntities;
}
```

By default, the entity namespace is derived from the bundle namespace:

```
Survos\MyBundle\SurvosMyBundle
// maps src/Entity as:
Survos\MyBundle\Entity
```

Override `entityNamespace()` only when the entities are not in the standard namespace:

```
protected function entityNamespace(): string
{
    return 'Survos\\MyBundle\\Model\\Doctrine';
}
```

Override `doctrineAlias()` only when the default alias, the bundle class name without `Bundle`, is not what you want.

### Route Configuration

[](#route-configuration)

Every bundle that uses `HasConfigurableRoutes` should expose the same two app options:

```
public function configure(DefinitionConfigurator $definition): void
{
    $children = $definition->rootNode()->children();
    $this->addRouteOptions($children, '/claims');

    // bundle-specific options...
    $children->end();
}
```

That gives apps parity with traditional `config/routes/*.yaml` imports:

- `routes_enabled: false` means "do not auto-import this bundle's controller attributes"
- `route_prefix: /custom-prefix` means "mount the bundle's routes under this URL prefix"

The bundle must then call `captureRouteConfig()` and `registerRouteLoader()` from `loadExtension()`, and `addRouteLoaderCompilerPass()` from `build()`.

### UX / AssetMapper Bundles

[](#ux--assetmapper-bundles)

Bundles that ship reusable frontend assets should extend `AbstractUxBundle`. It extends `AbstractSurvosBundle`, enables AssetMapper registration by default, and registers itself as a no-op compiler pass so asset-heavy bundles can override `process()` when they need compile time wiring.

```
use Survos\Kit\AbstractUxBundle;

final class SurvosIiifBundle extends AbstractUxBundle
{
    public const ASSET_PACKAGE = 'iiif';
}
```

### Overriding Conventions

[](#overriding-conventions)

Override `twigNamespace()` to customise or disable Twig path registration:

```
protected function twigNamespace(): ?string
{
    return 'MyCustomNamespace'; // null to skip entirely
}
```

Override `assetNamespace()` or define `ASSET_PACKAGE` to control AssetMapper registration:

```
// Option A: constant (preferred)
public const ASSET_PACKAGE = 'claims';  // → @survos/claims

// Option B: method override
protected function assetNamespace(): ?string
{
    return '@survos/claims';
}
```

---

For app developers
------------------

[](#for-app-developers)

Most behaviour is automatic. The only knobs exposed are **route registration**, which apps sometimes need to take over manually.

```
# config/packages/survos_claims.yaml
survos_claims:
    routes_enabled: true       # default — set false to manage routes yourself
    route_prefix:  /claims     # optional URL prefix for all bundle routes
    list_predicates: []        # bundle-specific options vary per bundle
```

`routes_enabled: false` is the escape hatch: the bundle's controllers are still registered as services, but no routes are loaded. Use this when you want to mount the bundle's routes under a custom prefix in your own `config/routes/` file, or only expose a subset of them.

Commands, Twig paths, and AssetMapper registration have no on/off toggle — they are unconditional. To suppress a command, use Symfony's built-in `console.command` tag exclusion or remove the command class from the bundle if you're forking it.

---

Before / after
--------------

[](#before--after)

A typical bundle before kit-bundle (~90 lines):

```
// ❌ Before: every class listed, every path hard-coded
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
{
    $services = $container->services()->defaults()->autowire()->autoconfigure();
    $services->set(MyRepository::class);
    $services->set(MyImporter::class);
    $services->set(MyProjector::class);
    $services->set(MyService::class)->arg('$config', $config);
    $services->set(MyImportCommand::class);
    $services->set(MyExportCommand::class);
    $services->set(MyTwigExtension::class)->autoconfigure();
    // ...
}

public function prependExtension(ContainerConfigurator $container, ContainerBuilder $builder): void
{
    $builder->prependExtensionConfig('doctrine', [
        'orm' => ['mappings' => [
            'SurvosMyBundle' => [
                'is_bundle' => false,
                'type'      => 'attribute',
                'dir'       => dirname(__DIR__) . '/src/Entity',   // repeated everywhere
                'prefix'    => 'Survos\\MyBundle\\Entity',
                'alias'     => 'My',
            ],
        ]],
    ]);
    $builder->prependExtensionConfig('twig', [
        'paths' => [dirname(__DIR__) . '/templates' => 'SurvosMy'],  // repeated everywhere
    ]);
}
```

After kit-bundle:

```
// ✅ After: conventions replace boilerplate
final class SurvosMyBundle extends AbstractSurvosBundle
{
    use HasDoctrineEntities;
    use HasConfigurableRoutes;

    // Doctrine + Twig handled by the base class
    protected function doctrineAlias(): string { return 'My'; }

    public function configure(DefinitionConfigurator $definition): void
    {
        $children = $definition->rootNode()->children();
        $this->addRouteOptions($children, '/my');
        $children->end();
    }

    public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
    {
        parent::loadExtension($config, $container, $builder); // scans Command/, Controller/
        $this->captureRouteConfig($config);
        $this->registerRouteLoader($builder);

        // Only wire services that need arguments
        $container->services()
            ->set(MyService::class)
            ->arg('$config', $config);
    }

    public function build(ContainerBuilder $container): void
    {
        parent::build($container);
        $this->addRouteLoaderCompilerPass($container);
    }
}
```

Commands, Twig paths, and entity mappings are handled by convention. Route loading keeps a small explicit hook so each bundle can expose the standard `routes_enabled` and `route_prefix`escape hatches.

---

Requirements
------------

[](#requirements)

- PHP 8.4+
- Symfony 8.1+
- `doctrine/orm` — optional, only needed when using `HasDoctrineEntities`
- `symfony/asset-mapper` — optional, only needed for Stimulus / UX bundles

###  Health Score

50

—

FairBetter than 95% of packages

Maintenance98

Actively maintained with recent releases

Popularity14

Limited adoption so far

Community21

Small or concentrated contributor base

Maturity60

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

19

Last Release

21d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/21b39551f92ed4143772c622f9e571589c5a72c96ab3c53fe67489ce0d83e806?d=identicon)[tacman1123](/maintainers/tacman1123)

---

Top Contributors

[![tacman](https://avatars.githubusercontent.com/u/619585?v=4)](https://github.com/tacman "tacman (3 commits)")

---

Tags

symfonybundletraitsbase class

### Embed Badge

![Health badge](/badges/survos-kit-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/survos-kit-bundle/health.svg)](https://phpackages.com/packages/survos-kit-bundle)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
