Php 在Symfony 2控制器中抽象通用功能的正确方法是什么
我们有一个相当大的symfony2代码库。通常,我们的控制器动作看起来像Php 在Symfony 2控制器中抽象通用功能的正确方法是什么,php,symfony,Php,Symfony,我们有一个相当大的symfony2代码库。通常,我们的控制器动作看起来像 public function landingPageAction(Request $request) { //do stuff return $this->render("view_to_render", $template_data); } 我们所有控制器之间有两个非常通用的功能: 我们倾向于将控制器级模板参数传递给特定控制器中的所有操作——我们称这些为“默认参数” 我们在每个操作的末尾设置HT
public function landingPageAction(Request $request) {
//do stuff
return $this->render("view_to_render", $template_data);
}
我们所有控制器之间有两个非常通用的功能:
class TemplateVariables {
protected $template_name;
protected $template_data;
public function __construct($template_name, $template_data) {
$this->template_name = $template_name;
$this->template_data = $template_data;
}
/**
* @param mixed $template_data
* @return $this
*/
public function setTemplateData($template_data) {
$this->template_data = $template_data;
return $this;
}
/**
* @return mixed
*/
public function getTemplateData() {
return $this->template_data;
}
/**
* @param mixed $template_name
* @return $this
*/
public function setTemplateName($template_name) {
$this->template_name = $template_name;
return $this;
}
/**
* @return mixed
*/
public function getTemplateName() {
return $this->template_name;
}
}
我们还定义了在渲染时触发的事件以及调用视图的事件
class InjectDefaultTemplateVariablesControllerEventListener {
/** @var DelegatingEngine */
private $templating;
private $default_template_variables;
public function __construct($templating) {
$this->templating = $templating;
}
public function onKernelController(FilterControllerEvent $event) {
$controller = $event->getController();
if (!is_array($controller)) {
return;
}
if ($controller[0] instanceof InjectDefaultTemplateVariablesController) {
$this->default_template_variables = $controller[0]->getDefaultTemplateVariables($event->getRequest());
}
}
public function onKernelView(GetResponseForControllerResultEvent $event) {
$controller_data = $event->getControllerResult();
if ($controller_data instanceof TemplateVariables) {
$template_data = (array)$controller_data->getTemplateData();
$template_data = array_merge($this->default_template_variables, $template_data);
$event->setResponse($this->templating->renderResponse($controller_data->getTemplateName(), $template_data));
}
}
}
现在我们的行动终于变成了现实
public function landingPageAction(Request $request) {
//do stuff
return new TemplateVariables("view_to_render", $template_data);
}
public function landingPageAction(Request $request) {
//do stuff
return $this->renderWithDefaultsAndCache("view_to_render", $template_data);
}
方法2
这种方法基于将公共逻辑放入BaseController中,其他所有控制器都从中继承。我们仍然保留让子控制器也扩展接口的方法,以防它们想要使用“默认参数”
以下是基本控制器中的新方法,用于确定是否需要将默认参数与特定模板参数合并。稍后,此方法还将使用ttl参数处理缓存头
public function renderWithDefaultsAndCache($view, array $parameters = array(), Response $response = null, $ttl = null)
{
$default_template_variables = array();
if ($this instanceof InjectDefaultTemplateVariablesController ) {
$default_template_variables = $this->getDefaultTemplateVariables();
}
$template_data = array_merge($default_template_variables, $parameters);
return $this->render($view, $template_data, $response);
}
现在行动变得更加重要
public function landingPageAction(Request $request) {
//do stuff
return new TemplateVariables("view_to_render", $template_data);
}
public function landingPageAction(Request $request) {
//do stuff
return $this->renderWithDefaultsAndCache("view_to_render", $template_data);
}
讨论
到目前为止,第一种方法的主要论点是它遵循坚实的原则,并且更容易扩展-如果要添加更多的通用逻辑,它可以直接放入事件侦听器,而不会影响控制器
第二种方法的主要论点是,我们试图抽象的逻辑实际上属于控制器,而不是外部事件。此外,有人担心以这种方式使用事件会导致性能不佳
我们非常希望听取专家们关于哪种方法更好,或者可能建议我们错过的第三种方法的意见
谢谢大家! 首先,我绝不会自称是Symfony 2架构专家 我有一个比赛时间表程序,它输出许多不同类型的时间表(公众、团队、裁判等)。不同的时间表都是相似的,它们处理一组游戏,但在细节上有所不同。时间表需要以各种格式显示(html、pdf、xls等)。我还希望能够为个人锦标赛做进一步的调整 我最初使用您的第二种方法,创建ScheduleBaseController,然后从中派生各种单独的日程控制器。效果不太好。我试图抽象出常见的功能,但日程安排不同,以至于常见的功能变得复杂且难以更新 因此,我采用了一种与您非常相似的事件驱动方法。为了回答您的一个问题,添加一些事件侦听器不会对性能产生任何明显的影响 我没有专注于模板数据,而是创建了我称之为动作模型的东西。动作模型负责根据请求参数加载游戏,并(在某些情况下)根据发布的数据更新游戏本身 动作模型在控制器事件侦听器中创建,存储在请求对象中,然后作为参数传递给控制器的动作方法
// KernelEvents::CONTROLLER listener
$modelFactoryServiceId = $request->attributes->get('_model');
$modelFactory = $this->container->get($modelFactoryServiceId);
$model = $modelFactory->create($request);
$request->attributes->set('model',$model);
// Controller action
public function action($request,$model)
{
// do stuff
// No template processing at all, just return null
return null;
}
// KernelEvents::VIEW listener
$model = $request->attributes->get('model')
$response = $view->renderResponse($model);
因此,控制器主要负责表单内容。如果需要,它可以从模型中获取数据,但是让我们让模型来处理大部分与数据相关的内容。控制器根本不进行模板处理。它只返回null,这将启动视图事件进行渲染
很多东西?当然。关键是在路线定义中将其连接起来:
// Referee Schedule Route
cerad_game__project__schedule_referee__show:
path: /project/{_project}/schedule-referee.{_format}
defaults:
_controller: cerad_game__project__schedule_referee__show_controller:action
_model: cerad_game__project__schedule_referee__show_model_factory
_form: cerad_game__project__schedule_referee__show_form_factory
_template: '@CeradGame\Project\Schedule\Referee\Show\ScheduleRefereeShowTwigPage.html.twig'
_format: html
_views:
csv: cerad_game__project__schedule_referee__show_view_csv
xls: cerad_game__project__schedule_referee__show_view_xls
html: cerad_game__project__schedule_referee__show_view_html
requirements:
_format: html|csv|xls|pdf
每个部分都被分解成单独的服务,至少对我来说,这使得定制单独的部分和查看正在发生的事情变得更容易。这是一个好方法吗?我真的不知道,但对我来说效果很好。Cerad,谢谢你,这是非常有用的信息!一个澄清:看起来您有一个创建模型的服务,并且模型也知道如何呈现自己。控制器中到底发生了什么,因为模板变量似乎是由模型计算出来的。非常感谢。实际上,视图渲染模型。模型主要是一个数据库,只提供数据。在控制器中实际完成的工作很少。在某些情况下,action方法不起任何作用。更常见的是会涉及到一个表单。action方法将执行isValid操作,并根据需要重定向。