用例
我正在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