在Symfony 2之上构建一个小部件管理器(一个页面中有多个控制器)



用例
我正在Symfony2的基础上开发CMF。其中一个特征将是支持";窗口小部件":最终用户可以在页面中添加小的"块"或"模块"。示例:

  • 一个小型登录表单
  • 产品列表
  • 一些照片来自画廊
  • 购物车

其想法是,这些小部件中的大多数将链接到正常的、完整页面的路由/控制器。

例如:用户想要在内容页面的侧边栏中列出一个流行产品的列表。这些项将链接到ProductController的常规/product/{name}路由。但在这种情况下,列表将是一个小部件。最终用户可以定义它必须放在哪里,例如,必须显示多少项目。

"小部件"的行为与常规Symfony2控制器相同,它有路由、操作、渲染视图等。有一个WidgetManager,它有一个catch-all路由来加载小部件、配置它们并在正确的位置渲染它们。

我对Symfony2没有太多经验,但我现在已经玩了三个多月了。我当然想和Symfony2呆在一起,但我需要添加一些魔法来实现我的一些想法。

问题
支持在一个请求中呈现多个控制器(小部件)的最佳方式是什么?

研究
Symfony的TwigExtension;ActionExtension";包含一个";渲染";方法,其中包含基本思想:

<div id="sidebar">
    {% render "AcmeArticleBundle:Article:recentArticles" with {'max': 3} %}
</div>

(文件:http://symfony.com/doc/current/book/templating.html#embedding-控制器)

但它是相当有限的。这种方法的一些问题:

  • 在渲染之前,我无法配置"小部件"(例如:$myWidget->set('show_toolbar', false)),我不想将所有选项作为控制器操作参数传递
  • 不能使用模板继承。例如,我需要在de base <HEAD>块中"注入"资产引用(javascript/css)

我想要什么

我希望以下代码能够工作(这是一个简化的示例):

// SeriusPageBundleControllerPageController.php
// executed by a catch-all route
public function indexAction($url) {
    // load CMS page, etc
    
    $widgets = $this->loadWidgets($page); // widgets configuration is stored in database
    // at this point, $widgets is an array of Controller *instances*
    // meaning, they are already constructed and configured
    return $this->render("SeriusPageBundle:Page:content.html.twig", array(
        'widgets' => $widgets
    ));   
}

Serius\PageBundle\Resources\views\Page\content.html.twitg

{% extends 'SeriusPageBundle::layout.html.twig' %}
{% block content %}
    {% for widget in widgets %}
    <div>
        {% render widget %}
        <!-- Of course, this doesn't work, I would have to create my own Twig extension -->
    </div>
    {% endfor %}
{% endblock %}

小部件模板示例:

{% extends '::base.html.twig' %}
{% block stylesheets %}
  My stylesheets
{% endblock %}
{% block body %}
    This is a shoppingcart widget!
{% endblock %}

我怎样才能做到这一点?有人有这样的经历吗?我已经看过Symfony CMF项目,但它对此没有支持(据我所知)。

我有类似的东西,我认为这段代码会对您有所帮助。在选定的模板中,您可以获得具有块名称的变量。

public function render()
{       
    $modules = $this->moduleService->getModules();
    foreach($modules as $m){
        $templateName = $m->getTemplateName();
        $template = $this->twig->loadTemplate($templateName);
        $blockNames = $template->getBlockNames();
        foreach($blockNames as $b){
            if(isset($this->blocks[$b]) == false)
                $this->blocks[$b] = '';
            $this->blocks[$b] .= $template->renderBlock($b, array('a' => 'aaa', 'b' => 'bbb'));
        }
    }
    $content = $this->twig->render('Admin/index.html.twig',$this->blocks);
    return new SymfonyComponentHttpFoundationResponse($content);
}

我知道这是旧的,但如果有人在寻找这样的东西,SonataBlockBundle可能是你的解决方案。

https://github.com/sonata-project/SonataBlockBundle