【问题标题】:How to send multiple rendering from a controller in Silex?如何从 Silex 中的控制器发送多个渲染?
【发布时间】:2016-02-16 15:46:50
【问题描述】:

为了修复用户等待的痛苦,因为一些(已经优化)数据库计算:大约 3 到 10 秒

我们需要在漫长的计算过程中创建一个等待页面,就像每个航班比较器所做的那样。

我们的架构基于 Silex 1.3

我们想要实现的是:

  1. 通过路由 URL 匹配触发控制器操作并开始计算
  2. 在不修改请求的情况下进行第一次渲染:树枝等待页面
  3. 在计算结束时用所有数据渲染最终的树枝 由计算驱动

我使用绑定到路由的$app->before 属性对其进行了测试,返回/渲染会停止计算...

那么基于计算的双重渲染怎么做呢?

不适合我们的解决方案:

我实现的第一个解决方法是 JavaScript 显示/隐藏微调器元素。 但这不是一个合适的解决方案,因为我们确实需要从服务器获取临时渲染。

编辑

第一次渲染正在工作,但我无法通过调用正在进行渲染的控制器操作(定义为控制器即服务)来进行第二次渲染,我得到了这个异常

RuntimeException:访问请求范围之外的请求服务。尝试将该调用移至 before 处理程序或控制器。

这是我的控制器定义

 $app['index.controller'] = $app->share(function() use ($app) {
    return new IndexController($app);
});

这是我的路线定义

$app->get('/vue-ensemble/{city}', function (Request $request, Application $app)
{
    $content = function() use ($app) {
        $wait = $app->render('index_test.twig', array());
        $wait->send();
        flush();

        // Long process
        $process = $app['index.controller']->overviewAction();
        $process->send();
        flush();
    };

    return $app->stream($content);
});

这是我的控制器操作

  protected $app;
    public function __construct(Application $app)
    {
        $this->app = $app;
    }

    public function overviewAction(){
          /* DO LONG PROCESS */
           return $this->app->render('overview.twig', array('some elements'=>'some values'));
        }

编辑'

不幸的是我仍然有同样的问题,这是堆栈跟踪:

    fatal error: Uncaught exception 'RuntimeException' with message 'Accessed request service outside of request scope. Try moving that call to a before handler or controller.' in C:\inforisq\application\vendor\silex\silex\src\Silex\Application.php on line 150
( ! ) RuntimeException: Accessed request service outside of request scope. Try moving that call to a before handler or controller. in C:\inforisq\application\vendor\silex\silex\src\Silex\Application.php on line 150
Call Stack
#   Time    Memory  Function    Location
1   0.0010  240848  {main}( )   ...\index.php:0
2   0.5250  4506656 Silex\Application->run( )   ...\index.php:14
3   0.7040  13098664    Symfony\Component\HttpFoundation\Response->send( )  ...\Application.php:564
4   0.7040  13101248    Symfony\Component\HttpFoundation\StreamedResponse->sendContent( )   ...\Response.php:372
5   0.7040  13101296    call_user_func:{C:\inforisq\application\vendor\symfony\http-foundation\StreamedResponse.php:90} ( ) ...\StreamedResponse.php:90
6   0.7040  13101384    {closure:C:\inforisq\application\app\config\routing.php:20-27}( )   ...\StreamedResponse.php:90
7   0.7040  13118168    Inforisq\Controller\IndexController->overviewAction( )  ...\routing.php:25
8   0.7040  13118328    Lib\InforisqApplication->place_analyzeURL( )    ...\IndexController.php:64
9   0.7060  13306184    Indicator\Repository\PlaceRepository->analyzeURLPlace( )    ...\PlaceTrait.php:23
10  0.7060  13306304    Lib\InforisqApplication->request( ) ...\PlaceRepository.php:423
11  0.7060  13306384    Pimple->offsetGet( )    ...\PlaceRepository.php:26
12  0.7060  13306464    Silex\Application->Silex\{closure}( )   ...\Pimple.php:83

【问题讨论】:

  • 能否为您的异常添加一些痕迹?它是从哪里扔的?
  • 我已经更新了答案
  • 这是什么电话11 0.7060 13306384 Pimple->offsetGet( ) ...\PlaceRepository.php:26
  • 这是一个具有简单构造的类,注入了 $app:__construct(Application $app)
  • 这显然是问题

标签: php symfony middleware silex


【解决方案1】:

您需要使用Symfony\Component\HttpFoundation\StreamedResponse

https://symfony.com/doc/current/components/http_foundation/introduction.html#streaming-a-response

编辑

当您从控制器返回响应时,内核调用$response->send(),但在内部Response::send() 调用Response::sendHeaders() 然后Response::sendContent()

所以在这种情况下,sendHeaders() 将由内核在流式响应中发送一次,然后如果您在回调中需要其他 Response 对象以方便起见,则必须仅调用 sendContent()

如果您需要自定义 http 响应代码或标头,您可以将它们作为参数传递给方法 Application::stream($callback, $statusCode, array $headers)

在编辑我的答案之前,我使用了flush(),就像 symfony 文档中的示例一样,但您可能需要一些缓存才能处理回调中的第二个控制器,因此首先使用 ob_start()ob_flush() 来表示“等待”的回应。

use \Silex\Application;
use \Symfony\Component\HttpFoundation\Request;
use \Symfony\Component\HttpFoundation\Response;

$app->get('/vue-ensemble/{city}', function (Request $request, Application $app, $city)
{
    /** @var \Acme\Controller\IndexController $indexController */
    $indexController = $app['index.controller'];

    /** @var Response $wait */
    $wait = $app->render('wait.html.twig', array('city' => $city);

    // Callback
    $content = function () use ($wait, $indexController) {
        ob_start();
        $wait->sendContent();
        ob_flush();

        $indexController->overviewAction($city)->sendContent();
        flush();
    }

    return $app->stream($content);

    // Will send a \Symfony\Component\HttpFoundation\StreamedResponse
    // equivalent to :

    // $streamedResponse = new StreamedResponse();
    // $streamedResponse->setCallback($content);
    //
    // return streamedResponse;
});

编辑 2:

你应该只在服务的构造函数中传递你需要的服务,而不是每次都传递整个容器:

$app['index.controller'] = $app->share(function() use ($app) {
    return new IndexController($app['twig'], $app['some_helper']);
});

$app['some_helper'] = $app->protect(function($arg1, arg2) use ($app) {
    $helper = new \Acme\Helper($app['some_dependency'];

    return $helper->help(arg1, arg2);
});

然后:

class IndexController
{
    protected $twig;
    protected $helper;

    public function __construct(\Twig_Engine $twig, \Acme\Helper $helper)
    {
        $this->twig = $twig;
        $this->helper = $helper;
    }

    public function overview($city)
    {
        $some_arg = ...

        $viewArg = $this->helper($some_arg, $city);

        return $this->twig->renderResponse('overview.twig', array('some elements' => $viewArg));
    }
}

但是如果indexController 刚刚返回$this->twig->render(...) 会更好,但它应该是echo $indexController->overview($city)

【讨论】:

  • 自定义 Traits 注入可以吗?在我们的引导文件中,我们正在实例化: $app = new InforisqApplication(array('debug' => true));
  • ...和 ​​InforisqApplication 类扩展了 Application 并且正在充分利用(每个 Trait 一个)
  • Trait 不是容器的服务,因此您需要传递所有容器(应用程序)才能使用它们的方法
  • 一个 Trait 不应该在服务中使用,只能在控制器中使用
  • 奇怪的行为,Services 应该拦截代码的所有业务部分(任何应用程序),并且 Traits 只是一个 Method Helper 来简化代码分解。这么说,Traits 基本上是由服务使用的......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-17
  • 1970-01-01
相关资源
最近更新 更多