I have a Console Controller and an action to send emails (defined below in module.config.php)
'console' => array(
'router' => array(
'routes' => array(
'cronroute' => array(
'options' => array(
'route' => 'sendEmails',
'defaults' => array(
'controller' => 'Application\Controller\Console',
'action' => 'send-emails'
)
)
),
)
)
),
In the action I want to send an email that contains a link to another action on the site. This would normally be done with a URL View Helper, but since the Request is of type Console and not HTTP, that doesn't work. I've tried to create an HTTP request, but I do not know how to give it the site domain or the Controller/Action link.
My Controller code:
$vhm = $this->getServiceLocator()->get('viewhelpermanager');
$url = $vhm->get('url');
$urlString = $url('communication', array('action' => 'respond', 'id' => $id,
array('force_canonical' => true));
This throws an error:
======================================================================
The application has thrown an exception!
======================================================================
Zend\Mvc\Router\Exception\RuntimeException
Request URI has not been set
How do I create an HTTP Request in a Console Controller that has the site scheme, domain and path/to/action? And how do I pass it along to the URL View Helper?
Here's how this issue can be solved:
<?php
// Module.php
use Zend\View\Helper\ServerUrl;
use Zend\View\Helper\Url as UrlHelper;
use Zend\Uri\Http as HttpUri;
use Zend\Console\Console;
use Zend\ModuleManager\Feature\ViewHelperProviderInterface;
class Module implements ViewHelperProviderInterface
{
public function getViewHelperConfig()
{
return array(
'factories' => array(
'url' => function ($helperPluginManager) {
$serviceLocator = $helperPluginManager->getServiceLocator();
$config = $serviceLocator->get('Config');
$viewHelper = new UrlHelper();
$routerName = Console::isConsole() ? 'HttpRouter' : 'Router';
/** @var \Zend\Mvc\Router\Http\TreeRouteStack $router */
$router = $serviceLocator->get($routerName);
if (Console::isConsole()) {
$requestUri = new HttpUri();
$requestUri->setHost($config['website']['host'])
->setScheme($config['website']['scheme']);
$router->setRequestUri($requestUri);
}
$viewHelper->setRouter($router);
$match = $serviceLocator->get('application')
->getMvcEvent()
->getRouteMatch();
if ($match instanceof RouteMatch) {
$viewHelper->setRouteMatch($match);
}
return $viewHelper;
},
'serverUrl' => function ($helperPluginManager) {
$serviceLocator = $helperPluginManager->getServiceLocator();
$config = $serviceLocator->get('Config');
$serverUrlHelper = new ServerUrl();
if (Console::isConsole()) {
$serverUrlHelper->setHost($config['website']['host'])
->setScheme($config['website']['scheme']);
}
return $serverUrlHelper;
},
),
);
}
}
Of course you have to define default host and scheme values in config since there's no way to detect them automatically in console mode.
Update: The correct answer for this post can be found here: Stackoverflow: Using HTTP routes within ZF2 console application
Well you're very close to this but you are not using the Url
plugin. If you dived a little bit further into the ZF2 Documentation of the Controller Plugins you could have found the solution.
See for reference: ZF2 Documentation - Controller plugins
Your ConsoleController
has to implement one of the following, to be able to retrieve the Controller plugins:
AbstractActionController
AbstractRestfulController
setPluginManager
Well I recommend to extend your controller with the AbstractActionController
if you haven't done it yet.
In case you do use the AbstractActionController
you can simply call $urlPlugin = $this->url()
since the AbstractActionController
has an __call()
implementation retrieving the plugin for you. But you can also use: $urlPlugin = $this->plugin('url');
So in order to generate the URL for your mail you can do the following in your controller:
$urlPlugin = $this->url();
$urlString = $urlPlugin->fromRoute(
'route/myroute',
array(
'param1' => $param1,
'param2' => $param2
),
array(
'option1' => $option1,
'option2' => $option2
)
);
You can now pass this URL to your viewModel
or use the URL viewHelper within your viewModel, but that is up to you.
Try to avoid viewHelpers within your controller as we've got plugins available for this case.
In case you wonder what methods the AbstractActionController
has, here is ZF2 ApiDoc - AbstractActionController
In order to make this work you have to setup your route config with a proper structure:
// This can sit inside of modules/Application/config/module.config.php or any other module's config.
array(
'router' => array(
'routes' => array(
// HTTP routes are here
)
),
'console' => array(
'router' => array(
'routes' => array(
// Console routes go here
)
)
),
)
If you've got a Console module, just stick with the console route paths. Don't forget the console key with all the routes beneath it! Take a look at the documentation for a reference: ZF2 - Documentation: Console routes and routing
I think the best solution is using a DelegatorFactory.
config/autoload/server-url.local.php:
return [
'server_url' => 'http://example.com',
];
module/Application/config/module.config.php:
'service_manager' => [
'delegators' => [
TreeRouteStack::class => [
TreeRouteStackConsoleDelegatorFactory::class,
],
]
],
module/Application/src/TreeRouteStackConsoleDelegatorFactory.php:
namespace Application;
use Interop\Container\ContainerInterface;
use Zend\Router\Http\TreeRouteStack;
use Zend\ServiceManager\Factory\DelegatorFactoryInterface;
use Zend\Uri\Http;
class TreeRouteStackConsoleDelegatorFactory implements DelegatorFactoryInterface
{
public function __invoke(ContainerInterface $container, $name, callable $callback, array $options = null)
{
/** @var TreeRouteStack $treeRouteStack */
$treeRouteStack = $callback();
if (!$treeRouteStack->getRequestUri()) {
$treeRouteStack->setRequestUri(
new Http($container->get('config')['server_url'])
);
}
return $treeRouteStack;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With