CBaseController.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. <?php
  2. /**
  3. * CBaseController class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright 2008-2013 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CBaseController is the base class for {@link CController} and {@link CWidget}.
  12. *
  13. * It provides the common functionalities shared by controllers who need to render views.
  14. *
  15. * CBaseController also implements the support for the following features:
  16. * <ul>
  17. * <li>{@link CClipWidget Clips} : a clip is a piece of captured output that can be inserted elsewhere.</li>
  18. * <li>{@link CWidget Widgets} : a widget is a self-contained sub-controller with its own view and model.</li>
  19. * <li>{@link COutputCache Fragment cache} : fragment cache selectively caches a portion of the output.</li>
  20. * </ul>
  21. *
  22. * To use a widget in a view, use the following in the view:
  23. * <pre>
  24. * $this->widget('path.to.widgetClass',array('property1'=>'value1',...));
  25. * </pre>
  26. * or
  27. * <pre>
  28. * $this->beginWidget('path.to.widgetClass',array('property1'=>'value1',...));
  29. * // ... display other contents here
  30. * $this->endWidget();
  31. * </pre>
  32. *
  33. * To create a clip, use the following:
  34. * <pre>
  35. * $this->beginClip('clipID');
  36. * // ... display the clip contents
  37. * $this->endClip();
  38. * </pre>
  39. * Then, in a different view or place, the captured clip can be inserted as:
  40. * <pre>
  41. * echo $this->clips['clipID'];
  42. * </pre>
  43. *
  44. * Note that $this in the code above refers to current controller so, for example,
  45. * if you need to access clip from a widget where $this refers to widget itself
  46. * you need to do it the following way:
  47. *
  48. * <pre>
  49. * echo $this->getController()->clips['clipID'];
  50. * </pre>
  51. *
  52. * To use fragment cache, do as follows,
  53. * <pre>
  54. * if($this->beginCache('cacheID',array('property1'=>'value1',...))
  55. * {
  56. * // ... display the content to be cached here
  57. * $this->endCache();
  58. * }
  59. * </pre>
  60. *
  61. * @author Qiang Xue <qiang.xue@gmail.com>
  62. * @package system.web
  63. * @since 1.0
  64. */
  65. abstract class CBaseController extends CComponent
  66. {
  67. private $_widgetStack=array();
  68. /**
  69. * Returns the view script file according to the specified view name.
  70. * This method must be implemented by child classes.
  71. * @param string $viewName view name
  72. * @return string the file path for the named view. False if the view cannot be found.
  73. */
  74. abstract public function getViewFile($viewName);
  75. /**
  76. * Renders a view file.
  77. *
  78. * @param string $viewFile view file path
  79. * @param array $data data to be extracted and made available to the view
  80. * @param boolean $return whether the rendering result should be returned instead of being echoed
  81. * @return string the rendering result. Null if the rendering result is not required.
  82. * @throws CException if the view file does not exist
  83. */
  84. public function renderFile($viewFile,$data=null,$return=false)
  85. {
  86. $widgetCount=count($this->_widgetStack);
  87. if(($renderer=Yii::app()->getViewRenderer())!==null && $renderer->fileExtension==='.'.CFileHelper::getExtension($viewFile))
  88. $content=$renderer->renderFile($this,$viewFile,$data,$return);
  89. else
  90. $content=$this->renderInternal($viewFile,$data,$return);
  91. if(count($this->_widgetStack)===$widgetCount)
  92. return $content;
  93. else
  94. {
  95. $widget=end($this->_widgetStack);
  96. 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.',
  97. array('{controller}'=>get_class($this), '{view}'=>$viewFile, '{widget}'=>get_class($widget))));
  98. }
  99. }
  100. /**
  101. * Renders a view file.
  102. * This method includes the view file as a PHP script
  103. * and captures the display result if required.
  104. * @param string $_viewFile_ view file
  105. * @param array $_data_ data to be extracted and made available to the view file
  106. * @param boolean $_return_ whether the rendering result should be returned as a string
  107. * @return string the rendering result. Null if the rendering result is not required.
  108. */
  109. public function renderInternal($_viewFile_,$_data_=null,$_return_=false)
  110. {
  111. // we use special variable names here to avoid conflict when extracting data
  112. if(is_array($_data_))
  113. extract($_data_,EXTR_PREFIX_SAME,'data');
  114. else
  115. $data=$_data_;
  116. if($_return_)
  117. {
  118. ob_start();
  119. ob_implicit_flush(false);
  120. require($_viewFile_);
  121. return ob_get_clean();
  122. }
  123. else
  124. require($_viewFile_);
  125. }
  126. /**
  127. * Creates a widget and initializes it.
  128. * This method first creates the specified widget instance.
  129. * It then configures the widget's properties with the given initial values.
  130. * At the end it calls {@link CWidget::init} to initialize the widget.
  131. * Starting from version 1.1, if a {@link CWidgetFactory widget factory} is enabled,
  132. * this method will use the factory to create the widget, instead.
  133. * @param string $className class name (can be in path alias format)
  134. * @param array $properties initial property values
  135. * @return CWidget the fully initialized widget instance.
  136. */
  137. public function createWidget($className,$properties=array())
  138. {
  139. $widget=Yii::app()->getWidgetFactory()->createWidget($this,$className,$properties);
  140. $widget->init();
  141. return $widget;
  142. }
  143. /**
  144. * Creates a widget and executes it.
  145. * @param string $className the widget class name or class in dot syntax (e.g. application.widgets.MyWidget)
  146. * @param array $properties list of initial property values for the widget (Property Name => Property Value)
  147. * @param boolean $captureOutput whether to capture the output of the widget. If true, the method will capture
  148. * and return the output generated by the widget. If false, the output will be directly sent for display
  149. * and the widget object will be returned. This parameter is available since version 1.1.2.
  150. * @return mixed the widget instance when $captureOutput is false, or the widget output when $captureOutput is true.
  151. */
  152. public function widget($className,$properties=array(),$captureOutput=false)
  153. {
  154. if($captureOutput)
  155. {
  156. ob_start();
  157. ob_implicit_flush(false);
  158. try
  159. {
  160. $widget=$this->createWidget($className,$properties);
  161. $widget->run();
  162. }
  163. catch(Exception $e)
  164. {
  165. ob_end_clean();
  166. throw $e;
  167. }
  168. return ob_get_clean();
  169. }
  170. else
  171. {
  172. $widget=$this->createWidget($className,$properties);
  173. $widget->run();
  174. return $widget;
  175. }
  176. }
  177. /**
  178. * Creates a widget and executes it.
  179. * This method is similar to {@link widget()} except that it is expecting
  180. * a {@link endWidget()} call to end the execution.
  181. * @param string $className the widget class name or class in dot syntax (e.g. application.widgets.MyWidget)
  182. * @param array $properties list of initial property values for the widget (Property Name => Property Value)
  183. * @return CWidget the widget created to run
  184. * @see endWidget
  185. */
  186. public function beginWidget($className,$properties=array())
  187. {
  188. $widget=$this->createWidget($className,$properties);
  189. $this->_widgetStack[]=$widget;
  190. return $widget;
  191. }
  192. /**
  193. * Ends the execution of the named widget.
  194. * This method is used together with {@link beginWidget()}.
  195. * @param string $id optional tag identifying the method call for debugging purpose.
  196. * @return CWidget the widget just ended running
  197. * @throws CException if an extra endWidget call is made
  198. * @see beginWidget
  199. */
  200. public function endWidget($id='')
  201. {
  202. if(($widget=array_pop($this->_widgetStack))!==null)
  203. {
  204. $widget->run();
  205. return $widget;
  206. }
  207. else
  208. throw new CException(Yii::t('yii','{controller} has an extra endWidget({id}) call in its view.',
  209. array('{controller}'=>get_class($this),'{id}'=>$id)));
  210. }
  211. /**
  212. * Begins recording a clip.
  213. * This method is a shortcut to beginning {@link CClipWidget}.
  214. * @param string $id the clip ID.
  215. * @param array $properties initial property values for {@link CClipWidget}.
  216. */
  217. public function beginClip($id,$properties=array())
  218. {
  219. $properties['id']=$id;
  220. $this->beginWidget('CClipWidget',$properties);
  221. }
  222. /**
  223. * Ends recording a clip.
  224. * This method is an alias to {@link endWidget}.
  225. */
  226. public function endClip()
  227. {
  228. $this->endWidget('CClipWidget');
  229. }
  230. /**
  231. * Begins fragment caching.
  232. * This method will display cached content if it is availabe.
  233. * If not, it will start caching and would expect a {@link endCache()}
  234. * call to end the cache and save the content into cache.
  235. * A typical usage of fragment caching is as follows,
  236. * <pre>
  237. * if($this->beginCache($id))
  238. * {
  239. * // ...generate content here
  240. * $this->endCache();
  241. * }
  242. * </pre>
  243. * @param string $id a unique ID identifying the fragment to be cached.
  244. * @param array $properties initial property values for {@link COutputCache}.
  245. * @return boolean whether we need to generate content for caching. False if cached version is available.
  246. * @see endCache
  247. */
  248. public function beginCache($id,$properties=array())
  249. {
  250. $properties['id']=$id;
  251. $cache=$this->beginWidget('COutputCache',$properties);
  252. if($cache->getIsContentCached())
  253. {
  254. $this->endCache();
  255. return false;
  256. }
  257. else
  258. return true;
  259. }
  260. /**
  261. * Ends fragment caching.
  262. * This is an alias to {@link endWidget}.
  263. * @see beginCache
  264. */
  265. public function endCache()
  266. {
  267. $this->endWidget('COutputCache');
  268. }
  269. /**
  270. * Begins the rendering of content that is to be decorated by the specified view.
  271. * @param mixed $view the name of the view that will be used to decorate the content. The actual view script
  272. * is resolved via {@link getViewFile}. If this parameter is null (default),
  273. * the default layout will be used as the decorative view.
  274. * Note that if the current controller does not belong to
  275. * any module, the default layout refers to the application's {@link CWebApplication::layout default layout};
  276. * If the controller belongs to a module, the default layout refers to the module's
  277. * {@link CWebModule::layout default layout}.
  278. * @param array $data the variables (name=>value) to be extracted and made available in the decorative view.
  279. * @see endContent
  280. * @see CContentDecorator
  281. */
  282. public function beginContent($view=null,$data=array())
  283. {
  284. $this->beginWidget('CContentDecorator',array('view'=>$view, 'data'=>$data));
  285. }
  286. /**
  287. * Ends the rendering of content.
  288. * @see beginContent
  289. */
  290. public function endContent()
  291. {
  292. $this->endWidget('CContentDecorator');
  293. }
  294. }