手机浏览 RSS 2.0 订阅 膘叔的简单人生 , 腾讯云RDS购买 | 超便宜的Vultr , 注册 | 登陆
浏览模式: 标准 | 列表Tag:yii

Best MVC Practices

这是一篇来自Yii官方的文章,写在了1.1.6的new fetures里面,值得一看,最起码,你在看完后,下次被人面试的时候,问到什么 是MVC,你可以有回答的东西了。

不多说,上原文,内容也不复杂,所以我就不翻译了(其实是我翻译不来,将就着看看可以,可是要翻译成人人都能看得懂的内容就难了)。。。

原文来自于:http://www.yiiframework.com/doc/guide/1.1/en/basics.best-practices,目前官方也确实没有中文版。

Although Model-View-Controller (MVC) is known by nearly every Web developer, how to properly use MVC in real application development still eludes many people. The central idea behind MVC is code reusability and separation of concerns. In this section, we describe some general guidelines on how to better follow MVC when developing a Yii application.

To better explain these guidelines, we assume a Web application consists of several sub-applications, such as

  • front end: a public-facing website for normal end users;
  • back end: a website that exposes administrative functionality for managing the application. This is usually restricted to administrative staff;
  • console: an application consisting of console commands to be run in a terminal window or as scheduled jobs to support the whole application;
  • Web API: providing interfaces to third parties for integrating with the application.

The sub-applications may be implemented in terms of modules, or as a Yii application that shares some code with other sub-applications.

1. Model 

Models represent the underlying data structure of a Web application. Models are often shared among different sub-applications of a Web application. For example, a LoginForm model may be used by both the front end and the back end of an application; a News model may be used by the console commands, Web APIs, and the front/back end of an application. Therefore, models

  • should contain properties to represent specific data;

  • should contain business logic (e.g. validation rules) to ensure the represented data fulfills the design requirement;

  • may contain code for manipulating data. For example, a SearchForm model, besides representing the search input data, may contain a search method to implement the actual search.

Sometimes, following the last rule above may make a model very fat, containing too much code in a single class. It may also make the model hard to maintain if the code it contains serves different purposes. For example, a News model may contain a method named getLatestNews which is only used by the front end; it may also contain a method named getDeletedNews which is only used by the back end. This may be fine for an application of small to medium size. For large applications, the following strategy may be used to make models more maintainable:

  • Define a NewsBase model class which only contains code shared by different sub-applications (e.g. front end, back end);

  • In each sub-application, define a News model by extending from NewsBase. Place all of the code that is specific to the sub-application in this News model.

So, if we were to employ this strategy in our above example, we would add a News model in the front end application that contains only the getLatestNews method, and we would add another News model in the back end application, which contains only the getDeletedNews method.

In general, models should not contain logic that deals directly with end users. More specifically, models

  • should not use $_GET, $_POST, or other similar variables that are directly tied to the end-user request. Remember that a model may be used by a totally different sub-application (e.g. unit test, Web API) that may not use these variables to represent user requests. These variables pertaining to the user request should be handled by the Controller.

  • should avoid embedding HTML or other presentational code. Because presentational code varies according to end user requirements (e.g. front end and back end may show the detail of a news in completely different formats), it is better taken care of by views.

2. View 

Views are responsible for presenting models in the format that end users desire. In general, views

  • should mainly contain presentational code, such as HTML, and simple PHP code to traverse, format and render data;

  • should avoid containing code that performs explicit DB queries. Such code is better placed in models.

  • should avoid direct access to $_GET, $_POST, or other similar variables that represent the end user request. This is the controller's job. The view should be focused on the display and layout of the data provided to it by the controller and/or model, but not attempting to access request variables or the database directly.

  • may access properties and methods of controllers and models directly. However, this should be done only for the purpose of presentation.

Views can be reused in different ways:

  • Layout: common presentational areas (e.g. page header, footer) can be put in a layout view.

  • Partial views: use partial views (views that are not decorated by layouts) to reuse fragments of presentational code. For example, we use _form.php partial view to render the model input form that is used in both model creation and updating pages.

  • Widgets: if a lot of logic is needed to present a partial view, the partial view can be turned into a widget whose class file is the best place to contain this logic. For widgets that generate a lot of HTML markup, it is best to use view files specific to the widget to contain the markup.

  • Helper classes: in views we often need some code snippets to do tiny tasks such as formatting data or generating HTML tags. Rather than placing this code directly into the view files, a better approach is to place all of these code snippets in a view helper class. Then, just use the helper class in your view files. Yii provides an example of this approach. Yii has a powerful CHtml helper class that can produce commonly used HTML code. Helper classes may be put in an autoloadable directory so that they can be used without explicit class inclusion.

3. Controller 

Controllers are the glue that binds models, views and other components together into a runnable application. Controllers are responsible for dealing directly with end user requests. Therefore, controllers

  • may access $_GET, $_POST and other PHP variables that represent user requests;

  • may create model instances and manage their life cycles. For example, in a typical model update action, the controller may first create the model instance; then populate the model with the user input from $_POST; after saving the model successfully, the controller may redirect the user browser to the model detail page. Note that the actual implementation of saving a model should be located in the model instead of the controller.

  • should avoid containing embedded SQL statements, which are better kept in models.

  • should avoid containing any HTML or any other presentational markup. This is better kept in views.

In a well-designed MVC application, controllers are often very thin, containing probably only a few dozen lines of code; while models are very fat, containing most of the code responsible for representing and manipulating the data. This is because the data structure and business logic represented by models is typically very specific to the particular application, and needs to be heavily customized to meet the specific application requirements; while controller logic often follows a similar pattern across applications and therefore may well be simplified by the underlying framework or the base classes.

Tags: yii

Yii:relations update(self::STAT)

今天在群里有朋友问我yii的relations的问题,结果贴出来的代码居然是类似这样的:

PHP代码
  1. function relations(){  
  2.     return array(  
  3.         'xx'=>array(self::STAT,'aa','aa_id')  
  4.     );  
  5. }  

我当时直接了当的对他说,错了,怎么可能会有self::STAT呢?只有has_many,has_one,belongs_to,many_many,stat是错的。结果他回答说我OUT了,STAT是新加入的。。。

于是欣欣然跑到YII的官网查看了一下GUIDE,果然在 Relational Active Record,一节中发现了这个Statistical Query

官方怎么说来着?第一句就让我大吃一惊:

Note: Statistical query has been supported since version 1.0.4.

奇怪,以前怎么没注意?

Besides the relational query described above, Yii also supports the so-called statistical query (or aggregational query). It refers to retrieving the aggregational information about the related objects, such as the number of comments for each post, the average rating for each product, etc. Statistical query can only be performed for objects related in HAS_MANY (e.g. a post has many comments) or MANY_MANY (e.g. a post belongs to many categories and a category has many posts).

Performing statistical query is very similar to performing relation query as we described before. We first need to declare the statistical query in the relations() method of CActiveRecord like we do with relational query.

PHP代码
  1. class Post extends CActiveRecord  
  2. {  
  3.     public function relations()  
  4.     {  
  5.         return array(  
  6.             'commentCount'=>array(self::STAT, 'Comment''post_id'),  
  7.             'categoryCount'=>array(self::STAT, 'Category''post_category(post_id, category_id)'),  
  8.         );  
  9.     }  
  10. }  

In the above, we declare two statistical queries: commentCount calculates the number of comments belonging to a post, and categoryCount calculates the number of categories that a post belongs to. Note that the relationship between Post and Comment is HAS_MANY, while the relationship between Post and Category is MANY_MANY (with the joining table post_category). As we can see, the declaration is very similar to those relations we described in earlier subsections. The only difference is that the relation type is STAT here.

With the above declaration, we can retrieve the number of comments for a post using the expression $post->commentCount. When we access this property for the first time, a SQL statement will be executed implicitly to retrieve the corresponding result. As we already know, this is the so-called lazy loading approach. We can also use the eager loading approach if we need to determine the comment count for multiple posts:

PHP代码
  1. $posts=Post::model()->with('commentCount''categoryCount')->findAll();  

The above statement will execute three SQLs to bring back all posts together with their comment counts and category counts. Using the lazy loading approach, we would end up with 2*N+1 SQL queries if there are N posts.

By default, a statistical query will calculate the COUNT expression (and thus the comment count and category count in the above example). We can customize it by specifying additional options when we declare it in relations(). The available options are summarized as below.

  • select: the statistical expression. Defaults to COUNT(*), meaning the count of child objects.

  • defaultValue: the value to be assigned to those records that do not receive a statistical query result. For example, if a post does not have any comments, its commentCount would receive this value. The default value for this option is 0.

  • condition: the WHERE clause. It defaults to empty.

  • params: the parameters to be bound to the generated SQL statement. This should be given as an array of name-value pairs.

  • order: the ORDER BY clause. It defaults to empty.

  • group: the GROUP BY clause. It defaults to empty.

  • having: the HAVING clause. It defaults to empty.

关键的是看最后这几个条件,可以让你知道这些参数如何配置。。。
官方的GUIDE地址:http://www.yiiframework.com/doc/guide/1.1/en/database.arr

Tags: yii, relations, stat, static query

Yii的config配置的一些记录

用YII开发的时候,由于会用到gii之类的工具,所以assets目录,仿佛就一定会需要存在了,但是assets这个目录名,会可能与我们自己的一些image,css之类的目录存在冲突(我是指目录太多了,不易管理,虽然这个目录似乎也不需要管理)
这时候,我们可以通过更改config来设置。
在config的components里加入:

XML/HTML代码
  1. 'assetManager'=>array(  
  2.     'basePath' => 'xxxxxx/xxx'  
  3. )  

OK,我就这样把它与我存放image/css/js的目录合并在一起了。

模版,如果不想过多的折腾,我想,官方推荐的prado这样的伪PHP模版其实也不错,最起码常用的操作都封装了,也不用担心与其他的模版是否会存在这样那样的冲突,最起码他们的整合应该算是最好的。所以。。。加上:

PHP代码
  1. 'viewRenderer' => array(  
  2.     'class' => 'CPradoViewRenderer',  
  3. ),  

不用过多的担心效率,它也是编译到runtime目录下执行的。。。

params,这个嘛。。。直接引用文件吧,其实在自动生成的代码里,就是这样配置的,直接引用文件有一个好处就是,你可以把系统的配置信息扔到一个文件里,然后让params来加载。就方便很多了。

Tags: yii, config

开发笔记记录

在开发的时候会遇到很多问题,比如就象今天,某个项目中用了swfupload,是集成的uploadify(可能打错了)?其他时候都正常,就突然今天出了点问题,因为昨天改版上线,今天被人发现了大的BUG,那就是商品不能上传图片了。这个问题非常严重,因为影响了用户的正常使用,但是我在测试的时候却没有发现任何 问题,照样正常上传,一下子就感觉特郁闷。
最后,客户截图出来,显示security error,然后问了一下,用的是IE。哦。。。我用的是firefox。于是換成IE测试,果然出现这种情况。
找了很多原因,都没有发现问题在哪里,JS啥的都没有出错,原来是正常的,现在是只有FIREFOX正常,这究竟是什么 原因 呢?
排查了很久,而且到最后就差要单独拎出来重写了,意外之中突然发现,URL里有两个斜杠,比如当前的页面可能就是类似 这样:http://www.neatstudio.com//upload/image/?act=upload,在host后面有两个斜杠。难道是这个原因 ?于是把那个斜杠去掉,再测试,结果真的成功了。现在想想,好象很多人都会忽略这个问题,但我就真的发现了,分析了一下源码,发现还真有这可能,因为它源码里是的路径是类似:../js/upload/swfobject.js。在URL里有//的时候,它解析的时候,可能会有偏差(没有仔细看,昨天一夜没睡,发现问题后立马解决了它了),对于路径就进行了处理,比如rtrim()之类的,搞定,开心的回家了。。

然后说一点yii中遇到的问题,比如获取当前controller的ID,就是Yii::app()->controller->id,如果获取当前的action的ID,那就是Yii::app()->controller->action->id。也有人说是$this->actionId,我是没有成功过。。
当然,要记得,在init()方法时在,这些都是获取不到的,毕竟,人家还没有初始化,怎么可能有呢,于是我用的是Yii::app()->getRequest()->getPathInfo(),然后explode一下"/",弹出的最后一个就是action,前面的是controller,看了一下源码,官方获取,好象也是这样获取的。黑黑。。。。

好了,做完记录,睡觉了。。

Tags: swfupload, yii, controller, action

Yii ClinkPager 郁闷

开发的时候,分页用了CLinkPager,然而在某一个页面的时候,page一直在变,但是下面的分类中的当前页永远在1上,不随着页码的变化而变化。
开始的时候以为是分页程序有问题,仔细对应了一下,发现分页的数据是正确的。
排查了半个多小时,突然想起,会不会$_GET['page']被unset了?
找了一下页面,最后在模版页里居然真的发现了unset($_GET['page']),当时我就傻眼了。
顺便再上一个option的onchange切换函数。很烂,只求先解决问题。。。。

JavaScript代码
  1. function urlchange(field,value) {  
  2.     var href= location.href;  
  3.     var regex = new RegExp(field+"=[\-|0-9]{0,}");  
  4.     if(href.indexOf(field)!=-1){  
  5.         location.href = href.replace(regex,field+"="+value);  
  6.     }else{  
  7.         location.href = href + "&"+field+"="+value;  
  8.     }  
  9. }      

用法就是<select onchange="urlchange('page',this.value)"><option value='1'>第一页</option><option value='2'>第二页</option><option value='10'>第十页</option></select>

Tags: yii, clinkpager