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

问题描述 投票:8回答:6

我有一个控制台控制器和一个发送电子邮件的动作(在下面的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 View Helper来完成,但由于Request类型是Console而不是HTTP,因此不起作用。我试图创建一个HTTP请求,但我不知道如何给它站点域或Controller / Action链接。

我的控制器代码:

$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

如何在具有站点方案,域和路径/到/ action的控制台控制器中创建HTTP请求?我如何将其传递给URL View Helper?

php zend-framework2
6个回答
1
投票

以下是此问题的解决方法:

<?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;
                },
            ),
        );
    }
}

当然,您必须在config中定义默认主机和方案值,因为无法在控制台模式下自动检测它们。


0
投票

我不敢相信,但我做到了:)

希望它能为你们所有人服务。

在使用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路由:)


0
投票

感谢@Alexey Kosov的回复。当您的应用程序在域'/'之后在子目录而不是根目录下工作时,您可能会遇到问题。

你必须添加:

$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;
                },
            ),
        );
    }
}

0
投票

更新:这篇文章的正确答案可以在这里找到:Stackoverflow: Using HTTP routes within ZF2 console application

嗯,你非常接近这个,但你没有使用Url插件。如果您进一步深入了解控制器插件的ZF2文档,您可以找到解决方案。

参见:ZF2 Documentation - Controller plugins

您的ConsoleController必须实现以下之一,才能检索Controller插件:

  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或在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 - Documentation: Console routes and routing


0
投票

我在zend控制台上遇到了类似的问题 - 默认情况下,serverUrl视图助手也无法正常工作。


我的情况:

/module/application/双人床/application/controller/console controller.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/emails/some_email_template.P HTML

<?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/auto load/global.PHP

return array(
    ...
    'website' => [
        'host' => isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'my.production.domain',
        'scheme' => 'https',
        'path' => '',
    ],
);

/config/auto load/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';
...

这就是我改变的一切。

魔法!有用! :-)

希望这对某人也有帮助。


0
投票

我认为最好的解决方案是使用DelegatorFactory。

配置/自动加载/服务器url.local.php:

return [
    'server_url' => 'http://example.com',
];

模块/应用/配置/ module.config.php:

'service_manager' => [
    'delegators' => [
        TreeRouteStack::class => [
            TreeRouteStackConsoleDelegatorFactory::class,
        ],
    ]
],

模块/应用/ 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;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.