当数据通过GET或POST发送到Magento扩展时,使用MVC验证数据、基于某些业务逻辑处理数据然后将结果输出到屏幕的正确方法是什么?
据我所知,控制器是接收和验证所提交数据的正确位置(也许需要调用模型来进行实际验证)。模型是任何业务逻辑的正确位置,接受请求参数并进行处理。块是准备输出数据的正确位置,它可以从模型中请求输出数据。
我了解控制器如何接收发布的数据并将其转发到模型
class Company_Project_IndexController extends Mage_Core_Controller_Front_Action
{
public function receivePostedData()
{
$model = Mage::getModel('project/somemodel');
if($model->validateData( $this->getRequest()->getPost('post_vars') )) {
$model->processData( $this->getRequest()->getPost('post_vars') );
}
}
}
然后模型将处理
class Company_Project_Model_Somemodel extends Mage_Core_Model_Abstract
{
public function validate( $data )
{
//return true of false if data is valid/invalid
}
public function processData( $data )
{
//Do something with the data
}
public function getData()
{
//return something
}
}
我还了解块如何实例化模型并从中获取数据
class Company_Project_Block_Display extends Mage_Core_Block_Template
{
public function getData()
{
$model = Mage::getModel('project/somemodel');
return $model->getData();
}
}
我遇到的问题是控制器和模型协同工作以及块和模型协同运行之间的脱节。如果控制器正在将数据推送到改变模型状态的模型中,那么我如何根据改变的状态将数据从模型中获取到块中?
在我看来,有以下可能的解决方案:
- 使用Mage::getSingleton获取我的模型,这样我就引用了控制器和块中的同一个实例
- 直接参考模型或块内部的帖子数据
- 使用注册表存储一些模型状态
我知道上面的任何一个是否正确,或者我应该使用哪一个。
我已经查看了核心目录搜索模块,因为它正是我所需要的,但我完全迷失在代码中。
我知道还有其他几个与此非常相似的问题被问到并得到了回答,但我仍然蒙在鼓里。
这是一个很好的问题,但没有一个明确的答案。如果你看看Magento核心本身,你会发现使用了各种方法。话虽如此,这里有一些关于权衡的一般建议/背景。
根据使用情况,Magento的控制器操作方法是而不是设计用于与视图交互(或者在Magento中称为布局)。相反,控制器操作是指作为特定URL的主要入口点,然后该URL应该与请求交互并处理模型。完成后,控制器操作然后通过调用来告诉布局渲染
$this->loadLayout();
$this->renderLayout();
按照设计(这只是一种观点)布局与系统的其他部分完全解耦。CodeIgniter/Kohana风格的系统具有"哑视图",它从控制器操作中获取变量,并对其进行简单的模板替换。另一方面,Magento布局是嵌套块对象的集合,块对象方法旨在在模型需要信息时直接查询。
例如,看看Mage_Adminhtml_Block_Catalog_Product_Attribute_Set_Main_Formset
块
#File: app/code/core/Mage/Adminhtml/Block/Catalog/Product/Attribute/Set/Main/Formset.php
protected function _prepareForm()
{
$data = Mage::getModel('eav/entity_attribute_set')
->load($this->getRequest()->getParam('id'));
//...
}
这里的想法是,无论控制器动作中发生了什么,Mage_Adminhtml_Block_Catalog_Product_Attribute_Set_Main_Formset
块在渲染时都将始终获取最新的eav/entity_attribute_set
模型信息(如上所述,访问请求对象以获取请求/发布数据)。控制器动作和视图彼此解耦。
按照设计,是使用该系统的正确方式。不幸的是,从性能的角度来看,它也是无效的。对于来自其他系统的开发人员来说,视图渲染引发另一轮SQL查询的想法似乎很疯狂。Magento著名的"开始拍摄并让缓存层"解决方法遇到了喜忧参半的结果。
解决这种潜在性能问题的一种方法是将已经实例化的模型隐藏在注册表中。你可以在管理控制台的产品编辑控制器中看到一个例子
#File: app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php
protected function _initProduct()
{
//...
Mage::register('current_product', $product);
//...
}
然后它在许多产生的块中的使用,这些块呈现产品的编辑形式
app/code/core/Mage/Adminhtml/Block/Catalog/Product/Edit/Js.php
public function getProduct()
{
return Mage::registry('current_product');
}
这里的折衷是更好的SQL性能(对块没有额外的查询),但您实际上已经将这些块与定义current_product
注册表项的控制器操作绑定在一起。Magento注册表(本质上)是一种处理全局变量的奇特方式,因此您会遇到全局变量固有的大多数问题。正如您所指出的,使用getSingleton
方法可以获得类似的结果,但根据您试图完成的任务,这可能不合适。
值得一提的是,对于管理控制台应用程序(Mage_Adminhtml
模块)和Magento Mobile控制器(Mage_XmlConnect
模块),注册表/单例方法似乎受到核心开发人员的青睐,而更偏执的"显式加载模型"模式在前端购物车应用程序中使用得更多。这是巧合,是有意识的选择,还是核心团队自己在不断学习,这可能是无法回答的问题之一。
最后,有一种技术核心代码没有太多使用,但当我在寻找更多的"愚蠢视图"行为时,我有点喜欢它,那就是这个。加载布局之后,但在渲染之前,可以按名称访问特定块。这与神奇的getter和setter的力量相结合,可以让你做一些类似的事情
$this->loadLayout();
$content = $this->getLayout()->getBlock('content');
if($content)
{
$content->setSomeValue('Hello World');
}
$this->renderLayout();
然后从你的块的模板中,你可以用获取变量
echo $this->getSomeValue();
您仍然将块的实现与控制器操作联系在一起,但这样做的方式不太全局。此外,解耦布局可能不包含您要查找的块,因此您需要小心处理。