# Symfony Plugin Intentions

**Intentions** are quick-fix actions that you can apply to your code. They appear as light bulb suggestions when you place your cursor on specific code elements, allowing you to refactor or improve your code with a single click.

---

## Add Route attribute to controller method

**Feature ID:** `AddRouteAttributeIntention`  
**Date:** 2025-12-03  
**Feature Page:** [Add Route attribute to controller method](https://espend.de/phpstorm/plugin/symfony#add-route-attribute-intention)  

Adds a `#[Route]` attribute to a public method in a Symfony controller class. The route path and name are automatically generated based on the controller and method name.

### Code Examples

```
# Before:
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class ProductController extends AbstractController
{
    public function index(): Response
    {
        return $this->render('product/index.html.twig');
    }
}
```

```
# After:
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class ProductController extends AbstractController
{
    #[Route('/product', name: 'app_product_index')]
    public function index(): Response
    {
        return $this->render('product/index.html.twig');
    }
}
```

---

## Migrate Symfony Command to invokable style (Symfony 7.3+)

**Feature ID:** `CommandToInvokableIntention`  
**Date:** 2025-11-29  
**Feature Page:** [Migrate Symfony Command to invokable style (Symfony 7.3+)](https://espend.de/phpstorm/plugin/symfony#command-to-invokable-intention)  

Converts traditional Symfony Command with `execute()` method to invokable command style using `__invoke()` with Argument and Option attributes.

This intention automatically:

### Code Examples

```
# Before:
namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class CreateUserCommand extends Command
{
    public function __construct()
    {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this
            ->addArgument('username', InputArgument::REQUIRED, 'The username')
            ->addOption('admin', null, InputOption::VALUE_NONE, 'Make user admin');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $username = $input->getArgument('username');
        $isAdmin = $input->getOption('admin');

        $output->writeln("Creating user: $username");

        if ($isAdmin) {
            $output->writeln('User will be admin');
        }

        return Command::SUCCESS;
    }
}
```

```
# After:
namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\Attribute\Argument;
use Symfony\Component\Console\Input\Attribute\Option;
use Symfony\Component\Console\Output\OutputInterface;

class CreateUserCommand
{
    public function __construct()
    {
    }

    public function __invoke(
        OutputInterface $output,
        #[Argument(description: 'The username')]
        string $username,
        #[Option(description: 'Make user admin')]
        bool $admin = false
    ): int {
        $output->writeln("Creating user: $username");

        if ($admin) {
            $output->writeln('User will be admin');
        }

        return 0;
    }
}
```

---

## Add parameter to invokable Command __invoke method

**Feature ID:** `CommandInvokeParameterIntention`  
**Date:** 2025-12-03  
**Feature Page:** [Add parameter to invokable Command __invoke method](https://espend.de/phpstorm/plugin/symfony#command-invoke-parameter-intention)  

Adds a supported parameter to invokable Symfony Command `__invoke` method. The intention is only available when the class does not extend `Command` and has an `__invoke()` method.

### Code Examples

```
# Before:
<?php

namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;

#[AsCommand(name: 'app:example')]
class ExampleCommand
{
    public function __invoke(): int
    {
        return 0;
    }
}
```

```
# After:
<?php

namespace App\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(name: 'app:example')]
class ExampleCommand
{
    public function __invoke(SymfonyStyle $io): int
    {
        return 0;
    }
}
```

---

## Generate complete Symfony service definition

**Feature ID:** `PhpServiceIntention`  
**Date:** 2015-02-21  
**Feature Page:** [Generate complete Symfony service definition](https://espend.de/phpstorm/plugin/symfony#php-service-intention)  

Generates a complete Symfony service definition from a PHP class by analyzing the constructor parameters. Opens an interactive dialog that allows customization of service ID, output format, and options.

Supported output formats include YAML, XML, PHP array config, and PHP fluent configurator code.

Perfect for quickly creating service definitions for existing PHP classes in Symfony applications.

### Code Examples

```
# Before:
# PHP class constructor not in sync with services.yaml
# class OrderProcessor
# {
#     public function __construct(
#         private LoggerInterface $logger,
#         private OrderRepository $orderRepository,
#         private EmailService $emailService,
#     ) {
#     }
# }

services:
    App\Service\OrderProcessor:
        arguments:
            - '@logger'
```

```
# After:
# Generated service definition (YAML format)
# After using the dialog and clicking "Insert" or "Copy"

services:
    App\Service\OrderProcessor:
        arguments:
            - '@logger'
            - '@App\Repository\OrderRepository'
            - '@App\Service\EmailService'
```

---

## Sync service definition arguments with constructor

**Feature ID:** `PhpServiceArgumentIntention`  
**Date:** 2017-10-19  
**Feature Page:** [Sync service definition arguments with constructor](https://espend.de/phpstorm/plugin/symfony#php-service-argument-intention)  

Automatically updates service definition arguments in YAML or XML configuration files to match the constructor parameters of the PHP class. When you modify a service class constructor by adding, removing, or reordering parameters, this intention synchronizes the service definition to match the new constructor signature.

---

## Create and register CompilerPass

**Feature ID:** `PhpBundleCompilerPassIntention`  
**Date:** 2015-05-01  
**Feature Page:** [Create and register CompilerPass](https://espend.de/phpstorm/plugin/symfony#php-bundle-compiler-pass-intention)  

Creates a new CompilerPass file in the `DependencyInjection/Compiler` directory and automatically registers it in the bundle's `build()` method.

### Code Examples

```
# Before:
namespace App;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class AppBundle extends Bundle
{
}
```

```
# After:
namespace App;

use App\DependencyInjection\Compiler\CustomCompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class AppBundle extends Bundle
{
    public function build(ContainerBuilder $container): void
    {
        parent::build($container);

        $container->addCompilerPass(new CustomCompilerPass());
    }
}
```

---

## Add parameter to route action method

**Feature ID:** `RouteActionParameterIntention`  
**Date:** 2025-12-04  
**Feature Page:** [Add parameter to route action method](https://espend.de/phpstorm/plugin/symfony#route-action-parameter-intention)  

Adds a parameter (e.g., Request) to a Symfony route action method. Available when the cursor is inside a public method that has a Route attribute or annotation, or inside an __invoke method where the class has a Route attribute or annotation.

### Code Examples

```
# Before:
<?php
use Symfony\Component\Routing\Attribute\Route;

class MyController
{
    #[Route('/hello')]
    public function index(): Response
    {
    }
}
```

```
# After:
<?php
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;

class MyController
{
    #[Route('/hello')]
    public function index(Request $request): Response
    {
    }
}
```

---

## Migrate Twig Extension to PHP attributes

**Feature ID:** `TwigExtensionToAttributeIntention`  
**Date:** 2025-12-10  
**Feature Page:** [Migrate Twig Extension to PHP attributes](https://espend.de/phpstorm/plugin/symfony#twig-extension-to-attribute-intention)  

Migrates Twig Extension from `getFilters()`, `getFunctions()`, and `getTests()` methods to PHP attributes.

### Code Examples

```
# Before:
<?php

namespace App\Twig;

use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Twig\TwigTest;
use Twig\Environment;

class MyExtension extends AbstractExtension
{
    public function getFilters()
    {
        return [
            new TwigFilter('filter_name', [$this, 'filterMethod'], ['needs_environment' => true]),
        ];
    }

    public function getFunctions()
    {
        return [
            new TwigFunction('function_name', [$this, 'functionMethod'], [
                'needs_environment' => true,
                'needs_context' => true,
                'is_safe' => ['html']
            ]),
        ];
    }

    public function getTests()
    {
        return [
            new TwigTest('test_name', [$this, 'testMethod']),
        ];
    }

    public function filterMethod(Environment $env, $value)
    {
        return $value;
    }

    public function functionMethod(Environment $env, $context, $value)
    {
        return $value;
    }

    public function testMethod($value)
    {
        return $value;
    }
}
```

```
# After:
<?php

namespace App\Twig;

use Twig\Attribute\AsTwigFilter;
use Twig\Attribute\AsTwigFunction;
use Twig\Attribute\AsTwigTest;
use Twig\Environment;

class MyExtension
{
    #[AsTwigFilter('filter_name', needsEnvironment: true)]
    public function filterMethod(Environment $env, $value)
    {
        return $value;
    }

    #[AsTwigFunction('function_name', needsEnvironment: true, needsContext: true, isSafe: ['html'])]
    public function functionMethod(Environment $env, $context, $value)
    {
        return $value;
    }

    #[AsTwigTest('test_name')]
    public function testMethod($value)
    {
        return $value;
    }
}
```

---

## Add missing service tags (YAML)

**Feature ID:** `YamlServiceTagIntention`  
**Date:** 2015-04-24  
**Feature Page:** [Add missing service tags (YAML)](https://espend.de/phpstorm/plugin/symfony#yaml-service-tag-intention)  

Automatically adds missing service tags to a YAML service definition based on the PHP class's interfaces and attributes. This intention analyzes your PHP class and suggests appropriate tags, opening a popup if multiple tags are detected.

### Code Examples

```
# Before:
# config/services.yaml
#
# PHP class implements EventSubscriberInterface:
# class OrderEventSubscriber implements EventSubscriberInterface
# {
#     public static function getSubscribedEvents(): array
#     {
#         return [OrderPlacedEvent::class => 'onOrderPlaced'];
#     }
# }
#
# But the service definition is missing the kernel.event_subscriber tag:

services:
    App\EventSubscriber\OrderEventSubscriber:
        arguments:
            - '@logger'
```

```
# After:
# config/services.yaml
#
# After applying the intention, the kernel.event_subscriber tag is added:

services:
    App\EventSubscriber\OrderEventSubscriber:
        arguments:
            - '@logger'
        tags:
            - kernel.event_subscriber
```

---

## Add missing service tags (XML)

**Feature ID:** `XmlServiceTagIntention`  
**Date:** 2015-04-23  
**Feature Page:** [Add missing service tags (XML)](https://espend.de/phpstorm/plugin/symfony#xml-service-tag-intention)  

Automatically adds missing `<tag>` elements to an XML service definition based on the PHP class's interfaces and attributes. This intention analyzes your PHP class and suggests appropriate tags, opening a popup if multiple tags are detected.

### Code Examples

```
# Before:
<!-- config/services.xml -->
<!--
PHP class implements EventSubscriberInterface:
class OrderEventSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [OrderPlacedEvent::class => 'onOrderPlaced'];
    }
}

But the service definition is missing the kernel.event_subscriber tag:
-->

<services>
    <service id="App\EventSubscriber\OrderEventSubscriber" class="App\EventSubscriber\OrderEventSubscriber">
        <argument type="service" id="logger"/>
    </service>
</services>
```

```
# After:
<!-- config/services.xml -->
<!--
After applying the intention, the kernel.event_subscriber tag is added:
-->

<services>
    <service id="App\EventSubscriber\OrderEventSubscriber" class="App\EventSubscriber\OrderEventSubscriber">
        <argument type="service" id="logger"/>
        <tag name="kernel.event_subscriber"/>
    </service>
</services>
```

---

## Sync XML service arguments with constructor

**Feature ID:** `XmlServiceArgumentIntention`  
**Date:** 2015-04-12  
**Feature Page:** [Sync XML service arguments with constructor](https://espend.de/phpstorm/plugin/symfony#xml-service-argument-intention)  

Suggests appropriate service IDs for `<argument>` elements based on the constructor parameter types. Available when your cursor is on an `<argument>` element.

### Code Examples

```
# Before:
<!-- config/services.xml -->
<!--
PHP class constructor:
public function __construct(
    private LoggerInterface $logger,
    private OrderRepository $orderRepository,
    private EmailService $emailService,
) {}

But XML service definition has only one argument:
-->

<services>
    <service id="App\Service\OrderProcessor" class="App\Service\OrderProcessor">
        <argument type="service" id="logger"/>
    </service>
</services>
```

```
# After:
<!-- config/services.xml -->
<!--
After applying the intention, all constructor arguments are present:
-->

<services>
    <service id="App\Service\OrderProcessor" class="App\Service\OrderProcessor">
        <argument type="service" id="logger"/>
        <argument type="service" id="App\Repository\OrderRepository"/>
        <argument type="service" id="App\Service\EmailService"/>
    </service>
</services>
```

---

## Suggest service ID for XML argument

**Feature ID:** `XmlServiceSuggestIntention`  
**Date:** 2016-03-06  
**Feature Page:** [Suggest service ID for XML argument](https://espend.de/phpstorm/plugin/symfony#xml-service-suggest-intention)  

Suggests appropriate service IDs for an `<argument>` element based on the constructor parameter type. Available when your cursor is on an `<argument>` element.

### Code Examples

```
# Before:
<!-- config/services.xml -->
<services>
    <service id="App\Service\OrderProcessor" class="App\Service\OrderProcessor">
        <argument type="service" id="logger"/>
        <argument/>
        <!-- Empty argument - need to fill in service ID -->
    </service>
</services>
```

```
# After:
<!-- config/services.xml -->
<services>
    <service id="App\Service\OrderProcessor" class="App\Service\OrderProcessor">
        <argument type="service" id="logger"/>
        <argument type="service" id="App\Repository\OrderRepository"/>
        <!-- Service ID filled in from suggestion popup -->
    </service>
</services>
```

---

## Convert Doctrine entity string to class constant

**Feature ID:** `DoctrineRepositoryClassConstantIntention`  
**Date:** 2015-12-13  
**Feature Page:** [Convert Doctrine entity string to class constant](https://espend.de/phpstorm/plugin/symfony#doctrine-repository-class-constant-intention)  

Converts deprecated shortcut notation like `'AppBundle:Product'` or `'App:Product'` to `Product::class`. This modernization provides better refactoring support and is the recommended approach since Doctrine 2.5 and Symfony 4.

### Code Examples

```
# Before:
namespace App\Controller;

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class ProductController extends AbstractController
{
    public function __construct(
        private EntityManagerInterface $entityManager
    ) {
    }

    public function show(int $id)
    {
        $repository = $this->entityManager->getRepository('App:Product');
        $product = $this->entityManager->find('App:Product', $id);

        return $this->render('product/show.html.twig', [
            'product' => $product
        ]);
    }
}
```

```
# After:
namespace App\Controller;

use App\Entity\Product;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class ProductController extends AbstractController
{
    public function __construct(
        private EntityManagerInterface $entityManager
    ) {
    }

    public function show(int $id)
    {
        $repository = $this->entityManager->getRepository(Product::class);
        $product = $this->entityManager->find(Product::class, $id);

        return $this->render('product/show.html.twig', [
            'product' => $product
        ]);
    }
}
```

---

## Convert Form type string alias to class constant

**Feature ID:** `FormStringToClassConstantIntention`  
**Date:** 2015-12-12  
**Feature Page:** [Convert Form type string alias to class constant](https://espend.de/phpstorm/plugin/symfony#form-string-to-class-constant-intention)  

Converts legacy form type aliases like `'text'` to `TextType::class`.

### Code Examples

```
# Before:
namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;

class ProductType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', 'text')
            ->add('description', 'textarea')
            ->add('price', 'number')
            ->add('category', 'entity', [
                'class' => 'App\Entity\Category',
            ])
            ->add('submit', 'submit');
    }
}
```

```
# After:
namespace App\Form;

use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;

class ProductType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class)
            ->add('description', TextareaType::class)
            ->add('price', NumberType::class)
            ->add('category', EntityType::class, [
                'class' => 'App\Entity\Category',
            ])
            ->add('submit', SubmitType::class);
    }
}
```

---

## Inject missing service property via constructor

**Feature ID:** `PhpPropertyArgumentIntention`  
**Date:** 2022-04-21  
**Feature Page:** [Inject missing service property via constructor](https://espend.de/phpstorm/plugin/symfony#php-property-argument-intention)  

Automatically injects a missing service property through the constructor when you reference `$this->serviceProperty` that doesn't exist in the class.

### Code Examples

```
# Before:
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class ProductController extends AbstractController
{
    public function index(): Response
    {
        $products = $this->productRepository->findAll();

        return $this->render('product/index.html.twig', [
            'products' => $products
        ]);
    }
}
```

```
# After:
namespace App\Controller;

use App\Repository\ProductRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class ProductController extends AbstractController
{
    public function __construct(
        private ProductRepository $productRepository,
    ) {
    }

    public function index(): Response
    {
        $products = $this->productRepository->findAll();

        return $this->render('product/index.html.twig', [
            'products' => $products
        ]);
    }
}
```

---

## Create missing translation key

**Feature ID:** `TwigTranslationKeyIntention`  
**Date:** 2017-03-06  
**Feature Page:** [Create missing translation key](https://espend.de/phpstorm/plugin/symfony#twig-translation-key-intention)  

Creates a missing translation key in the appropriate translation file when using `trans` or `transchoice` filters in Twig templates.

### Code Examples

```
# Before:
<h1>product.title</h1>
<p>product.description</p>
<button>product.add_to_cart</button>
```

```
# After:
# translations/messages.fr.yaml
# After using the intention and entering the French translation:

product:
    title: 'Produit'
    description: 'Description du produit'
    add_to_cart: 'Ajouter au panier'
```

---

