如何在 ZF2 中的控制台控制器中创建 URL


How can I create a URL in a Console Controller in ZF2?

我有一个控制台控制器和一个发送电子邮件的操作(在下面的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必须实现以下其中一项,才能检索控制器插件:

  1. AbstractActionController
  2. AbstractRestfulController
  3. 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';
...

这就是我改变的全部

魔法!它有效!:-)

希望这对某人也有帮助。