Overview

Packages

  • application
    • commands
    • components
      • actions
      • filters
      • leftWidget
      • permissions
      • sortableWidget
      • util
      • webupdater
      • x2flow
        • actions
        • triggers
      • X2GridView
      • X2Settings
    • controllers
    • models
      • embedded
    • modules
      • accounts
        • controllers
        • models
      • actions
        • controllers
        • models
      • calendar
        • controllers
        • models
      • charts
        • models
      • contacts
        • controllers
        • models
      • docs
        • components
        • controllers
        • models
      • groups
        • controllers
        • models
      • marketing
        • components
        • controllers
        • models
      • media
        • controllers
        • models
      • mobile
        • components
      • opportunities
        • controllers
        • models
      • products
        • controllers
        • models
      • quotes
        • controllers
        • models
      • services
        • controllers
        • models
      • template
        • models
      • users
        • controllers
        • models
      • workflow
        • controllers
        • models
      • x2Leads
        • controllers
        • models
  • Net
  • None
  • PHP
  • system
    • base
    • caching
      • dependencies
    • collections
    • console
    • db
      • ar
      • schema
        • cubrid
        • mssql
        • mysql
        • oci
        • pgsql
        • sqlite
    • i18n
      • gettext
    • logging
    • test
    • utils
    • validators
    • web
      • actions
      • auth
      • filters
      • form
      • helpers
      • renderers
      • services
      • widgets
        • captcha
        • pagers
  • Text
    • Highlighter
  • zii
    • behaviors
    • widgets
      • grid
      • jui

Classes

  • CForm
  • CFormButtonElement
  • CFormElement
  • CFormElementCollection
  • CFormInputElement
  • CFormStringElement
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * CForm 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: /**
 12:  * CForm represents a form object that contains form input specifications.
 13:  *
 14:  * The main purpose of introducing the abstraction of form objects is to enhance the
 15:  * reusability of forms. In particular, we can divide a form in two parts: those
 16:  * that specify each individual form inputs, and those that decorate the form inputs.
 17:  * A CForm object represents the former part. It relies on the rendering process to
 18:  * accomplish form input decoration. Reusability is mainly achieved in the rendering process.
 19:  * That is, a rendering process can be reused to render different CForm objects.
 20:  *
 21:  * A form can be rendered in different ways. One can call the {@link render} method
 22:  * to get a quick form rendering without writing any HTML code; one can also override
 23:  * {@link render} to render the form in a different layout; and one can use an external
 24:  * view template to render each form element explicitly. In these ways, the {@link render}
 25:  * method can be applied to all kinds of forms and thus achieves maximum reusability;
 26:  * while the external view template keeps maximum flexibility in rendering complex forms.
 27:  *
 28:  * Form input specifications are organized in terms of a form element hierarchy.
 29:  * At the root of the hierarchy, it is the root CForm object. The root form object maintains
 30:  * its children in two collections: {@link elements} and {@link buttons}.
 31:  * The former contains non-button form elements ({@link CFormStringElement},
 32:  * {@link CFormInputElement} and CForm); while the latter mainly contains
 33:  * button elements ({@link CFormButtonElement}). When a CForm object is embedded in the
 34:  * {@link elements} collection, it is called a sub-form which can have its own {@link elements}
 35:  * and {@link buttons} collections and thus form the whole form hierarchy.
 36:  *
 37:  * Sub-forms are mainly used to handle multiple models. For example, in a user
 38:  * registration form, we can have the root form to collect input for the user
 39:  * table while a sub-form to collect input for the profile table. Sub-form is also
 40:  * a good way to partition a lengthy form into shorter ones, even though all inputs
 41:  * may belong to the same model.
 42:  *
 43:  * Form input specifications are given in terms of a configuration array which is
 44:  * used to initialize the property values of a CForm object. The {@link elements} and
 45:  * {@link buttons} properties need special attention as they are the main properties
 46:  * to be configured. To configure {@link elements}, we should give it an array like
 47:  * the following:
 48:  * <pre>
 49:  * 'elements'=>array(
 50:  *     'username'=>array('type'=>'text', 'maxlength'=>80),
 51:  *     'password'=>array('type'=>'password', 'maxlength'=>80),
 52:  * )
 53:  * </pre>
 54:  * The above code specifies two input elements: 'username' and 'password'. Note the model
 55:  * object must have exactly the same attributes 'username' and 'password'. Each element
 56:  * has a type which specifies what kind of input should be used. The rest of the array elements
 57:  * (e.g. 'maxlength') in an input specification are rendered as HTML element attributes
 58:  * when the input field is rendered. The {@link buttons} property is configured similarly.
 59:  *
 60:  * If you're going to use AJAX and/or client form validation with the enabled error summary
 61:  * you have to set {@link $showErrors} property to true. Please refer to it's documentation
 62:  * for more details.
 63:  *
 64:  * For more details about configuring form elements, please refer to {@link CFormInputElement}
 65:  * and {@link CFormButtonElement}.
 66:  *
 67:  * @property CForm $root The top-level form object.
 68:  * @property CActiveForm $activeFormWidget The active form widget associated with this form.
 69:  * This method will return the active form widget as specified by {@link activeForm}.
 70:  * @property CBaseController $owner The owner of this form. This refers to either a controller or a widget
 71:  * by which the form is created and rendered.
 72:  * @property CModel $model The model associated with this form. If this form does not have a model,
 73:  * it will look for a model in its ancestors.
 74:  * @property array $models The models that are associated with this form or its sub-forms.
 75:  * @property CFormElementCollection $elements The form elements.
 76:  * @property CFormElementCollection $buttons The form elements.
 77:  *
 78:  * @author Qiang Xue <qiang.xue@gmail.com>
 79:  * @package system.web.form
 80:  * @since 1.1
 81:  */
 82: class CForm extends CFormElement implements ArrayAccess
 83: {
 84:     /**
 85:      * @var string the title for this form. By default, if this is set, a fieldset may be rendered
 86:      * around the form body using the title as its legend. Defaults to null.
 87:      */
 88:     public $title;
 89:     /**
 90:      * @var string the description of this form.
 91:      */
 92:     public $description;
 93:     /**
 94:      * @var string the submission method of this form. Defaults to 'post'.
 95:      * This property is ignored when this form is a sub-form.
 96:      */
 97:     public $method='post';
 98:     /**
 99:      * @var mixed the form action URL (see {@link CHtml::normalizeUrl} for details about this parameter.)
100:      * Defaults to an empty string, meaning the current request URL.
101:      * This property is ignored when this form is a sub-form.
102:      */
103:     public $action='';
104:     /**
105:      * @var string the name of the class for representing a form input element. Defaults to 'CFormInputElement'.
106:      */
107:     public $inputElementClass='CFormInputElement';
108:     /**
109:      * @var string the name of the class for representing a form button element. Defaults to 'CFormButtonElement'.
110:      */
111:     public $buttonElementClass='CFormButtonElement';
112:     /**
113:      * @var array HTML attribute values for the form tag. When the form is embedded within another form,
114:      * this property will be used to render the HTML attribute values for the fieldset enclosing the child form.
115:      */
116:     public $attributes=array();
117:     /**
118:      * @var boolean whether to show error summary. Defaults to false.
119:      */
120:     public $showErrorSummary=false;
121:     /**
122:      * @var boolean|null whether error elements of the form attributes should be rendered. There are three possible
123:      * valid values: null, true and false.
124:      *
125:      * Defaults to null meaning that {@link $showErrorSummary} will be used as value. This is done mainly to keep
126:      * backward compatibility with existing applications. If you want to use error summary with AJAX and/or client
127:      * validation you have to set this property to true (recall that {@link CActiveForm::error()} should be called
128:      * for each attribute that is going to be AJAX and/or client validated).
129:      *
130:      * False value means that the error elements of the form attributes shall not be displayed. True value means that
131:      * the error elements of the form attributes will be rendered.
132:      *
133:      * @since 1.1.14
134:      */
135:     public $showErrors;
136:     /**
137:      * @var string|null HTML code to prepend to the list of errors in the error summary. See {@link CActiveForm::errorSummary()}.
138:      */
139:     public $errorSummaryHeader;
140:     /**
141:      * @var string|null HTML code to append to the list of errors in the error summary. See {@link CActiveForm::errorSummary()}.
142:      */
143:     public $errorSummaryFooter;
144:     /**
145:      * @var array the configuration used to create the active form widget.
146:      * The widget will be used to render the form tag and the error messages.
147:      * The 'class' option is required, which specifies the class of the widget.
148:      * The rest of the options will be passed to {@link CBaseController::beginWidget()} call.
149:      * Defaults to array('class'=>'CActiveForm').
150:      * @since 1.1.1
151:      */
152:     public $activeForm=array('class'=>'CActiveForm');
153: 
154:     private $_model;
155:     private $_elements;
156:     private $_buttons;
157:     private $_activeForm;
158: 
159:     /**
160:      * Constructor.
161:      * If you override this method, make sure you do not modify the method
162:      * signature, and also make sure you call the parent implementation.
163:      * @param mixed $config the configuration for this form. It can be a configuration array
164:      * or the path alias of a PHP script file that returns a configuration array.
165:      * The configuration array consists of name-value pairs that are used to initialize
166:      * the properties of this form.
167:      * @param CModel $model the model object associated with this form. If it is null,
168:      * the parent's model will be used instead.
169:      * @param mixed $parent the direct parent of this form. This could be either a {@link CBaseController}
170:      * object (a controller or a widget), or a {@link CForm} object.
171:      * If the former, it means the form is a top-level form; if the latter, it means this form is a sub-form.
172:      */
173:     public function __construct($config,$model=null,$parent=null)
174:     {
175:         $this->setModel($model);
176:         if($parent===null)
177:             $parent=Yii::app()->getController();
178:         parent::__construct($config,$parent);
179:         if($this->showErrors===null)
180:             $this->showErrors=!$this->showErrorSummary;
181:         $this->init();
182:     }
183: 
184:     /**
185:      * Initializes this form.
186:      * This method is invoked at the end of the constructor.
187:      * You may override this method to provide customized initialization (such as
188:      * configuring the form object).
189:      */
190:     protected function init()
191:     {
192:     }
193: 
194:     /**
195:      * Returns a value indicating whether this form is submitted.
196:      * @param string $buttonName the name of the submit button
197:      * @param boolean $loadData whether to call {@link loadData} if the form is submitted so that
198:      * the submitted data can be populated to the associated models.
199:      * @return boolean whether this form is submitted.
200:      * @see loadData
201:      */
202:     public function submitted($buttonName='submit',$loadData=true)
203:     {
204:         $ret=$this->clicked($this->getUniqueId()) && $this->clicked($buttonName);
205:         if($ret && $loadData)
206:             $this->loadData();
207:         return $ret;
208:     }
209: 
210:     /**
211:      * Returns a value indicating whether the specified button is clicked.
212:      * @param string $name the button name
213:      * @return boolean whether the button is clicked.
214:      */
215:     public function clicked($name)
216:     {
217:         if(strcasecmp($this->getRoot()->method,'get'))
218:             return isset($_POST[$name]);
219:         else
220:             return isset($_GET[$name]);
221:     }
222: 
223:     /**
224:      * Validates the models associated with this form.
225:      * All models, including those associated with sub-forms, will perform
226:      * the validation. You may use {@link CModel::getErrors()} to retrieve the validation
227:      * error messages.
228:      * @return boolean whether all models are valid
229:      */
230:     public function validate()
231:     {
232:         $ret=true;
233:         foreach($this->getModels() as $model)
234:             $ret=$model->validate() && $ret;
235:         return $ret;
236:     }
237: 
238:     /**
239:      * Loads the submitted data into the associated model(s) to the form.
240:      * This method will go through all models associated with this form and its sub-forms
241:      * and massively assign the submitted data to the models.
242:      * @see submitted
243:      */
244:     public function loadData()
245:     {
246:         if($this->_model!==null)
247:         {
248:             $class=CHtml::modelName($this->_model);
249:             if(strcasecmp($this->getRoot()->method,'get'))
250:             {
251:                 if(isset($_POST[$class]))
252:                     $this->_model->setAttributes($_POST[$class]);
253:             }
254:             elseif(isset($_GET[$class]))
255:                 $this->_model->setAttributes($_GET[$class]);
256:         }
257:         foreach($this->getElements() as $element)
258:         {
259:             if($element instanceof self)
260:                 $element->loadData();
261:         }
262:     }
263: 
264:     /**
265:      * @return CForm the top-level form object
266:      */
267:     public function getRoot()
268:     {
269:         $root=$this;
270:         while($root->getParent() instanceof self)
271:             $root=$root->getParent();
272:         return $root;
273:     }
274: 
275:     /**
276:      * @return CActiveForm the active form widget associated with this form.
277:      * This method will return the active form widget as specified by {@link activeForm}.
278:      * @since 1.1.1
279:      */
280:     public function getActiveFormWidget()
281:     {
282:         if($this->_activeForm!==null)
283:             return $this->_activeForm;
284:         else
285:             return $this->getRoot()->_activeForm;
286:     }
287: 
288:     /**
289:      * @return CBaseController the owner of this form. This refers to either a controller or a widget
290:      * by which the form is created and rendered.
291:      */
292:     public function getOwner()
293:     {
294:         $owner=$this->getParent();
295:         while($owner instanceof self)
296:             $owner=$owner->getParent();
297:         return $owner;
298:     }
299: 
300:     /**
301:      * Returns the model that this form is associated with.
302:      * @param boolean $checkParent whether to return parent's model if this form doesn't have model by itself.
303:      * @return CModel the model associated with this form. If this form does not have a model,
304:      * it will look for a model in its ancestors.
305:      */
306:     public function getModel($checkParent=true)
307:     {
308:         if(!$checkParent)
309:             return $this->_model;
310:         $form=$this;
311:         while($form->_model===null && $form->getParent() instanceof self)
312:             $form=$form->getParent();
313:         return $form->_model;
314:     }
315: 
316:     /**
317:      * @param CModel $model the model to be associated with this form
318:      */
319:     public function setModel($model)
320:     {
321:         $this->_model=$model;
322:     }
323: 
324:     /**
325:      * Returns all models that are associated with this form or its sub-forms.
326:      * @return array the models that are associated with this form or its sub-forms.
327:      */
328:     public function getModels()
329:     {
330:         $models=array();
331:         if($this->_model!==null)
332:             $models[]=$this->_model;
333:         foreach($this->getElements() as $element)
334:         {
335:             if($element instanceof self)
336:                 $models=array_merge($models,$element->getModels());
337:         }
338:         return $models;
339:     }
340: 
341:     /**
342:      * Returns the input elements of this form.
343:      * This includes text strings, input elements and sub-forms.
344:      * Note that the returned result is a {@link CFormElementCollection} object, which
345:      * means you can use it like an array. For more details, see {@link CMap}.
346:      * @return CFormElementCollection the form elements.
347:      */
348:     public function getElements()
349:     {
350:         if($this->_elements===null)
351:             $this->_elements=new CFormElementCollection($this,false);
352:         return $this->_elements;
353:     }
354: 
355:     /**
356:      * Configures the input elements of this form.
357:      * The configuration must be an array of input configuration array indexed by input name.
358:      * Each input configuration array consists of name-value pairs that are used to initialize
359:      * a {@link CFormStringElement} object (when 'type' is 'string'), a {@link CFormElement} object
360:      * (when 'type' is a string ending with 'Form'), or a {@link CFormInputElement} object in
361:      * all other cases.
362:      * @param array $elements the elements configurations
363:      */
364:     public function setElements($elements)
365:     {
366:         $collection=$this->getElements();
367:         foreach($elements as $name=>$config)
368:             $collection->add($name,$config);
369:     }
370: 
371:     /**
372:      * Returns the button elements of this form.
373:      * Note that the returned result is a {@link CFormElementCollection} object, which
374:      * means you can use it like an array. For more details, see {@link CMap}.
375:      * @return CFormElementCollection the form elements.
376:      */
377:     public function getButtons()
378:     {
379:         if($this->_buttons===null)
380:             $this->_buttons=new CFormElementCollection($this,true);
381:         return $this->_buttons;
382:     }
383: 
384:     /**
385:      * Configures the buttons of this form.
386:      * The configuration must be an array of button configuration array indexed by button name.
387:      * Each button configuration array consists of name-value pairs that are used to initialize
388:      * a {@link CFormButtonElement} object.
389:      * @param array $buttons the button configurations
390:      */
391:     public function setButtons($buttons)
392:     {
393:         $collection=$this->getButtons();
394:         foreach($buttons as $name=>$config)
395:             $collection->add($name,$config);
396:     }
397: 
398:     /**
399:      * Renders the form.
400:      * The default implementation simply calls {@link renderBegin}, {@link renderBody} and {@link renderEnd}.
401:      * @return string the rendering result
402:      */
403:     public function render()
404:     {
405:         return $this->renderBegin() . $this->renderBody() . $this->renderEnd();
406:     }
407: 
408:     /**
409:      * Renders the open tag of the form.
410:      * The default implementation will render the open form tag.
411:      * @return string the rendering result
412:      */
413:     public function renderBegin()
414:     {
415:         if($this->getParent() instanceof self)
416:             return '';
417:         else
418:         {
419:             $options=$this->activeForm;
420:             if(isset($options['class']))
421:             {
422:                 $class=$options['class'];
423:                 unset($options['class']);
424:             }
425:             else
426:                 $class='CActiveForm';
427:             $options['action']=$this->action;
428:             $options['method']=$this->method;
429:             if(isset($options['htmlOptions']))
430:             {
431:                 foreach($this->attributes as $name=>$value)
432:                     $options['htmlOptions'][$name]=$value;
433:             }
434:             else
435:                 $options['htmlOptions']=$this->attributes;
436:             ob_start();
437:             $this->_activeForm=$this->getOwner()->beginWidget($class, $options);
438:             return ob_get_clean() . "<div style=\"visibility:hidden\">".CHtml::hiddenField($this->getUniqueID(),1)."</div>\n";
439:         }
440:     }
441: 
442:     /**
443:      * Renders the close tag of the form.
444:      * @return string the rendering result
445:      */
446:     public function renderEnd()
447:     {
448:         if($this->getParent() instanceof self)
449:             return '';
450:         else
451:         {
452:             ob_start();
453:             $this->getOwner()->endWidget();
454:             return ob_get_clean();
455:         }
456:     }
457: 
458:     /**
459:      * Renders the body content of this form.
460:      * This method mainly renders {@link elements} and {@link buttons}.
461:      * If {@link title} or {@link description} is specified, they will be rendered as well.
462:      * And if the associated model contains error, the error summary may also be displayed.
463:      * The form tag will not be rendered. Please call {@link renderBegin} and {@link renderEnd}
464:      * to render the open and close tags of the form.
465:      * You may override this method to customize the rendering of the form.
466:      * @return string the rendering result
467:      */
468:     public function renderBody()
469:     {
470:         $output='';
471:         if($this->title!==null)
472:         {
473:             if($this->getParent() instanceof self)
474:             {
475:                 $attributes=$this->attributes;
476:                 unset($attributes['name'],$attributes['type']);
477:                 $output=CHtml::openTag('fieldset', $attributes)."<legend>".$this->title."</legend>\n";
478:             }
479:             else
480:                 $output="<fieldset>\n<legend>".$this->title."</legend>\n";
481:         }
482: 
483:         if($this->description!==null)
484:             $output.="<div class=\"description\">\n".$this->description."</div>\n";
485: 
486:         if($this->showErrorSummary && ($model=$this->getModel(false))!==null)
487:             $output.=$this->getActiveFormWidget()->errorSummary($model,$this->errorSummaryHeader,$this->errorSummaryFooter)."\n";
488: 
489:         $output.=$this->renderElements()."\n".$this->renderButtons()."\n";
490: 
491:         if($this->title!==null)
492:             $output.="</fieldset>\n";
493: 
494:         return $output;
495:     }
496: 
497:     /**
498:      * Renders the {@link elements} in this form.
499:      * @return string the rendering result
500:      */
501:     public function renderElements()
502:     {
503:         $output='';
504:         foreach($this->getElements() as $element)
505:             $output.=$this->renderElement($element);
506:         return $output;
507:     }
508: 
509:     /**
510:      * Renders the {@link buttons} in this form.
511:      * @return string the rendering result
512:      */
513:     public function renderButtons()
514:     {
515:         $output='';
516:         foreach($this->getButtons() as $button)
517:             $output.=$this->renderElement($button);
518:         return $output!=='' ? "<div class=\"row buttons\">".$output."</div>\n" : '';
519:     }
520: 
521:     /**
522:      * Renders a single element which could be an input element, a sub-form, a string, or a button.
523:      * @param mixed $element the form element to be rendered. This can be either a {@link CFormElement} instance
524:      * or a string representing the name of the form element.
525:      * @return string the rendering result
526:      */
527:     public function renderElement($element)
528:     {
529:         if(is_string($element))
530:         {
531:             if(($e=$this[$element])===null && ($e=$this->getButtons()->itemAt($element))===null)
532:                 return $element;
533:             else
534:                 $element=$e;
535:         }
536:         if($element->getVisible())
537:         {
538:             if($element instanceof CFormInputElement)
539:             {
540:                 if($element->type==='hidden')
541:                     return "<div style=\"visibility:hidden\">\n".$element->render()."</div>\n";
542:                 else
543:                     return "<div class=\"row field_{$element->name}\">\n".$element->render()."</div>\n";
544:             }
545:             elseif($element instanceof CFormButtonElement)
546:                 return $element->render()."\n";
547:             else
548:                 return $element->render();
549:         }
550:         return '';
551:     }
552: 
553:     /**
554:      * This method is called after an element is added to the element collection.
555:      * @param string $name the name of the element
556:      * @param CFormElement $element the element that is added
557:      * @param boolean $forButtons whether the element is added to the {@link buttons} collection.
558:      * If false, it means the element is added to the {@link elements} collection.
559:      */
560:     public function addedElement($name,$element,$forButtons)
561:     {
562:     }
563: 
564:     /**
565:      * This method is called after an element is removed from the element collection.
566:      * @param string $name the name of the element
567:      * @param CFormElement $element the element that is removed
568:      * @param boolean $forButtons whether the element is removed from the {@link buttons} collection
569:      * If false, it means the element is removed from the {@link elements} collection.
570:      */
571:     public function removedElement($name,$element,$forButtons)
572:     {
573:     }
574: 
575:     /**
576:      * Evaluates the visibility of this form.
577:      * This method will check the visibility of the {@link elements}.
578:      * If any one of them is visible, the form is considered as visible. Otherwise, it is invisible.
579:      * @return boolean whether this form is visible.
580:      */
581:     protected function evaluateVisible()
582:     {
583:         foreach($this->getElements() as $element)
584:             if($element->getVisible())
585:                 return true;
586:         return false;
587:     }
588: 
589:     /**
590:      * Returns a unique ID that identifies this form in the current page.
591:      * @return string the unique ID identifying this form
592:      */
593:     protected function getUniqueId()
594:     {
595:         if(isset($this->attributes['id']))
596:             return 'yform_'.$this->attributes['id'];
597:         else
598:             return 'yform_'.sprintf('%x',crc32(serialize(array_keys($this->getElements()->toArray()))));
599:     }
600: 
601:     /**
602:      * Returns whether there is an element at the specified offset.
603:      * This method is required by the interface ArrayAccess.
604:      * @param mixed $offset the offset to check on
605:      * @return boolean
606:      */
607:     public function offsetExists($offset)
608:     {
609:         return $this->getElements()->contains($offset);
610:     }
611: 
612:     /**
613:      * Returns the element at the specified offset.
614:      * This method is required by the interface ArrayAccess.
615:      * @param integer $offset the offset to retrieve element.
616:      * @return mixed the element at the offset, null if no element is found at the offset
617:      */
618:     public function offsetGet($offset)
619:     {
620:         return $this->getElements()->itemAt($offset);
621:     }
622: 
623:     /**
624:      * Sets the element at the specified offset.
625:      * This method is required by the interface ArrayAccess.
626:      * @param integer $offset the offset to set element
627:      * @param mixed $item the element value
628:      */
629:     public function offsetSet($offset,$item)
630:     {
631:         $this->getElements()->add($offset,$item);
632:     }
633: 
634:     /**
635:      * Unsets the element at the specified offset.
636:      * This method is required by the interface ArrayAccess.
637:      * @param mixed $offset the offset to unset element
638:      */
639:     public function offsetUnset($offset)
640:     {
641:         $this->getElements()->remove($offset);
642:     }
643: }
644: 
API documentation generated by ApiGen 2.8.0