CActiveForm.php 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955
  1. <?php
  2. /**
  3. * CActiveForm 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. * CActiveForm provides a set of methods that can help to simplify the creation
  12. * of complex and interactive HTML forms that are associated with data models.
  13. *
  14. * The 'beginWidget' and 'endWidget' call of CActiveForm widget will render
  15. * the open and close form tags. Most other methods of CActiveForm are wrappers
  16. * of the corresponding 'active' methods in {@link CHtml}. Calling them in between
  17. * the 'beginWidget' and 'endWidget' calls will render text labels, input fields,
  18. * etc. For example, calling {@link CActiveForm::textField}
  19. * would generate an input field for a specified model attribute.
  20. *
  21. * What makes CActiveForm extremely useful is its support for data validation.
  22. * CActiveForm supports data validation at three levels:
  23. * <ul>
  24. * <li>server-side validation: the validation is performed at server side after
  25. * the whole page containing the form is submitted. If there is any validation error,
  26. * CActiveForm will render the error in the page back to user.</li>
  27. * <li>AJAX-based validation: when the user enters data into an input field,
  28. * an AJAX request is triggered which requires server-side validation. The validation
  29. * result is sent back in AJAX response and the input field changes its appearance
  30. * accordingly.</li>
  31. * <li>client-side validation (available since version 1.1.7):
  32. * when the user enters data into an input field,
  33. * validation is performed on the client side using JavaScript. No server contact
  34. * will be made, which reduces the workload on the server.</li>
  35. * </ul>
  36. *
  37. * All these validations share the same set of validation rules declared in
  38. * the associated model class. CActiveForm is designed in such a way that
  39. * all these validations will lead to the same user interface changes and error
  40. * message content.
  41. *
  42. * To ensure data validity, server-side validation is always performed.
  43. * By setting {@link enableAjaxValidation} to true, one can enable AJAX-based validation;
  44. * and by setting {@link enableClientValidation} to true, one can enable client-side validation.
  45. * Note that in order to make the latter two validations work, the user's browser
  46. * must has its JavaScript enabled. If not, only the server-side validation will
  47. * be performed.
  48. *
  49. * The AJAX-based validation and client-side validation may be used together
  50. * or separately. For example, in a user registration form, one may use AJAX-based
  51. * validation to check if the user has picked a unique username, and use client-side
  52. * validation to ensure all required fields are entered with data.
  53. * Because the AJAX-based validation may bring extra workload on the server,
  54. * if possible, one should mainly use client-side validation.
  55. *
  56. * The AJAX-based validation has a few limitations. First, it does not work
  57. * with file upload fields. Second, it should not be used to perform validations that
  58. * may cause server-side state changes. Third, it is not designed
  59. * to work with tabular data input for the moment.
  60. *
  61. * Support for client-side validation varies for different validators. A validator
  62. * will support client-side validation only if it implements {@link CValidator::clientValidateAttribute}
  63. * and has its {@link CValidator::enableClientValidation} property set true.
  64. * At this moment, the following core validators support client-side validation:
  65. * <ul>
  66. * <li>{@link CBooleanValidator}</li>
  67. * <li>{@link CCaptchaValidator}</li>
  68. * <li>{@link CCompareValidator}</li>
  69. * <li>{@link CEmailValidator}</li>
  70. * <li>{@link CNumberValidator}</li>
  71. * <li>{@link CRangeValidator}</li>
  72. * <li>{@link CRegularExpressionValidator}</li>
  73. * <li>{@link CRequiredValidator}</li>
  74. * <li>{@link CStringValidator}</li>
  75. * <li>{@link CUrlValidator}</li>
  76. * </ul>
  77. *
  78. * CActiveForm relies on CSS to customize the appearance of input fields
  79. * which are in different validation states. In particular, each input field
  80. * may be one of the four states: initial (not validated),
  81. * validating, error and success. To differentiate these states, CActiveForm
  82. * automatically assigns different CSS classes for the last three states
  83. * to the HTML element containing the input field.
  84. * By default, these CSS classes are named as 'validating', 'error' and 'success',
  85. * respectively. We may customize these CSS classes by configuring the
  86. * {@link clientOptions} property or specifying in the {@link error} method.
  87. *
  88. * The following is a piece of sample view code showing how to use CActiveForm:
  89. *
  90. * <pre>
  91. * <?php $form = $this->beginWidget('CActiveForm', array(
  92. * 'id'=>'user-form',
  93. * 'enableAjaxValidation'=>true,
  94. * 'enableClientValidation'=>true,
  95. * 'focus'=>array($model,'firstName'),
  96. * )); ?>
  97. *
  98. * <?php echo $form->errorSummary($model); ?>
  99. *
  100. * <div class="row">
  101. * <?php echo $form->labelEx($model,'firstName'); ?>
  102. * <?php echo $form->textField($model,'firstName'); ?>
  103. * <?php echo $form->error($model,'firstName'); ?>
  104. * </div>
  105. * <div class="row">
  106. * <?php echo $form->labelEx($model,'lastName'); ?>
  107. * <?php echo $form->textField($model,'lastName'); ?>
  108. * <?php echo $form->error($model,'lastName'); ?>
  109. * </div>
  110. *
  111. * <?php $this->endWidget(); ?>
  112. * </pre>
  113. *
  114. * To respond to the AJAX validation requests, we need the following class code:
  115. * <pre>
  116. * public function actionCreate()
  117. * {
  118. * $model=new User;
  119. * $this->performAjaxValidation($model);
  120. * if(isset($_POST['User']))
  121. * {
  122. * $model->attributes=$_POST['User'];
  123. * if($model->save())
  124. * $this->redirect('index');
  125. * }
  126. * $this->render('create',array('model'=>$model));
  127. * }
  128. *
  129. * protected function performAjaxValidation($model)
  130. * {
  131. * if(isset($_POST['ajax']) && $_POST['ajax']==='user-form')
  132. * {
  133. * echo CActiveForm::validate($model);
  134. * Yii::app()->end();
  135. * }
  136. * }
  137. * </pre>
  138. *
  139. * In the above code, if we do not enable the AJAX-based validation, we can remove
  140. * the <code>performAjaxValidation</code> method and its invocation.
  141. *
  142. * @author Qiang Xue <qiang.xue@gmail.com>
  143. * @package system.web.widgets
  144. * @since 1.1.1
  145. */
  146. class CActiveForm extends CWidget
  147. {
  148. /**
  149. * @var mixed the form action URL (see {@link CHtml::normalizeUrl} for details about this parameter).
  150. * If not set, the current page URL is used.
  151. */
  152. public $action='';
  153. /**
  154. * @var string the form submission method. This should be either 'post' or 'get'.
  155. * Defaults to 'post'.
  156. */
  157. public $method='post';
  158. /**
  159. * @var boolean whether to generate a stateful form (See {@link CHtml::statefulForm}). Defaults to false.
  160. */
  161. public $stateful=false;
  162. /**
  163. * @var string the CSS class name for error messages.
  164. * Since 1.1.14 this defaults to 'errorMessage' defined in {@link CHtml::$errorMessageCss}.
  165. * Individual {@link error} call may override this value by specifying the 'class' HTML option.
  166. */
  167. public $errorMessageCssClass;
  168. /**
  169. * @var array additional HTML attributes that should be rendered for the form tag.
  170. */
  171. public $htmlOptions=array();
  172. /**
  173. * @var array the options to be passed to the javascript validation plugin.
  174. * The following options are supported:
  175. * <ul>
  176. * <li>ajaxVar: string, the name of the parameter indicating the request is an AJAX request.
  177. * When the AJAX validation is triggered, a parameter named as this property will be sent
  178. * together with the other form data to the server. The parameter value is the form ID.
  179. * The server side can then detect who triggers the AJAX validation and react accordingly.
  180. * Defaults to 'ajax'.</li>
  181. * <li>validationUrl: string, the URL that performs the AJAX validations.
  182. * If not set, it will take the value of {@link action}.</li>
  183. * <li>validationDelay: integer, the number of milliseconds that an AJAX validation should be
  184. * delayed after an input is changed. A value 0 means the validation will be triggered immediately
  185. * when an input is changed. A value greater than 0 means changing several inputs may only
  186. * trigger a single validation if they happen fast enough, which may help reduce the server load.
  187. * Defaults to 200 (0.2 second).</li>
  188. * <li>validateOnSubmit: boolean, whether to perform AJAX validation when the form is being submitted.
  189. * If there are any validation errors, the form submission will be stopped.
  190. * Defaults to false.</li>
  191. * <li>validateOnChange: boolean, whether to trigger an AJAX validation
  192. * each time when an input's value is changed. You may want to turn this off
  193. * if it causes too much performance impact, because each AJAX validation request
  194. * will submit the data of the whole form. Defaults to true.</li>
  195. * <li>validateOnType: boolean, whether to trigger an AJAX validation each time when the user
  196. * presses a key. When setting this property to be true, you should tune up the 'validationDelay'
  197. * option to avoid triggering too many AJAX validations. Defaults to false.</li>
  198. * <li>hideErrorMessage: boolean, whether to hide the error message even if there is an error.
  199. * Defaults to false, which means the error message will show up whenever the input has an error.</li>
  200. * <li>inputContainer: string, the jQuery selector for the HTML element containing the input field.
  201. * During the validation process, CActiveForm will set different CSS class for the container element
  202. * to indicate the state change. If not set, it means the closest 'div' element that contains the input field.</li>
  203. * <li>errorCssClass: string, the CSS class to be assigned to the container whose associated input
  204. * has AJAX validation error. Defaults to 'error'.</li>
  205. * <li>successCssClass: string, the CSS class to be assigned to the container whose associated input
  206. * passes AJAX validation without any error. Defaults to 'success'.</li>
  207. * <li>validatingCssClass: string, the CSS class to be assigned to the container whose associated input
  208. * is currently being validated via AJAX. Defaults to 'validating'.</li>
  209. * <li>errorMessageCssClass: string, the CSS class assigned to the error messages returned
  210. * by AJAX validations. Defaults to 'errorMessage'.</li>
  211. * <li>beforeValidate: function, the function that will be invoked before performing ajax-based validation
  212. * triggered by form submission action (available only when validateOnSubmit is set true).
  213. * The expected function signature should be <code>beforeValidate(form) {...}</code>, where 'form' is
  214. * the jquery representation of the form object. If the return value of this function is NOT true, the validation
  215. * will be cancelled.
  216. *
  217. * Note that because this option refers to a js function, you should wrap the value with {@link CJavaScriptExpression} to prevent it
  218. * from being encoded as a string. This option has been available since version 1.1.3.</li>
  219. * <li>afterValidate: function, the function that will be invoked after performing ajax-based validation
  220. * triggered by form submission action (available only when validateOnSubmit is set true).
  221. * The expected function signature should be <code>afterValidate(form, data, hasError) {...}</code>, where 'form' is
  222. * the jquery representation of the form object; 'data' is the JSON response from the server-side validation; 'hasError'
  223. * is a boolean value indicating whether there is any validation error. If the return value of this function is NOT true,
  224. * the normal form submission will be cancelled.
  225. *
  226. * Note that because this option refers to a js function, you should wrap the value with {@link CJavaScriptExpression} to prevent it
  227. * from being encoded as a string. This option has been available since version 1.1.3.</li>
  228. * <li>beforeValidateAttribute: function, the function that will be invoked before performing ajax-based validation
  229. * triggered by a single attribute input change. The expected function signature should be
  230. * <code>beforeValidateAttribute(form, attribute) {...}</code>, where 'form' is the jquery representation of the form object
  231. * and 'attribute' refers to the js options for the triggering attribute (see {@link error}).
  232. * If the return value of this function is NOT true, the validation will be cancelled.
  233. *
  234. * Note that because this option refers to a js function, you should wrap the value with {@link CJavaScriptExpression} to prevent it
  235. * from being encoded as a string. This option has been available since version 1.1.3.</li>
  236. * <li>afterValidateAttribute: function, the function that will be invoked after performing ajax-based validation
  237. * triggered by a single attribute input change. The expected function signature should be
  238. * <code>afterValidateAttribute(form, attribute, data, hasError) {...}</code>, where 'form' is the jquery
  239. * representation of the form object; 'attribute' refers to the js options for the triggering attribute (see {@link error});
  240. * 'data' is the JSON response from the server-side validation; 'hasError' is a boolean value indicating whether
  241. * there is any validation error.
  242. *
  243. * Note that because this option refers to a js function, you should wrap the value with {@link CJavaScriptExpression} to prevent it
  244. * from being encoded as a string. This option has been available since version 1.1.3.</li>
  245. * </ul>
  246. *
  247. * Some of the above options may be overridden in individual calls of {@link error()}.
  248. * They include: validationDelay, validateOnChange, validateOnType, hideErrorMessage,
  249. * inputContainer, errorCssClass, successCssClass, validatingCssClass, beforeValidateAttribute, afterValidateAttribute.
  250. */
  251. public $clientOptions=array();
  252. /**
  253. * @var boolean whether to enable data validation via AJAX. Defaults to false.
  254. * When this property is set true, you should respond to the AJAX validation request on the server side as shown below:
  255. * <pre>
  256. * public function actionCreate()
  257. * {
  258. * $model=new User;
  259. * if(isset($_POST['ajax']) && $_POST['ajax']==='user-form')
  260. * {
  261. * echo CActiveForm::validate($model);
  262. * Yii::app()->end();
  263. * }
  264. * ......
  265. * }
  266. * </pre>
  267. */
  268. public $enableAjaxValidation=false;
  269. /**
  270. * @var boolean whether to enable client-side data validation. Defaults to false.
  271. *
  272. * When this property is set true, client-side validation will be performed by validators
  273. * that support it (see {@link CValidator::enableClientValidation} and {@link CValidator::clientValidateAttribute}).
  274. *
  275. * @see error
  276. * @since 1.1.7
  277. */
  278. public $enableClientValidation=false;
  279. /**
  280. * @var mixed form element to get initial input focus on page load.
  281. *
  282. * Defaults to null meaning no input field has a focus.
  283. * If set as array, first element should be model and second element should be the attribute.
  284. * If set as string any jQuery selector can be used
  285. *
  286. * Example - set input focus on page load to:
  287. * <ul>
  288. * <li>'focus'=>array($model,'username') - $model->username input filed</li>
  289. * <li>'focus'=>'#'.CHtml::activeId($model,'username') - $model->username input field</li>
  290. * <li>'focus'=>'#LoginForm_username' - input field with ID LoginForm_username</li>
  291. * <li>'focus'=>'input[type="text"]:first' - first input element of type text</li>
  292. * <li>'focus'=>'input:visible:enabled:first' - first visible and enabled input element</li>
  293. * <li>'focus'=>'input:text[value=""]:first' - first empty input</li>
  294. * </ul>
  295. *
  296. * @since 1.1.4
  297. */
  298. public $focus;
  299. /**
  300. * @var array the javascript options for model attributes (input ID => options)
  301. * @see error
  302. * @since 1.1.7
  303. */
  304. protected $attributes=array();
  305. /**
  306. * @var string the ID of the container element for error summary
  307. * @see errorSummary
  308. * @since 1.1.7
  309. */
  310. protected $summaryID;
  311. /**
  312. * @var string[] attribute IDs to be used to display error summary.
  313. * @since 1.1.14
  314. */
  315. private $_summaryAttributes=array();
  316. /**
  317. * Initializes the widget.
  318. * This renders the form open tag.
  319. */
  320. public function init()
  321. {
  322. if(!isset($this->htmlOptions['id']))
  323. $this->htmlOptions['id']=$this->id;
  324. else
  325. $this->id=$this->htmlOptions['id'];
  326. if($this->stateful)
  327. echo CHtml::statefulForm($this->action, $this->method, $this->htmlOptions);
  328. else
  329. echo CHtml::beginForm($this->action, $this->method, $this->htmlOptions);
  330. if($this->errorMessageCssClass===null)
  331. $this->errorMessageCssClass=CHtml::$errorMessageCss;
  332. }
  333. /**
  334. * Runs the widget.
  335. * This registers the necessary javascript code and renders the form close tag.
  336. */
  337. public function run()
  338. {
  339. if(is_array($this->focus))
  340. $this->focus="#".CHtml::activeId($this->focus[0],$this->focus[1]);
  341. echo CHtml::endForm();
  342. $cs=Yii::app()->clientScript;
  343. if(!$this->enableAjaxValidation && !$this->enableClientValidation || empty($this->attributes))
  344. {
  345. if($this->focus!==null)
  346. {
  347. $cs->registerCoreScript('jquery');
  348. $cs->registerScript('CActiveForm#focus',"
  349. if(!window.location.hash)
  350. jQuery('".$this->focus."').focus();
  351. ");
  352. }
  353. return;
  354. }
  355. $options=$this->clientOptions;
  356. if(isset($this->clientOptions['validationUrl']) && is_array($this->clientOptions['validationUrl']))
  357. $options['validationUrl']=CHtml::normalizeUrl($this->clientOptions['validationUrl']);
  358. foreach($this->_summaryAttributes as $attribute)
  359. $this->attributes[$attribute]['summary']=true;
  360. $options['attributes']=array_values($this->attributes);
  361. if($this->summaryID!==null)
  362. $options['summaryID']=$this->summaryID;
  363. if($this->focus!==null)
  364. $options['focus']=$this->focus;
  365. if(!empty(CHtml::$errorCss))
  366. $options['errorCss']=CHtml::$errorCss;
  367. $options=CJavaScript::encode($options);
  368. $cs->registerCoreScript('yiiactiveform');
  369. $id=$this->id;
  370. $cs->registerScript(__CLASS__.'#'.$id,"jQuery('#$id').yiiactiveform($options);");
  371. }
  372. /**
  373. * Displays the first validation error for a model attribute.
  374. * This is similar to {@link CHtml::error} except that it registers the model attribute
  375. * so that if its value is changed by users, an AJAX validation may be triggered.
  376. * @param CModel $model the data model
  377. * @param string $attribute the attribute name
  378. * @param array $htmlOptions additional HTML attributes to be rendered in the container div tag.
  379. * Besides all those options available in {@link CHtml::error}, the following options are recognized in addition:
  380. * <ul>
  381. * <li>validationDelay</li>
  382. * <li>validateOnChange</li>
  383. * <li>validateOnType</li>
  384. * <li>hideErrorMessage</li>
  385. * <li>inputContainer</li>
  386. * <li>errorCssClass</li>
  387. * <li>successCssClass</li>
  388. * <li>validatingCssClass</li>
  389. * <li>beforeValidateAttribute</li>
  390. * <li>afterValidateAttribute</li>
  391. * </ul>
  392. * These options override the corresponding options as declared in {@link options} for this
  393. * particular model attribute. For more details about these options, please refer to {@link clientOptions}.
  394. * Note that these options are only used when {@link enableAjaxValidation} or {@link enableClientValidation}
  395. * is set true.
  396. * <ul>
  397. * <li>inputID</li>
  398. * </ul>
  399. * When an CActiveForm input field uses a custom ID, for ajax/client validation to work properly
  400. * inputID should be set to the same ID
  401. *
  402. * Example:
  403. * <pre>
  404. * <div class="form-element">
  405. * <?php echo $form->labelEx($model,'attribute'); ?>
  406. * <?php echo $form->textField($model,'attribute', array('id'=>'custom-id')); ?>
  407. * <?php echo $form->error($model,'attribute',array('inputID'=>'custom-id')); ?>
  408. * </div>
  409. * </pre>
  410. *
  411. * When client-side validation is enabled, an option named "clientValidation" is also recognized.
  412. * This option should take a piece of JavaScript code to perform client-side validation. In the code,
  413. * the variables are predefined:
  414. * <ul>
  415. * <li>value: the current input value associated with this attribute.</li>
  416. * <li>messages: an array that may be appended with new error messages for the attribute.</li>
  417. * <li>attribute: a data structure keeping all client-side options for the attribute</li>
  418. * </ul>
  419. * This should NOT be a function but just the code, Yii will enclose the code you provide inside the
  420. * actual JS function.
  421. * @param boolean $enableAjaxValidation whether to enable AJAX validation for the specified attribute.
  422. * Note that in order to enable AJAX validation, both {@link enableAjaxValidation} and this parameter
  423. * must be true.
  424. * @param boolean $enableClientValidation whether to enable client-side validation for the specified attribute.
  425. * Note that in order to enable client-side validation, both {@link enableClientValidation} and this parameter
  426. * must be true. This parameter has been available since version 1.1.7.
  427. * @return string the validation result (error display or success message).
  428. * @see CHtml::error
  429. */
  430. public function error($model,$attribute,$htmlOptions=array(),$enableAjaxValidation=true,$enableClientValidation=true)
  431. {
  432. if(!$this->enableAjaxValidation)
  433. $enableAjaxValidation=false;
  434. if(!$this->enableClientValidation)
  435. $enableClientValidation=false;
  436. if(!isset($htmlOptions['class']))
  437. $htmlOptions['class']=$this->errorMessageCssClass;
  438. if(!$enableAjaxValidation && !$enableClientValidation)
  439. return CHtml::error($model,$attribute,$htmlOptions);
  440. $id=CHtml::activeId($model,$attribute);
  441. $inputID=isset($htmlOptions['inputID']) ? $htmlOptions['inputID'] : $id;
  442. unset($htmlOptions['inputID']);
  443. if(!isset($htmlOptions['id']))
  444. $htmlOptions['id']=$inputID.'_em_';
  445. $option=array(
  446. 'id'=>$id,
  447. 'inputID'=>$inputID,
  448. 'errorID'=>$htmlOptions['id'],
  449. 'model'=>get_class($model),
  450. 'name'=>$attribute,
  451. 'enableAjaxValidation'=>$enableAjaxValidation,
  452. );
  453. $optionNames=array(
  454. 'validationDelay',
  455. 'validateOnChange',
  456. 'validateOnType',
  457. 'hideErrorMessage',
  458. 'inputContainer',
  459. 'errorCssClass',
  460. 'successCssClass',
  461. 'validatingCssClass',
  462. 'beforeValidateAttribute',
  463. 'afterValidateAttribute',
  464. );
  465. foreach($optionNames as $name)
  466. {
  467. if(isset($htmlOptions[$name]))
  468. {
  469. $option[$name]=$htmlOptions[$name];
  470. unset($htmlOptions[$name]);
  471. }
  472. }
  473. if($model instanceof CActiveRecord && !$model->isNewRecord)
  474. $option['status']=1;
  475. if($enableClientValidation)
  476. {
  477. $validators=isset($htmlOptions['clientValidation']) ? array($htmlOptions['clientValidation']) : array();
  478. unset($htmlOptions['clientValidation']);
  479. $attributeName = $attribute;
  480. if(($pos=strrpos($attribute,']'))!==false && $pos!==strlen($attribute)-1) // e.g. [a]name
  481. {
  482. $attributeName=substr($attribute,$pos+1);
  483. }
  484. foreach($model->getValidators($attributeName) as $validator)
  485. {
  486. if($validator->enableClientValidation)
  487. {
  488. if(($js=$validator->clientValidateAttribute($model,$attributeName))!='')
  489. $validators[]=$js;
  490. }
  491. }
  492. if($validators!==array())
  493. $option['clientValidation']=new CJavaScriptExpression("function(value, messages, attribute) {\n".implode("\n",$validators)."\n}");
  494. }
  495. $html=CHtml::error($model,$attribute,$htmlOptions);
  496. if($html==='')
  497. {
  498. if(isset($htmlOptions['style']))
  499. $htmlOptions['style']=rtrim($htmlOptions['style'],';').';display:none';
  500. else
  501. $htmlOptions['style']='display:none';
  502. $html=CHtml::tag(CHtml::$errorContainerTag,$htmlOptions,'');
  503. }
  504. $this->attributes[$inputID]=$option;
  505. return $html;
  506. }
  507. /**
  508. * Displays a summary of validation errors for one or several models.
  509. * This method is very similar to {@link CHtml::errorSummary} except that it also works
  510. * when AJAX validation is performed.
  511. * @param mixed $models the models whose input errors are to be displayed. This can be either
  512. * a single model or an array of models.
  513. * @param string $header a piece of HTML code that appears in front of the errors
  514. * @param string $footer a piece of HTML code that appears at the end of the errors
  515. * @param array $htmlOptions additional HTML attributes to be rendered in the container div tag.
  516. * @return string the error summary. Empty if no errors are found.
  517. * @see CHtml::errorSummary
  518. */
  519. public function errorSummary($models,$header=null,$footer=null,$htmlOptions=array())
  520. {
  521. if(!$this->enableAjaxValidation && !$this->enableClientValidation)
  522. return CHtml::errorSummary($models,$header,$footer,$htmlOptions);
  523. if(!isset($htmlOptions['id']))
  524. $htmlOptions['id']=$this->id.'_es_';
  525. $html=CHtml::errorSummary($models,$header,$footer,$htmlOptions);
  526. if($html==='')
  527. {
  528. if($header===null)
  529. $header='<p>'.Yii::t('yii','請更正下列輸入錯誤:').'</p>';
  530. if(!isset($htmlOptions['class']))
  531. $htmlOptions['class']=CHtml::$errorSummaryCss;
  532. $htmlOptions['style']=isset($htmlOptions['style']) ? rtrim($htmlOptions['style'],';').';display:none' : 'display:none';
  533. $html=CHtml::tag('div',$htmlOptions,$header."\n<ul><li>dummy</li></ul>".$footer);
  534. }
  535. $this->summaryID=$htmlOptions['id'];
  536. foreach(is_array($models) ? $models : array($models) as $model)
  537. foreach($model->getSafeAttributeNames() as $attribute)
  538. $this->_summaryAttributes[]=CHtml::activeId($model,$attribute);
  539. return $html;
  540. }
  541. /**
  542. * Renders an HTML label for a model attribute.
  543. * This method is a wrapper of {@link CHtml::activeLabel}.
  544. * Please check {@link CHtml::activeLabel} for detailed information
  545. * about the parameters for this method.
  546. * @param CModel $model the data model
  547. * @param string $attribute the attribute
  548. * @param array $htmlOptions additional HTML attributes.
  549. * @return string the generated label tag
  550. */
  551. public function label($model,$attribute,$htmlOptions=array())
  552. {
  553. return CHtml::activeLabel($model,$attribute,$htmlOptions);
  554. }
  555. /**
  556. * Renders an HTML label for a model attribute.
  557. * This method is a wrapper of {@link CHtml::activeLabelEx}.
  558. * Please check {@link CHtml::activeLabelEx} for detailed information
  559. * about the parameters for this method.
  560. * @param CModel $model the data model
  561. * @param string $attribute the attribute
  562. * @param array $htmlOptions additional HTML attributes.
  563. * @return string the generated label tag
  564. */
  565. public function labelEx($model,$attribute,$htmlOptions=array())
  566. {
  567. return CHtml::activeLabelEx($model,$attribute,$htmlOptions);
  568. }
  569. /**
  570. * Renders a url field for a model attribute.
  571. * This method is a wrapper of {@link CHtml::activeUrlField}.
  572. * Please check {@link CHtml::activeUrlField} for detailed information
  573. * about the parameters for this method.
  574. * @param CModel $model the data model
  575. * @param string $attribute the attribute
  576. * @param array $htmlOptions additional HTML attributes.
  577. * @return string the generated input field
  578. * @since 1.1.11
  579. */
  580. public function urlField($model,$attribute,$htmlOptions=array())
  581. {
  582. return CHtml::activeUrlField($model,$attribute,$htmlOptions);
  583. }
  584. /**
  585. * Renders an email field for a model attribute.
  586. * This method is a wrapper of {@link CHtml::activeEmailField}.
  587. * Please check {@link CHtml::activeEmailField} for detailed information
  588. * about the parameters for this method.
  589. * @param CModel $model the data model
  590. * @param string $attribute the attribute
  591. * @param array $htmlOptions additional HTML attributes.
  592. * @return string the generated input field
  593. * @since 1.1.11
  594. */
  595. public function emailField($model,$attribute,$htmlOptions=array())
  596. {
  597. return CHtml::activeEmailField($model,$attribute,$htmlOptions);
  598. }
  599. /**
  600. * Renders a number field for a model attribute.
  601. * This method is a wrapper of {@link CHtml::activeNumberField}.
  602. * Please check {@link CHtml::activeNumberField} for detailed information
  603. * about the parameters for this method.
  604. * @param CModel $model the data model
  605. * @param string $attribute the attribute
  606. * @param array $htmlOptions additional HTML attributes.
  607. * @return string the generated input field
  608. * @since 1.1.11
  609. */
  610. public function numberField($model,$attribute,$htmlOptions=array())
  611. {
  612. return CHtml::activeNumberField($model,$attribute,$htmlOptions);
  613. }
  614. /**
  615. * Generates a range field for a model attribute.
  616. * This method is a wrapper of {@link CHtml::activeRangeField}.
  617. * Please check {@link CHtml::activeRangeField} for detailed information
  618. * about the parameters for this method.
  619. * @param CModel $model the data model
  620. * @param string $attribute the attribute
  621. * @param array $htmlOptions additional HTML attributes.
  622. * @return string the generated input field
  623. * @since 1.1.11
  624. */
  625. public function rangeField($model,$attribute,$htmlOptions=array())
  626. {
  627. return CHtml::activeRangeField($model,$attribute,$htmlOptions);
  628. }
  629. /**
  630. * Renders a date field for a model attribute.
  631. * This method is a wrapper of {@link CHtml::activeDateField}.
  632. * Please check {@link CHtml::activeDateField} for detailed information
  633. * about the parameters for this method.
  634. * @param CModel $model the data model
  635. * @param string $attribute the attribute
  636. * @param array $htmlOptions additional HTML attributes.
  637. * @return string the generated input field
  638. * @since 1.1.11
  639. */
  640. public function dateField($model,$attribute,$htmlOptions=array())
  641. {
  642. return CHtml::activeDateField($model,$attribute,$htmlOptions);
  643. }
  644. /**
  645. * Renders a time field for a model attribute.
  646. * This method is a wrapper of {@link CHtml::activeTimeField}.
  647. * Please check {@link CHtml::activeTimeField} for detailed information
  648. * about the parameters for this method.
  649. * @param CModel $model the data model
  650. * @param string $attribute the attribute
  651. * @param array $htmlOptions additional HTML attributes.
  652. * @return string the generated input field
  653. * @since 1.1.14
  654. */
  655. public function timeField($model,$attribute,$htmlOptions=array())
  656. {
  657. return CHtml::activeTimeField($model,$attribute,$htmlOptions);
  658. }
  659. /**
  660. * Renders a time field for a model attribute.
  661. * This method is a wrapper of {@link CHtml::activeTimeField}.
  662. * Please check {@link CHtml::activeTimeField} for detailed information
  663. * about the parameters for this method.
  664. * @param CModel $model the data model
  665. * @param string $attribute the attribute
  666. * @param array $htmlOptions additional HTML attributes.
  667. * @return string the generated input field
  668. * @since 1.1.14
  669. */
  670. public function telField($model,$attribute,$htmlOptions=array())
  671. {
  672. return CHtml::activeTelField($model,$attribute,$htmlOptions);
  673. }
  674. /**
  675. * Renders a text field for a model attribute.
  676. * This method is a wrapper of {@link CHtml::activeTextField}.
  677. * Please check {@link CHtml::activeTextField} for detailed information
  678. * about the parameters for this method.
  679. * @param CModel $model the data model
  680. * @param string $attribute the attribute
  681. * @param array $htmlOptions additional HTML attributes.
  682. * @return string the generated input field
  683. */
  684. public function textField($model,$attribute,$htmlOptions=array())
  685. {
  686. return CHtml::activeTextField($model,$attribute,$htmlOptions);
  687. }
  688. /**
  689. * Renders a search field for a model attribute.
  690. * This method is a wrapper of {@link CHtml::activeSearchField}.
  691. * Please check {@link CHtml::activeSearchField} for detailed information
  692. * about the parameters for this method.
  693. * @param CModel $model the data model
  694. * @param string $attribute the attribute
  695. * @param array $htmlOptions additional HTML attributes.
  696. * @return string the generated input field
  697. * @since 1.1.14
  698. */
  699. public function searchField($model,$attribute,$htmlOptions=array())
  700. {
  701. return CHtml::activeSearchField($model,$attribute,$htmlOptions);
  702. }
  703. /**
  704. * Renders a hidden field for a model attribute.
  705. * This method is a wrapper of {@link CHtml::activeHiddenField}.
  706. * Please check {@link CHtml::activeHiddenField} for detailed information
  707. * about the parameters for this method.
  708. * @param CModel $model the data model
  709. * @param string $attribute the attribute
  710. * @param array $htmlOptions additional HTML attributes.
  711. * @return string the generated input field
  712. */
  713. public function hiddenField($model,$attribute,$htmlOptions=array())
  714. {
  715. return CHtml::activeHiddenField($model,$attribute,$htmlOptions);
  716. }
  717. /**
  718. * Renders a password field for a model attribute.
  719. * This method is a wrapper of {@link CHtml::activePasswordField}.
  720. * Please check {@link CHtml::activePasswordField} for detailed information
  721. * about the parameters for this method.
  722. * @param CModel $model the data model
  723. * @param string $attribute the attribute
  724. * @param array $htmlOptions additional HTML attributes.
  725. * @return string the generated input field
  726. */
  727. public function passwordField($model,$attribute,$htmlOptions=array())
  728. {
  729. return CHtml::activePasswordField($model,$attribute,$htmlOptions);
  730. }
  731. /**
  732. * Renders a text area for a model attribute.
  733. * This method is a wrapper of {@link CHtml::activeTextArea}.
  734. * Please check {@link CHtml::activeTextArea} for detailed information
  735. * about the parameters for this method.
  736. * @param CModel $model the data model
  737. * @param string $attribute the attribute
  738. * @param array $htmlOptions additional HTML attributes.
  739. * @return string the generated text area
  740. */
  741. public function textArea($model,$attribute,$htmlOptions=array())
  742. {
  743. return CHtml::activeTextArea($model,$attribute,$htmlOptions);
  744. }
  745. /**
  746. * Renders a file field for a model attribute.
  747. * This method is a wrapper of {@link CHtml::activeFileField}.
  748. * Please check {@link CHtml::activeFileField} for detailed information
  749. * about the parameters for this method.
  750. * @param CModel $model the data model
  751. * @param string $attribute the attribute
  752. * @param array $htmlOptions additional HTML attributes
  753. * @return string the generated input field
  754. */
  755. public function fileField($model,$attribute,$htmlOptions=array())
  756. {
  757. return CHtml::activeFileField($model,$attribute,$htmlOptions);
  758. }
  759. /**
  760. * Renders a radio button for a model attribute.
  761. * This method is a wrapper of {@link CHtml::activeRadioButton}.
  762. * Please check {@link CHtml::activeRadioButton} for detailed information
  763. * about the parameters for this method.
  764. * @param CModel $model the data model
  765. * @param string $attribute the attribute
  766. * @param array $htmlOptions additional HTML attributes.
  767. * @return string the generated radio button
  768. */
  769. public function radioButton($model,$attribute,$htmlOptions=array())
  770. {
  771. return CHtml::activeRadioButton($model,$attribute,$htmlOptions);
  772. }
  773. /**
  774. * Renders a checkbox for a model attribute.
  775. * This method is a wrapper of {@link CHtml::activeCheckBox}.
  776. * Please check {@link CHtml::activeCheckBox} for detailed information
  777. * about the parameters for this method.
  778. * @param CModel $model the data model
  779. * @param string $attribute the attribute
  780. * @param array $htmlOptions additional HTML attributes.
  781. * @return string the generated check box
  782. */
  783. public function checkBox($model,$attribute,$htmlOptions=array())
  784. {
  785. return CHtml::activeCheckBox($model,$attribute,$htmlOptions);
  786. }
  787. /**
  788. * Renders a dropdown list for a model attribute.
  789. * This method is a wrapper of {@link CHtml::activeDropDownList}.
  790. * Please check {@link CHtml::activeDropDownList} for detailed information
  791. * about the parameters for this method.
  792. * @param CModel $model the data model
  793. * @param string $attribute the attribute
  794. * @param array $data data for generating the list options (value=>display)
  795. * @param array $htmlOptions additional HTML attributes.
  796. * @return string the generated drop down list
  797. */
  798. public function dropDownList($model,$attribute,$data,$htmlOptions=array())
  799. {
  800. return CHtml::activeDropDownList($model,$attribute,$data,$htmlOptions);
  801. }
  802. /**
  803. * Renders a list box for a model attribute.
  804. * This method is a wrapper of {@link CHtml::activeListBox}.
  805. * Please check {@link CHtml::activeListBox} for detailed information
  806. * about the parameters for this method.
  807. * @param CModel $model the data model
  808. * @param string $attribute the attribute
  809. * @param array $data data for generating the list options (value=>display)
  810. * @param array $htmlOptions additional HTML attributes.
  811. * @return string the generated list box
  812. */
  813. public function listBox($model,$attribute,$data,$htmlOptions=array())
  814. {
  815. return CHtml::activeListBox($model,$attribute,$data,$htmlOptions);
  816. }
  817. /**
  818. * Renders a checkbox list for a model attribute.
  819. * This method is a wrapper of {@link CHtml::activeCheckBoxList}.
  820. * Please check {@link CHtml::activeCheckBoxList} for detailed information
  821. * about the parameters for this method.
  822. * @param CModel $model the data model
  823. * @param string $attribute the attribute
  824. * @param array $data value-label pairs used to generate the check box list.
  825. * @param array $htmlOptions addtional HTML options.
  826. * @return string the generated check box list
  827. */
  828. public function checkBoxList($model,$attribute,$data,$htmlOptions=array())
  829. {
  830. return CHtml::activeCheckBoxList($model,$attribute,$data,$htmlOptions);
  831. }
  832. /**
  833. * Renders a radio button list for a model attribute.
  834. * This method is a wrapper of {@link CHtml::activeRadioButtonList}.
  835. * Please check {@link CHtml::activeRadioButtonList} for detailed information
  836. * about the parameters for this method.
  837. * @param CModel $model the data model
  838. * @param string $attribute the attribute
  839. * @param array $data value-label pairs used to generate the radio button list.
  840. * @param array $htmlOptions addtional HTML options.
  841. * @return string the generated radio button list
  842. */
  843. public function radioButtonList($model,$attribute,$data,$htmlOptions=array())
  844. {
  845. return CHtml::activeRadioButtonList($model,$attribute,$data,$htmlOptions);
  846. }
  847. /**
  848. * Validates one or several models and returns the results in JSON format.
  849. * This is a helper method that simplifies the way of writing AJAX validation code.
  850. * @param mixed $models a single model instance or an array of models.
  851. * @param array $attributes list of attributes that should be validated. Defaults to null,
  852. * meaning any attribute listed in the applicable validation rules of the models should be
  853. * validated. If this parameter is given as a list of attributes, only
  854. * the listed attributes will be validated.
  855. * @param boolean $loadInput whether to load the data from $_POST array in this method.
  856. * If this is true, the model will be populated from <code>$_POST[ModelClass]</code>.
  857. * @return string the JSON representation of the validation error messages.
  858. */
  859. public static function validate($models, $attributes=null, $loadInput=true)
  860. {
  861. $result=array();
  862. if(!is_array($models))
  863. $models=array($models);
  864. foreach($models as $model)
  865. {
  866. $modelName=CHtml::modelName($model);
  867. if($loadInput && isset($_POST[$modelName]))
  868. $model->attributes=$_POST[$modelName];
  869. $model->validate($attributes);
  870. foreach($model->getErrors() as $attribute=>$errors)
  871. $result[CHtml::activeId($model,$attribute)]=$errors;
  872. }
  873. return function_exists('json_encode') ? json_encode($result) : CJSON::encode($result);
  874. }
  875. /**
  876. * Validates an array of model instances and returns the results in JSON format.
  877. * This is a helper method that simplifies the way of writing AJAX validation code for tabular input.
  878. * @param mixed $models an array of model instances.
  879. * @param array $attributes list of attributes that should be validated. Defaults to null,
  880. * meaning any attribute listed in the applicable validation rules of the models should be
  881. * validated. If this parameter is given as a list of attributes, only
  882. * the listed attributes will be validated.
  883. * @param boolean $loadInput whether to load the data from $_POST array in this method.
  884. * If this is true, the model will be populated from <code>$_POST[ModelClass][$i]</code>.
  885. * @return string the JSON representation of the validation error messages.
  886. */
  887. public static function validateTabular($models, $attributes=null, $loadInput=true)
  888. {
  889. $result=array();
  890. if(!is_array($models))
  891. $models=array($models);
  892. foreach($models as $i=>$model)
  893. {
  894. $modelName=CHtml::modelName($model);
  895. if($loadInput && isset($_POST[$modelName][$i]))
  896. $model->attributes=$_POST[$modelName][$i];
  897. $model->validate($attributes);
  898. foreach($model->getErrors() as $attribute=>$errors)
  899. $result[CHtml::activeId($model,'['.$i.']'.$attribute)]=$errors;
  900. }
  901. return function_exists('json_encode') ? json_encode($result) : CJSON::encode($result);
  902. }
  903. }