{"id":15077732,"url":"https://github.com/dunglas/dunglasactionbundle","last_synced_at":"2025-10-05T11:31:47.252Z","repository":{"id":56973263,"uuid":"50048652","full_name":"dunglas/DunglasActionBundle","owner":"dunglas","description":"Symfony controllers, redesigned","archived":true,"fork":false,"pushed_at":"2017-07-07T22:31:27.000Z","size":124,"stargazers_count":257,"open_issues_count":7,"forks_count":14,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-09-22T15:18:41.932Z","etag":null,"topics":["adr","controller","php","symfony","symfony-bundle"],"latest_commit_sha":null,"homepage":"https://dunglas.fr/2016/01/dunglasactionbundle-symfony-controllers-redesigned/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dunglas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-01-20T17:53:00.000Z","updated_at":"2024-12-19T22:57:03.000Z","dependencies_parsed_at":"2022-08-21T07:10:31.610Z","dependency_job_id":null,"html_url":"https://github.com/dunglas/DunglasActionBundle","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/dunglas/DunglasActionBundle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dunglas%2FDunglasActionBundle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dunglas%2FDunglasActionBundle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dunglas%2FDunglasActionBundle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dunglas%2FDunglasActionBundle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dunglas","download_url":"https://codeload.github.com/dunglas/DunglasActionBundle/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dunglas%2FDunglasActionBundle/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278445879,"owners_count":25988071,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adr","controller","php","symfony","symfony-bundle"],"created_at":"2024-09-25T04:31:08.885Z","updated_at":"2025-10-05T11:31:46.961Z","avatar_url":"https://github.com/dunglas.png","language":"PHP","readme":"# DunglasActionBundle: Symfony controllers, redesigned\n\n[![Build Status](https://travis-ci.org/dunglas/DunglasActionBundle.svg?branch=master)](https://travis-ci.org/dunglas/DunglasActionBundle)\n[![Build status](https://ci.appveyor.com/api/projects/status/jpjsasx59syknghe?svg=true)](https://ci.appveyor.com/project/dunglas/dunglasactionbundle)\n[![SensioLabsInsight](https://insight.sensiolabs.com/projects/7022bce4-9d67-4ade-9b19-cf7e417c0a80/mini.png)](https://insight.sensiolabs.com/projects/7022bce4-9d67-4ade-9b19-cf7e417c0a80)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/dunglas/DunglasActionBundle/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/dunglas/DunglasActionBundle/?branch=master)\n[![StyleCI](https://styleci.io/repos/50048652/shield)](https://styleci.io/repos/50048652)\n\nThis bundle is a replacement for [the controller system](https://symfony.com/doc/current/book/controller.html) of the [Symfony framework](https://symfony.com) and for its [command system](https://symfony.com/doc/current/cookbook/console/console_command.html).\n\nIt is as convenient as the original but doesn't suffer from its drawbacks:\n\n* Action and console classes are automatically **registered as services** by the bundle\n* Their dependencies are **explicitly injected** in the constructor (no more ugly access to the service container) using the [autowiring feature of the Dependency Injection Component](https://dunglas.fr/2015/10/new-in-symfony-2-83-0-services-autowiring/)\n* Only one action per class thanks to the [`__invoke()` method](http://php.net/manual/en/language.oop5.magic.php#object.invoke)\n  (but you're still free to create classes with more than 1 action if you want to)\n* 100% compatible with common libraries and bundles including [SensioFrameworkExtraBundle](https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/)\n  annotations\n\nDunglasActionBundle allows to create **reusable**, **framework agnostic** (especially when used with [the PSR-7 bridge](https://dunglas.fr/2015/06/using-psr-7-in-symfony/))\nand **easy to unit test** classes.\n\nSee https://github.com/symfony/symfony/pull/16863#issuecomment-162221353 for the history behind this bundle.\n\n## Note for Symfony \u003e=3.3 users\n\nIf you use Symfony at version 3.3 or superior, you do not need to use this bundle as all the features were ported\nin Symfony. You can learn more about it in the [Symfony blog](http://symfony.com/blog/the-new-symfony-3-3-service-configuration-changes-explained)\nor in the [Symfony documentation](http://symfony.com/doc/current/service_container/3.3-di-changes.html).\n\n## Installation\n\nUse [Composer](https://getcomposer.org/) to install this bundle:\n\n    composer require dunglas/action-bundle\n\nAdd the bundle in your application kernel:\n\n```php\n// app/AppKernel.php\n\npublic function registerBundles()\n{\n    return [\n        // ...\n        new Dunglas\\ActionBundle\\DunglasActionBundle(),\n        // ...\n    ];\n}\n```\n\nOptional: to use the `@Route` annotation add the following lines in `app/config/routing.yml`:\n\n```yaml\napp:\n    resource: '@AppBundle/Action/' # Use @AppBundle/Controller/ if you prefer\n    type:     'annotation'\n```\n\nIf you don't want to use annotations but prefer raw YAML, use the following syntax:\n\n```yaml\nfoo:\n    path:      /foo/{bar}\n    defaults:  { _controller: 'AppBundle\\Action\\Homepage' } # this is the name of the autoregistered service corresponding to this action\n```\n\n## Usage\n\n1. Create [an invokable class](http://www.lornajane.net/posts/2012/phps-magic-__invoke-method-and-the-callable-typehint)\n   in the `Action\\` namespace of your bundle:\n\n```php\n\n// src/AppBundle/Action/MyAction.php\n\nnamespace AppBundle\\Action;\n\nuse Symfony\\Component\\Routing\\Annotation\\Route;\nuse Symfony\\Component\\Routing\\RouterInterface;\nuse Symfony\\Component\\HttpFoundation\\RedirectResponse;\nuse Symfony\\Component\\HttpFoundation\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\nclass Homepage\n{\n    private $router;\n    private $twig;\n\n    /**\n     * The action is automatically registered as a service and dependencies are autowired.\n     * Typehint any service you need, it will be automatically injected.\n     */\n    public function __construct(RouterInterface $router, \\Twig_Environment $twig)\n    {\n        $this-\u003erouter = $router;\n        $this-\u003etwig = $twig;\n    }\n\n    /**\n     * @Route(\"/myaction\", name=\"my_action\")\n     *\n     * Using annotations is not mandatory, XML and YAML configuration files can be used instead.\n     * If you want to decouple your actions from the framework, don't use annotations.\n     */\n    public function __invoke(Request $request)\n    {\n        if (!$request-\u003eisMethod('GET')) {\n            // Redirect to the current URL using the the GET method if it's not the current one\n            return new RedirectResponse($this-\u003erouter-\u003egenerateUrl('my_action'), 301);\n        }\n\n        return new Response($this-\u003etwig-\u003erender('mytemplate.html.twig'));\n    }\n}\n```\n\nAlternatively, you can create a typical Symfony controller class with several `*Action` methods in the `Controller` directory\nof your bundle, it will be autowired the same way.\n\n**There is no step 2! You're already done.**\n\nAll classes inside `Action/` and `Controller/` directories of your project bundles are automatically registered as services.\nBy convention, the service name is the Fully Qualified Name of the class.\n\nFor instance, the class in the example is automatically registered with the name `AppBundle\\Action\\Homepage`.\n\nThere are other classes/tags supported:\n\n| Class Name               | Tag automatically added | Directory\n| ------------------------ | ----------------------- | ---------\n| Command                  | console.command         | Command\n| EventSubscriberInterface | kernel.event_subscriber | EventSubscriber\n| Twig_ExtensionInterface  | twig.extension          | Twig\n\nThanks to the [autowiring feature](http://symfony.com/blog/new-in-symfony-2-8-service-auto-wiring) of the Dependency Injection\nComponent, you can just typehint dependencies you need in the constructor, they will be automatically initialized and injected.\n\nService definition can easily be customized by explicitly defining a service named according to the same convention:\n\n```yaml\n# app/config/services.yml\n\nservices:\n    # This is a custom service definition\n    'AppBundle\\Action\\MyAction':\n        arguments: [ '@router', '@twig' ]\n\n    'AppBundle\\Command\\MyCommand':\n        arguments: [ '@router', '@twig' ]\n        tags:\n            - { name: console.command }\n\n    # With Symfony \u003c 3.3\n    'AppBundle\\EventSubscriber\\MySubscriber':\n        class: 'AppBundle\\EventSubscriber\\MySubscriber'\n        tags:\n            - { name: kernel.event_subscriber }\n```\n\nThis bundle also hooks into the Routing Component (if it is available): when the `@Route` annotation is used as in the example,\nthe route is automatically registered: the bundle guesses the service to map with the path specified in the annotation.\n\n[Dive into the TestBundle](Tests/Fixtures/TestBundle) to discover more examples such as using custom services with ease\n(no configuration at all) or classes containing several actions.\n\n## Using the Symfony Micro Framework\n\nYou might be interested to see how this bundle can be used together with [the Symfony \"Micro\" framework](https://symfony.com/doc/current/cookbook/configuration/micro-kernel-trait.html).\n\nHere we go:\n\n```php\n// MyMicroKernel.php\n\nuse AppBundle\\Action\\Homepage;\nuse Symfony\\Bundle\\FrameworkBundle\\Kernel\\MicroKernelTrait;\nuse Symfony\\Component\\Config\\Loader\\LoaderInterface;\nuse Symfony\\Component\\DependencyInjection\\ContainerBuilder;\nuse Symfony\\Component\\HttpKernel\\Kernel;\nuse Symfony\\Component\\Routing\\RouteCollectionBuilder;\n\nfinal class MyMicroKernel extends Kernel\n{\n    use MicroKernelTrait;\n\n    public function registerBundles()\n    {\n        return [\n            new Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle(),\n            new Dunglas\\ActionBundle\\DunglasActionBundle(),\n            new AppBundle\\AppBundle(),\n        ];\n    }\n\n    protected function configureRoutes(RouteCollectionBuilder $routes)\n    {\n        // Specify explicitly the controller\n        $routes-\u003eadd('/', Homepage::class, 'my_route');\n        // Alternatively, use @Route annotations\n        // $routes-\u003eimport('@AppBundle/Action/', '/', 'annotation');\n    }\n\n    protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)\n    {\n        $c-\u003eloadFromExtension('framework', ['secret' =\u003e 'MySecretKey']);\n    }\n}\n```\n\nAmazing isn't it?\n\nWant to see a more advanced example? [Checkout our test micro kernel](Tests/Fixtures/TestKernel.php).\n\n## Configuration\n\n```yaml\n# app/config/config.yml\n\ndunglas_action:\n    directories: # List of directories relative to the kernel root directory containing classes to auto-register.\n        - '../src/*Bundle/{Controller,Action,Command,EventSubscriber}'\n        # This one is not registered by default\n        - '../src/*Bundle/My/Uncommon/Directory'\n    tags:\n        'Symfony\\Component\\Console\\Command\\Command': console.command\n        'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface': kernel.event_subscriber\n        'My\\Custom\\Interface\\To\\Auto\\Tag':\n            - 'my_custom.tag'\n            - [ 'my_custom.tag_with_attributes', { attribute: 'value' } ]\n```\n\n## Credits\n\nThis bundle is brought to you by [Kévin Dunglas](https://dunglas.fr) and [awesome contributors](https://github.com/dunglas/DunglasActionBundle/graphs/contributors).\nSponsored by [Les-Tilleuls.coop](https://les-tilleuls.coop).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdunglas%2Fdunglasactionbundle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdunglas%2Fdunglasactionbundle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdunglas%2Fdunglasactionbundle/lists"}