专家:一个重构的故事(3/5)
今天,我们将首先重构的一些行为产品
模块。
symob娱乐下载fony仿照MVC设计模式,文斯非常自信,他的关注点分离良好。
控制器的作用是让来自模型的数据并将它们传递给视图。
很简单,不是吗?
很多人知道这个理论但很难跟随它的信在实践中;很容易让一些业务逻辑陷入控制器。和文斯也不例外。
薄的控制器,脂肪模型
网站的首页是由所有可用的产品和当前的列表的代码包含在管理这个页面指数
行动:
/ /应用程序/前端/模块/产品/ actions.class.php公共函数executeIndex(){/ /获得所有可用的产品美元标准=新标准();美元标准- >添加(ProductPeer::IS_IN_STOCK,真正的);美元标准- >addAscendingOrderByColumn(ProductPeer::价格);美元标准- >setLimit(10);这个美元- >产品= ProductPeer::doSelectJoinCategory(美元标准);这个美元- >getResponse()- >setTitle(“所有产品”);这个美元- >getResponse()- >addStylesheet(“homepage.css”);返回sfView::成功;}
但是,标准
定义和调用doSelectJoinCategory
不属于控制器,它属于模型。
所以,文斯决定将这段代码ProductPeer
类通过创建一个getAvailableProducts ()
方法:
/ / lib /模型/ ProductPeer.php静态公共函数getAvailableProducts(){美元标准=新标准();美元标准- >添加(自我::IS_IN_STOCK,真正的);美元标准- >addAscendingOrderByColumn(自我::价格);美元标准- >setLimit(10);返回自我::doSelectJoinCategory(美元标准);}
和控制器现在可以直接使用这个新方法:
/ /应用程序/前端/模块/产品/ actions.class.php公共函数executeIndex(){这个美元- >产品= ProductPeer::getAvailableProducts();这个美元- >getResponse()- >setTitle(“所有产品”);这个美元- >getResponse()- >addStylesheet(“homepage.css”);返回sfView::成功;}
这种重构代码前有几个好处:
- 逻辑来获得可用的产品现在在模型中,属于他们的权利
- 控制器中的代码更可读
- 代码可读性,我们不需要文档了
- 的
getAvailableProducts ()
方法是可重用(在另一个动作,或者在任务,…) - 现在模型代码单元测试
- 两个开发人员可以协同工作:
- 一个负责代码模型和创建一个公共API
- 另一个使用公共API代码控制器模型。她并不关心如何检索产品(数据库、XML、Web服务…)
但是如果你需要所有可用的产品在另一个操作的列表,或者如果你只需要一分之五的吗?你会想要重用相同的方法但现在,它不是很灵活的极限(10
)是硬编码的。
是不错的保持这种方式,但是一旦文斯将需要得到同样的结果,但略有不同的条件下,他需要重构新方法添加一些参数:
/ / lib /模型/ ProductPeer.php静态公共函数getAvailableProducts(美元最大=10){美元标准=新标准();美元标准- >添加(自我::IS_IN_STOCK,真正的);美元标准- >addAscendingOrderByColumn(自我::价格);美元标准- >setLimit(美元最大);返回自我::doSelectJoinCategory(美元标准);}
你懂的。我们的目标是能够在不同的上下文中重用相同的代码。第一步是重构代码正确的层,然后添加一些方法来定制结果通过添加一些参数。
这个很容易。让我们重构代码的另一部分。
薄的控制器,脂肪模型,再现
时间看一看最喜欢的管理代码。
最爱的代码来添加和删除产品中发现的产品
模块:
/ /应用程序/前端/模块/产品/ actions.class.php公共函数executeAddToFavorites(){美元的产品= ProductPeer::retrieveByPk(这个美元- >getRequestParameter(“id”));这个美元- >forward404Unless(美元的产品);美元的最爱=这个美元- >getUser()- >getAttribute(“最爱”);美元的最爱(美元的产品- >getId()]=真正的;这个美元- >getUser()- >setAttribute(“最爱”,美元的最爱);这个美元- >重定向(“产品/指数”);}公共函数executeRemoveFromFavorites(){美元的产品= ProductPeer::retrieveByPk(这个美元- >getRequestParameter(“id”));这个美元- >forward404Unless(美元的产品);美元的最爱=这个美元- >getUser()- >getAttribute(“最爱”);设置(美元的最爱(美元的产品- >getId()]);这个美元- >getUser()- >setAttribute(“最爱”,美元的最爱);这个美元- >重定向(“产品/指数”);}
正如你所看到的自己,最喜欢的是存储在用户会话中。但是,太多的逻辑是嵌入在这段代码中。控制器不需要知道,最爱是如何存储的。
所以,文斯决定将这段代码myUser
类通过创建方法来封装的逻辑:
/ /应用程序/前端/ lib / myUser.class.php公共函数addProductToFavorites(产品美元的产品){美元的最爱=这个美元- >getAttribute(“最爱”);美元的最爱(美元的产品- >getId()]=真正的;这个美元- >setAttribute(“最爱”,美元的最爱);}公共函数removeProductFromFavorites(产品美元的产品){美元的最爱=这个美元- >getAttribute(“最爱”);设置(美元的最爱(美元的产品- >getId()]);这个美元- >setAttribute(“最爱”,美元的最爱);}
和重构操作产品
模块类现在使用这个新用户API:
/ /应用程序/前端/模块/产品/ actions.class.php公共函数executeAddToFavorites(){美元的产品= ProductPeer::retrieveByPk(这个美元- >getRequestParameter(“id”));这个美元- >forward404Unless(美元的产品);这个美元- >getUser()- >addProductToFavorites(美元的产品);这个美元- >重定向(“产品/指数”);}公共函数executeRemoveFromFavorites(){美元的产品= ProductPeer::retrieveByPk(这个美元- >getRequestParameter(“id”));这个美元- >forward404Unless(美元的产品);这个美元- >getUser()- >removeProductFromFavorites(美元的产品);这个美元- >重定向(“产品/指数”);}
这种重构像之前那样,我们有相同的好处,但有一个额外的好处:如果我们决定改变属性名称,或如果我们想存储数据库中的最爱,控制器代码不会改变。
和同样的模板。这是处理最爱的代码片段:
/ /应用程序/前端/模块/产品/模板/ indexSuccess.php< ? php如果(in_array(美元的产品- >getId(),中的(sf_user美元- >getAttribute(“最爱”,数组())))):? >< ? php回声link_to(image_tag(“/图片/ favorite.png”),“产品/ removeFromFavorites id = ?”。美元的产品- >getId())? >< ? php其他的:? >< ? php回声link_to(“添加到我的最爱”,“产品/ addToFavorites id = ?”。美元的产品- >getId())? >< ? phpendif;? >
第一行测试如果产品在当前用户收藏夹显示“添加”或“删除”链接的链接。
在这里,我们需要这段代码移动到用户类通过创建另一个方法:
/ /应用程序/前端/ lib / myUser.class.php公共函数hasInFavorites(产品美元的产品){返回in_array(美元的产品- >getId(),中的(这个美元- >getAttribute(“最爱”,数组())));}
这是清理模板片段:
/ /应用程序/前端/模块/产品/模板/ indexSuccess.php< ? php如果(sf_user美元- >hasInFavorites(美元的产品)):? >< ? php回声link_to(image_tag(“/图片/ favorite.png”),“产品/ removeFromFavorites id = ?”。美元的产品- >getId())? >< ? php其他的:? >< ? php回声link_to(“添加到我的最爱”,“产品/ addToFavorites id = ?”。美元的产品- >getId())? >< ? phpendif;? >
我们做了一次。代码可读性更强由于重构和我们选择的方法名称。它是非常重要的思考方法名称。他们传达很多信息,大多数时候,他们可以替换的文档。欧宝官网下载app你不需要解释什么(sf_user - > hasInFavorites美元产品)
所做的事。
这种重构后,所有的代码处理最爱现在在同一个班。这是另一个好处。而不是操纵最爱的代码在很多不同的地方,现在一切都是集中在一个班。你甚至可以重构它进一步通过创建一个专用的ProductFavorite
类(如果需要)。
今天就到这儿了。下次,我和文斯将重构操作,允许修改产品和相关的图片。同时,可以将我们今天学到的规则应用到你自己的symfony项目。ob娱乐下载
评论
评论都关门了。
以确保评论保持相关,他们关闭了旧的帖子。
1)创建一个新产品应该也用ProductPeer吗?我想答案是肯定的。
2)类插件带来的呢?例如,sfGuardUser:我们应该在哪里添加特定的代码,因为我想它不会很高兴硬编码到sfGuardPlugin目录. .
1)是的。
2)创建自己的类扩展模块提供的插件。
但是如果我有两个插件使用sfGuardPlugin CommentPlugin PollPlugin和我。
我需要两种方法:我需要测试,用户是否已经读了一些评论和我需要测试,用户是否已经投票选举。
我不能延长sfGuardSecurityUser因为我不知道,在这订单我将使用插件和哪一个将从另一个扩展。
那是因为我创建了类似“ProducFavorite”——“PollVoter”,“CommentReader”操纵sfContext:: getUser ()……
感谢建议,这是一个非常经验tuto。
产品= ProductPeer:美元:retrieveByPk ($ this - > getRequestParameter (id));
美元$ this - > forward404Unless(产品);
preExecute函数
和
$ this - >重定向(“产品/指数”);
一个postExecute吗?
所以executeAddToFavorites()和executeRemoveFromFavorites()都是精减至一行。
{
返回in_array($产品- > getId()中的($ this - > getAttribute(“最爱”,数组())));
}
我会更好的编写:
公共函数hasInFavorites(产品美元)
{
$最爱= $ this - > getAttribute(“最爱”,数组());
返回收取(最爱美元($产品- > getId ()));
}
这个小小的优化允许这段代码执行O(1),而不是O (n)(其中n是目前最喜欢的数量)。