Laminas-cli: service_manager config in module.config.php doesn't work

My problem is plain and simple: When trying to configure my cli module I seem to only be able to configure the service manager inside of my src/Module.php and not in my config/module.config.php.

This works:

class Module implements ConfigProviderInterface
{
    public function getConfig(): array
    {
        return include __DIR__ . '/../config/module.config.php';
    }

    public function getServiceConfig(): array
    {
        return [
            'factories' => [
                HelloCommand::class => QueueCommandFactory::class,
                PublishCommand::class => QueueCommandFactory::class,
                RabbitmqService::class => InvokableFactory::class,
            ],
        ];
    }
}

But this doesn’t:

//config/module.config.php
return [
    'laminas-cli'  => [
        'commands' => [
            'app:hello' => HelloCommand::class,
            'app:publish' => PublishCommand::class,
        ],
    ],
    'service_manager' => [
        'factories'          => [
            HelloCommand::class => QueueCommandFactory::class,
            PublishCommand::class => QueueCommandFactory::class,
            RabbitmqService::class => InvokableFactory::class,
        ],
    ]
];

The error I get when I’m trying to run the command in the shell is:

Unable to resolve service "Worker\Command\PublishCommand" to a factory; are you certain you provided it during configuration?

Heyo,

Can you try dumping the merged config of your entire application, and see if it contains your section (and where)?

Usually $container->get('config').

(Ignore the previous version of this post. I forgot to add the new module in modules.config.php. Here you go:)

The following is the result of creating a duplicate module - since my app only has one module - the original one “Worker” has the not working config in module.config.php and the new one “Tester” has the working config in the Module.php.

Array
(
    [service_manager] => Array
        (
            [aliases] => Array
                (
                    [HttpRouter] => Laminas\Router\Http\TreeRouteStack
                    [router] => Laminas\Router\RouteStackInterface
                    [Router] => Laminas\Router\RouteStackInterface
                    [RoutePluginManager] => Laminas\Router\RoutePluginManager
                    [Zend\Router\Http\TreeRouteStack] => Laminas\Router\Http\TreeRouteStack
                    [Zend\Router\RoutePluginManager] => Laminas\Router\RoutePluginManager
                    [Zend\Router\RouteStackInterface] => Laminas\Router\RouteStackInterface
                    [ValidatorManager] => Laminas\Validator\ValidatorPluginManager
                    [Zend\Validator\ValidatorPluginManager] => Laminas\Validator\ValidatorPluginManager
                )

            [factories] => Array
                (
                    [Laminas\Router\Http\TreeRouteStack] => Laminas\Router\Http\HttpRouterFactory
                    [Laminas\Router\RoutePluginManager] => Laminas\Router\RoutePluginManagerFactory
                    [Laminas\Router\RouteStackInterface] => Laminas\Router\RouterFactory
                    [Laminas\Validator\ValidatorPluginManager] => Laminas\Validator\ValidatorPluginManagerFactory
                    [Worker\Command\HelloCommand] => Worker\QueueCommandFactory
                    [Worker\Command\PublishCommand] => Worker\QueueCommandFactory
                    [Worker\RabbitmqService] => Worker\InvokableFactory
                )

        )

    [route_manager] => Array
        (
        )

    [router] => Array
        (
            [routes] => Array
                (
                )

        )

    [laminas-cli] => Array
        (
            [commands] => Array
                (
                    [app:hello] => Worker\Command\HelloCommand
                    [app:publish] => Worker\Command\PublishCommand
                    [test:hello] => Tester\Command\HelloCommand
                    [test:publish] => Tester\Command\PublishCommand
                )

        )

)

Anyone has an idea? Should I file a bug for this?

Are sure? The factories and the commands of your Worker module are there:

And the both commands of your Tester module are there:

If you have some factories in your Tester module then they are missing.

1 Like

I get why you’d think that but as far as I can tell it is configured as I claim:

I have made the test-repo available here. Maybe I’m doing something wrong?

Based on this example repo, some classes can not be found because of the wrong class names in the config. The namespaces are not included or wrong here.

First remove the method Tester\Module::getServiceConfig().

Update the module configuration of Worker:

namespace Worker;

use Laminas\ServiceManager\Factory\InvokableFactory;

return [
    'laminas-cli'     => [
        'commands' => [
            'app:hello'   => Command\HelloCommand::class,
            'app:publish' => Command\PublishCommand::class,
        ],
    ],
    'service_manager' => [
        'factories' => [
            Command\HelloCommand::class    => Command\QueueCommandFactory::class,
            Command\PublishCommand::class  => Command\QueueCommandFactory::class,
            Service\RabbitmqService::class => InvokableFactory::class,
        ],
    ],
];

Update the module configuration of Tester:

namespace Tester;

use Laminas\ServiceManager\Factory\InvokableFactory;

return [
    'laminas-cli'     => [
        'commands' => [
            'test:hello'   => Command\HelloCommand::class,
            'test:publish' => Command\PublishCommand::class,
        ],
    ],
    'service_manager' => [
        'factories' => [
            Command\HelloCommand::class    => Command\QueueCommandFactory::class,
            Command\PublishCommand::class  => Command\QueueCommandFactory::class,
            Service\RabbitmqService::class => InvokableFactory::class,
        ],
    ],
];

Then I can run the commands with the service class RabbitmqService in module Worker and Tester. The service is created and called.

1 Like

Thanks that solved it! So I guess you have to add the sub-namespaces to the commands and factories? But if I add a use statement before the return in the module.config.php to refer to the Command/Factory that I want to use that doesn’t work here? Do you have any idea why?

It all works because it is normal PHP.

You can use one of these options and the result is the same:

use Tester\Command\HelloCommand;
use Tester\Command\QueueCommandFactory;

return [
    'service_manager' => [
        'factories' => [
           HelloCommand::class =>QueueCommandFactory::class,
        ],
    ],
];
namespace Tester;

return [
    'service_manager' => [
        'factories' => [
            Command\HelloCommand::class => Command\QueueCommandFactory::class,
        ],
    ],
];
return [
    'service_manager' => [
        'factories' => [
            Tester\Command\HelloCommand::class => Tester\Command\QueueCommandFactory::class,
        ],
    ],
];

More on this topic can be found in the PHP documentation:

https://www.php.net/manual/language.namespaces.basics.php

Thanks! I prefer the first version and was wondering why it didn’t work in my case. But I see now that I had missed some use statements for the factories and the rabbitmqservice. Oops. Thanks for taking a look.

Use an IDE like PHPStorm and any problems with namespaces and or class names will be shown immediately:

Thanks! I use intelephense which normally works for autoimporting, but not if you copy/paste MyClass:class to a new location. :wink: