MVC的view层的功能一样就是负责显示,可以认为是用户的输入输出。是用户直接使用操作的地方。大部分功能就是把数据如何更有好的展示给用户,如何更容易的让用户操作,输入反馈数据,完成交互功能。
V(view)层和C(controller)控制器是紧密相连的。C在需要的时候调用V来显示数据,也可以在需要的时候调用V来获取用户的数据。如果C是灵魂,那么V就是一个肉体。两者是不可分割的一部分。
1.yii中view的存储位置以及规则
打开testwebap项目
/www/yii_dev/testwebap/protected# tree views
views
├── layouts
│ ├── column1.php
│ ├── column2.php
│ └── main.php
├── site
│ ├── contact.php
│ ├── error.php
│ ├── index.php
│ ├── login.php
│ └── pages
│ └── about.php
├── testTest
│ ├── action1.php
│ ├── action2.php
│ ├── action3.php
│ └── index.php
└── user
├── admin.php
├── create.php
├── _form.php
├── index.php
├── _search.php
├── update.php
├── userformtest.php
├── _view.php
└── view.php
5 directories, 21 files
/www/yii_dev/testwebap/protected# tree modules/
modules/
└── testmod
├── components
├── controllers
│ └── DefaultController.php
├── messages
├── models
├── TestmodModule.php
└── views
├── default
│ ├── index.php
└── layouts
8 directories, 1 files
可以看到:
一般在项目的protected的views文件夹中。如果有modules,放在protected/modules/views中.
结构一般view下对应的是以contoller的名字命名的文件夹。下面存放此contoller使用的相应文件。
2.Controller是如何调用View。
打开SiteController.php
/**
* This is the default 'index' action that is invoked
* when an action is not explicitly requested by users.
*/
public function actionIndex()
{
// renders the view file 'protected/views/site/index.php'
// using the default layout 'protected/views/layouts/main.php'
$this->render('index');
}
可以看到是通过
$this->render函数来调用view。
3。Controller调用View的实现原理
打开render方法看一下。
public function render($view,$data=null,$return=false)
{
if($this->beforeRender($view))
{
$output=$this->renderPartial($view,$data,true);
if(($layoutFile=$this->getLayoutFile($this->layout))!==false)
$output=$this->renderFile($layoutFile,array('content'=>$output),true);
$this->afterRender($view,$output);
$output=$this->processOutput($output);
if($return)
return $output;
else
echo $output;
}
}
protected function beforeRender($view)
{
return true;
}
protected function afterRender($view, &$output)
{
}
public function renderPartial($view,$data=null,$return=false,$processOutput=false)
{
if(($viewFile=$this->getViewFile($view))!==false)
{
$output=$this->renderFile($viewFile,$data,true);
if($processOutput)
$output=$this->processOutput($output);
if($return)
return $output;
else
echo $output;
}
else
throw new CException(Yii::t('yii','{controller} cannot find the requested view "{view}".',
array('{controller}'=>get_class($this), '{view}'=>$view)));
}
public function getViewFile($viewName)
{
if(($theme=Yii::app()->getTheme())!==null && ($viewFile=$theme->getViewFile($this,$viewName))!==false)
return $viewFile;
$moduleViewPath=$basePath=Yii::app()->getViewPath();
if(($module=$this->getModule())!==null)
$moduleViewPath=$module->getViewPath();
return $this->resolveViewFile($viewName,$this->getViewPath(),$basePath,$moduleViewPath);
}
public function getLayoutFile($layoutName)
{
if($layoutName===false)
return false;
if(($theme=Yii::app()->getTheme())!==null && ($layoutFile=$theme->getLayoutFile($this,$layoutName))!==false)
return $layoutFile;
if(empty($layoutName))
{
$module=$this->getModule();
while($module!==null)
{
if($module->layout===false)
return false;
if(!empty($module->layout))
break;
$module=$module->getParentModule();
}
if($module===null)
$module=Yii::app();
$layoutName=$module->layout;
}
else if(($module=$this->getModule())===null)
$module=Yii::app();
return $this->resolveViewFile($layoutName,$module->getLayoutPath(),Yii::app()->getViewPath(),$module->getViewPath());
}
public function resolveViewFile($viewName,$viewPath,$basePath,$moduleViewPath=null)
{
if(empty($viewName))
return false;
if($moduleViewPath===null)
$moduleViewPath=$basePath;
if(($renderer=Yii::app()->getViewRenderer())!==null)
$extension=$renderer->fileExtension;
else
$extension='.php';
if($viewName[0]==='/')
{
if(strncmp($viewName,'//',2)===0)
$viewFile=$basePath.$viewName;
else
$viewFile=$moduleViewPath.$viewName;
}
else if(strpos($viewName,'.'))
$viewFile=Yii::getPathOfAlias($viewName);
else
$viewFile=$viewPath.DIRECTORY_SEPARATOR.$viewName;
if(is_file($viewFile.$extension))
return Yii::app()->findLocalizedFile($viewFile.$extension);
else if($extension!=='.php' && is_file($viewFile.'.php'))
return Yii::app()->findLocalizedFile($viewFile.'.php');
else
return false;
}
public function renderFile($viewFile,$data=null,$return=false)
{
$widgetCount=count($this->_widgetStack);
if(($renderer=Yii::app()->getViewRenderer())!==null && $renderer->fileExtension==='.'.CFileHelper::getExtension($viewFile))
$content=$renderer->renderFile($this,$viewFile,$data,$return);
else
$content=$this->renderInternal($viewFile,$data,$return);
if(count($this->_widgetStack)===$widgetCount)
return $content;
else
{
$widget=end($this->_widgetStack);
throw new CException(Yii::t('yii','{controller} contains improperly nested widget tags in its view "{view}". A {widget} widget does not have an endWidget() call.',
array('{controller}'=>get_class($this), '{view}'=>$viewFile, '{widget}'=>get_class($widget))));
}
}
public function renderInternal($_viewFile_,$_data_=null,$_return_=false)
{
// we use special variable names here to avoid conflict when extracting data
if(is_array($_data_))
extract($_data_,EXTR_PREFIX_SAME,'data');
else
$data=$_data_;
if($_return_)
{
ob_start();
ob_implicit_flush(false);
require($_viewFile_);
return ob_get_clean();
}
else
require($_viewFile_);
}
public function processOutput($output)
{
Yii::app()->getClientScript()->render($output);
// if using page caching, we should delay dynamic output replacement
if($this->_dynamicOutput!==null && $this->isCachingStackEmpty())
{
$output=$this->processDynamicOutput($output);
$this->_dynamicOutput=null;
}
if($this->_pageStates===null)
$this->_pageStates=$this->loadPageStates();
if(!empty($this->_pageStates))
$this->savePageStates($this->_pageStates,$output);
return $output;
}
public function processDynamicOutput($output)
{
if($this->_dynamicOutput)
{
$output=preg_replace_callback('/<###dynamic-(\d+)###>/',array($this,'replaceDynamicOutput'),$output);
}
return $output;
}
protected function replaceDynamicOutput($matches)
{
$content=$matches[0];
if(isset($this->_dynamicOutput[$matches[1]]))
{
$content=$this->_dynamicOutput[$matches[1]];
$this->_dynamicOutput[$matches[1]]=null;
}
return $content;
}
lic function findLocalizedFile($srcFile,$srcLanguage=null,$language=null)
{
if($srcLanguage===null)
$srcLanguage=$this->sourceLanguage;
if($language===null)
$language=$this->getLanguage();
if($language===$srcLanguage)
return $srcFile;
$desiredFile=dirname($srcFile).DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.basename($srcFile);
return is_file($desiredFile) ? $desiredFile : $srcFile;
}
以上已经按照颜色传递做了简单的流程说明。你应该大体了解实现方法了。上面主要标记的是view文件的引用方法和相应的数据的传递方式。应该不难理解。当然还有附属的layout,widget,页面缓存和国际化等等处理。可以自己追踪代码看看具体的实现方法这里只是简单说明。便于理解。明白在controller中调用render可以解析到指定的view文件,并把数据传递过去。我们做的就是在view中如何展示。