Ben
Ben

Reputation: 915

Symfony 4 - 3rd-party bundle commands are no longer automatically discovered

According to the documentation, a command class must extend Command or ContainerAwareCommand to be automatically discovered and registered (provided its bundle is registered in the Kernel, of course).

Since Symfony 4+, this discovery doesn't work as expected.

Try this:

composer create-project symfony/skeleton test-maxmind-sf4
cd test-maxmind-sf4
composer req cravler/maxmind-geoip-bundle dev-master
php bin/console list

You will notice that:

Now when I do exactly the same thing with Symfony 3, everything works properly.

composer create-project symfony/skeleton test-maxmind-sf3 ^3.0
cd test-maxmind-sf3
composer req cravler/maxmind-geoip-bundle dev-master
php bin/console list

What's missing there?

Thank you,

Ben

Upvotes: 5

Views: 3121

Answers (4)

mc.watras
mc.watras

Reputation: 381

If you are absolutely sure you config is correct and command is still not recognized, try resetting composer's autoloader:

$ composer dump-autoload

This helped in my case.

Upvotes: 1

Tac Tacelosky
Tac Tacelosky

Reputation: 3424

I think the command should be registered within the bundle, not the app. I think this will work:

# Cravler\/axMindGeoIpBundle/src/Resources/config/services.xml
<services>
  <service
      id="Cravler\MaxMindGeoIpBundle\Command\UpdateDatabaseCommand" 
      class="Cravler\MaxMindGeoIpBundle\Command\UpdateDatabaseCommand" 
      public="false">
      <tag name="console.command" />
  </service>
</services>

The command class extends Symfony\Component\Console\Command\Command.

Upvotes: 0

Cerad
Cerad

Reputation: 48865

Basically you have to add a few things to your bundle's services file in order to autoconfig your services. Following the example in config/services.yaml:

# Cravler/MaxMindGeolpBundle/Resources/config/services.xml
<services>
    <defaults autowire="true" autoconfigure="true" public="false" />
    <prototype 
        namespace="Cravler\MaxMindGeoIpBundle\" 
        resource="../../*" 
        exclude="../../*/{Entity,Migrations,Tests}" />

    <service id="cravler_max_mind_geo_ip.service.geo_ip_service"
             public="true"
             class="Cravler\MaxMindGeoIpBundle\Service\GeoIpService">
    </service>
</services>

Clear the cache and your command should be there.

And then of course you should probably tweak the command itself and inject the ip service instead of locating it.

I did poke around a bit and did not find this approach documented anywhere. And as I mentioned in the comments, all the bundles I did check still explicitly defined their command services. So I'm not sure if this approach is discouraged or not.

Update: as @Matteo said in their answer, prior to 4.0 any classes defined in the Command directory were treated as commands. This scanning was considered to be magical and removed. However, around the same time, Harry Potter was added to the core team and magic was his thing. So the scanning of the Command directory was replaced with all sorts of auto wiring and auto tagging spells.

Upvotes: 2

Matteo
Matteo

Reputation: 39390

From the UPGRADE FROM to 4.0 Guide here:

Relying on convention-based commands discovery is not supported anymore. Use PSR-4 based service discovery instead.

Before:

# app/config/services.yml
services:
    # ...

    # implicit registration of all commands in the `Command` folder

After:

# app/config/services.yml
services:
    # ...

    # explicit commands registration
    AppBundle\Command\:
        resource: '../../src/AppBundle/Command/*'
        tags: ['console.command']

Hope this help

Upvotes: 7

Related Questions