我有一个控制台控制器和一个发送电子邮件的操作(在下面的module.config.php中定义(
'console' => array(
'router' => array(
'routes' => array(
'cronroute' => array(
'options' => array(
'route' => 'sendEmails',
'defaults' => array(
'controller' => 'Application'Controller'Console',
'action' => 'send-emails'
)
)
),
)
)
),
在操作中,我想发送一封电子邮件,其中包含指向网站上另一个操作的链接。这通常使用 URL 视图帮助程序完成,但由于请求的类型是控制台而不是 HTTP,因此不起作用。我尝试创建一个HTTP请求,但我不知道如何为其提供站点域或控制器/操作链接。
我的控制器代码:
$vhm = $this->getServiceLocator()->get('viewhelpermanager');
$url = $vhm->get('url');
$urlString = $url('communication', array('action' => 'respond', 'id' => $id,
array('force_canonical' => true));
这将引发错误:
======================================================================
The application has thrown an exception!
======================================================================
Zend'Mvc'Router'Exception'RuntimeException
Request URI has not been set
如何在具有站点方案、域和路径/到/操作的控制台控制器中创建 HTTP 请求?以及如何将其传递给 URL 视图帮助程序?
以下是解决此问题的方法:
<?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;
},
),
);
}
}
当然,您必须在配置中定义默认主机和方案值,因为无法在控制台模式下自动检测它们。
更新:这篇文章的正确答案可以在这里找到:Stackoverflow:在 ZF2 控制台应用程序中使用 HTTP 路由
好吧,您非常接近这一点,但您没有使用Url
插件。如果您进一步了解控制器插件的 ZF2 文档,您可能会找到解决方案。
请参阅参考: ZF2 文档 - 控制器插件
您的ConsoleController
必须实现以下其中一项,才能检索控制器插件:
-
AbstractActionController
-
AbstractRestfulController
-
setPluginManager
好吧,如果您还没有这样做,我建议您使用 AbstractActionController
扩展您的控制器。
如果您确实使用了AbstractActionController
您可以简单地调用$urlPlugin = $this->url()
因为AbstractActionController
有一个__call()
实现为您检索插件。 但您也可以使用: $urlPlugin = $this->plugin('url');
因此,为了为您的邮件生成 URL,您可以在控制器中执行以下操作:
$urlPlugin = $this->url();
$urlString = $urlPlugin->fromRoute(
'route/myroute',
array(
'param1' => $param1,
'param2' => $param2
),
array(
'option1' => $option1,
'option2' => $option2
)
);
您现在可以将此 URL 传递给您的viewModel
或在您的视图模型中使用 URL viewHelper,但这取决于您。
尽量避免在控制器中使用 viewHelpers,因为我们有适用于这种情况的插件。
如果您想知道AbstractActionController
有什么方法,这里是 ZF2 ApiDoc - AbstractActionController
为了完成这项工作,您必须使用适当的结构设置路由配置:
// 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
)
)
),
)
如果您有控制台模块,只需坚持使用控制台路由路径即可。不要忘记控制台键及其下的所有路由!查看文档以获取参考:ZF2 - 文档:控制台路由和路由
我认为最好的解决方案是使用DedelegateatorFactory。
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;
}
}
我不敢相信,但我做到了:)
希望它对你们所有人都有用。
在使用fromRoute((函数的控制器中,我在下面添加了这些行:
$event = $this->getEvent();
$http = $this->getServiceLocator()->get('HttpRouter');
$router = $event->setRouter($http);
$request = new 'Zend'Http'Request();
$request->setUri('');
$router = $event->getRouter();
$routeMatch = $router->match($request);
var_dump($this->url()->fromRoute(
'route_parent/route_child',
[
'param1' => 1,
'param2' => 2,
)
);
输出:
//mydomain.local/route-url/1/2
当然,route_parent/route_child不是控制台路由,而是HTTP路由:)
感谢@Alexey科索夫的回复。当您的应用程序在子目录下工作而不是域"/"之后的根目录下工作时,您可能会遇到问题。
您必须添加:
$router->setBaseUrl($config['website']['path']);
整个代码:
<?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);
$router->setBaseUrl($config['website']['path']);
}
$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;
},
),
);
}
}
我在 zend 控制台上遇到了类似的问题 - 默认情况下,serverUrl
视图助手也无法正常工作。
我的情况:
/module/application/src/application/controller/ConsoleController.php
...
public function someAction()
{
...
$view = new ViewModel([
'data' => $data,
]);
$view->setTemplate('Application/view/application/emails/some_email_template');
$this->mailerZF2()->send(array(
'to' => $data['customer_email'],
'subject' => 'Some email subject',
), $view);
...
}
/module/Application/view/application/email/some_email_template.phtml
<?php
/** @var 'Zend'View'Renderer'PhpRenderer $this */
/** @var array $data */
?><!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link ... rel="stylesheet" />
<title>...</title>
</head>
<body>
<div style="...">
... <a href="<?= $this->serverUrl() ?>"><img src="<?= $this->serverUrl() ?>/images/logo-maillist.png" width="228" height="65"></a> ...
<p>Hello, <?= $this->escapeHtml($data['customer_name']) ?>!</p>
<p>... email body ...</p>
<div style="...">
<a href="<?= $this->serverUrl() ?>/somepath/<?= $data['some-key'] ?>" style="...">some action</a> ...
</div>
...
</div>
</body>
</html>
serverUrl
视图帮助程序仅在控制台控制器(由 cron 运行(下返回"http://"
。但是相同的模板在其他控制器处理的 Web http 请求下正确呈现。
我通过以下方式修复了它:
/config/autoload/global.php
return array(
...
'website' => [
'host' => isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'my.production.domain',
'scheme' => 'https',
'path' => '',
],
);
/config/autoload/local.php
return array(
...
'website' => [
'host' => isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'my.local.domain',
],
);
/public/index.php(ZF2 引擎启动脚本(
chdir(dirname(__DIR__));
// --- Here is my additional code ---------------------------
if (empty($_SERVER['HTTP_HOST'])) {
function array_merge_recursive_distinct(array &$array1, array &$array2)
{
$merged = $array1;
foreach ($array2 as $key => &$value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = array_merge_recursive_distinct($merged[$key], $value);
} else {
$merged[$key] = $value;
}
}
return $merged;
}
$basicConfig = require 'config/autoload/global.php';
$localConfig = @include 'config/autoload/local.php';
if (!empty($localConfig)) {
$basicConfig = array_merge_recursive_distinct($basicConfig, $localConfig);
}
unset($localConfig);
$_SERVER['HTTP_HOST'] = $basicConfig['website']['host'];
$_SERVER['HTTP_SCHEME'] = $basicConfig['website']['scheme'];
$_SERVER['HTTPS'] = $_SERVER['HTTP_SCHEME'] === 'https' ? 'on' : '';
$_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST'];
unset($basicConfig);
}
// ---/End of my additional code ---------------------------
// Setup autoloading
require 'init_autoloader.php';
...
这就是我改变的全部。
魔法!它有效!:-)
希望这对某人也有帮助。