Welcome to Abdul Malik Ikhsan's Blog

Using laminas-cli to Consume Symfony Console Command in Mezzio Application

Posted in Laminas, Mezzio, Symfony 4 by samsonasik on April 17, 2020


So, you want to use Symfony Console Command in Mezzio Application? You can! There is laminas-cli for that. While it still in development, you already can give it a try. First, I assume that you already installed the mezzio application. Next, you can set minimum-stability and prefer-stable config in your composer.json:

➜  composer config minimum-stability dev
➜  composer config prefer-stable true

By above command, you can ensure that you can install the non-stable dependency, while prefer stable version if found. Next, you can require the laminas-cli via command:

➜  composer require laminas/laminas-cli

After installed, let’s create our first command: “HelloWorld command”, like the following:

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\Output\OutputInterface;

use function sprintf;

final class HelloWorld extends Command
{
    protected function configure()
    {
        $this
            ->addArgument('message', InputArgument::REQUIRED, 'Greeting Message');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $message = $input->getArgument('message');
        $output->writeln(sprintf('<info>Hello to world: %s<info>! ', $message));

        return 0;
    }
}

Greet! Now, time to register it to our App\ConfigProvider class:

<?php

declare(strict_types=1);

namespace App;

class ConfigProvider
{
    public function __invoke(): array
    {
        return [
            // ...
            'laminas-cli'  => $this->getCliConfig(),
            // ...
        ];
    }

    public function getCliConfig(): array
    {
        return [
            'commands' => [
                // ...
                'app:hello-world' => Command\HelloWorld::class,
                // ...
            ],
        ];
    }

    public function getDependencies(): array
    {
        return [
            'invokables'         => [
                // ...
                Command\HelloWorld::class  => Command\HelloWorld::class,
                // ...
            ],
        ];
    }

    // ...
}

First, in getDependencies(), we register the command, if the command has dependency, you need to provide factory for it. And then, in __invoke() method, we register the commands, which you can move the commands definition in separate method. That’s it! Now, you can run the command:

➜  vendor/bin/laminas app:hello-world "Good Morning"
Hello to world: Good Morning! 

5 Responses

Subscribe to comments with RSS.

  1. Bill said, on December 4, 2020 at 4:50 pm

    Trying a variation of the above from this reference; https://github.com/laminas/laminas-cli/blob/0.2.x/docs/book/intro.md

    The configuration never seems to invoke the CommandFactory despite it being known to the container. This is not using Mezzio but with laminas-mvc only. Have you been able to configure a command factory instead of just an invokable?

  2. Peter said, on January 12, 2021 at 3:44 pm

    Great tutorial. I was able to get it work and in working further with this example, I expanded the execute() function to generate HTML pages that needs to be saved in a file or send out as an email. I’m struggling to get the Url view helper to return the appropriate route links in the view even though I have passed the router stack into the Url view helper. Do you have any tutorials/guide on how to ensure that the Url view helper is generating the appropriate route links in View Models that are instantiated in the CLI mode?

    • samsonasik said, on January 12, 2021 at 10:16 pm

      you may need to pull from `ViewHelperManager` service

      • Peter said, on January 14, 2021 at 4:21 pm

        I’m not sure if i’m doing this right but still having trouble here.

        in the HelloWorld factory, i have this:
        class HelloWorldFactory implements FactoryInterface
        {
        public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
        {
        $entityManager = $container->get(‘doctrine.entitymanager.orm_default’);
        $config = $container->get(‘Config’);
        $viewRenderer = $container->get(‘ViewRenderer’);
        $viewHelperMgr = $container->get(‘ViewHelperManager’);
        $router = $container->get(‘router’);

        // Instantiate the command and inject dependencies
        return new HelloWorld ($viewRenderer, $entityManager, $config, $router, $viewHelperMgr);
        }
        }

        In the HelloWorld Command class constructor:
        public function __construct(PhpRenderer $renderer, $entityManager, $config, SimpleRouteStack $router, $viewHelperMgr, string $name = null)
        {

        $this->renderer = $renderer;
        $this->entityManager = $entityManager;
        $this->config = $config;
        $this->router = $router;
        $this->viewHelperManager = $viewHelperMgr;

        // Setup plugin Manager
        $pluginManager = $this->renderer->getHelperPluginManager();
        $pluginManager->setAlias(‘url’,Url::class);
        $pluginManager->setService(Url::class, $this->viewHelperManager->get(‘url’));
        /** This has been commented out as this was my previous attempt but the url helper is returning blank.
        $pluginManager->setService(Url::class, function($name = null, $params = [], $options = [], $reuseMatchedParams = false) {
        $url = new Url($name, $params, $options, $reuseMatchedParams);
        $url->setRouter($this->router);

        });
        */
        $pluginManager->setAlias(‘serverUrl’,ServerUrl::class);
        $pluginManager->setService(ServerUrl::class, function() {
        $urlHelper = new ServerUrl();
        $urlHelper->setHost($this->config[‘hostname’]);
        return $urlHelper->getHost();
        });

        $this->renderer->setHelperPluginManager($pluginManager);

        parent::__construct($name);
        }

        I commented out the few lines below the $pluginManager->setService() method as the url helper was printing out blank url in a ViewModel even though I manually injected the SimpleRouteStack in the URL helper.

        I tried your suggestion of getting the URL helper from the ViewHelperManager but I’m getting the error message:
        PHP Fatal error: Uncaught Error: Call to a member function getRouteMatch() on null in C:\xampp\htdocs\helloworld\vendor\laminas\laminas-mvc-console\src\Service\ConsoleViewHelperManagerDelegatorFactory.php:97
        Stack trace:
        #0 C:\xampp\htdocs\helloworld\vendor\laminas\laminas-servicemanager\src\ServiceManager.php(765): Laminas\Mvc\Console\Service\ConsoleViewHelperManagerDelegatorFactory->Laminas\Mvc\Console\Service\{closure}(Object(Laminas\ServiceManager\ServiceManager
        ), ‘Laminas\\View\\He…’, NULL)
        #1 C:\xampp\htdocs\helloworld\vendor\laminas\laminas-servicemanager\src\ServiceManager.php(201): Laminas\ServiceManager\ServiceManager->doCreate(‘Laminas\\View\\He…’)
        #2 C:\xampp\htdocs\helloworld\vendor\laminas\laminas-servicemanager\src\AbstractPluginManager.php(153): Laminas\ServiceManager\ServiceManager->get(‘Laminas\\View\\He…’)
        #3 C:\xampp\htdocs\helloworld\module\Application\src\Command\HelloWorld.php(70): Laminas\ServiceManager\AbstractPluginManager->get(‘Laminas\\View\\He…’)
        #4 C:\xampp\htdocs\helloworld\module\Applic in C:\xampp\htdocs\helloworld\vendor\laminas\laminas-mvc-console\src\Service\ConsoleViewHelperManagerDelegatorFactory.php on line 97

      • samsonasik said, on January 15, 2021 at 2:13 pm

        Thank you for verify, I will try to take a look when I have a chance. Meanwhile, you may can post a question in forum https://discourse.laminas.dev/ if somebody have a chance to take a look/help 😉


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: