You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							383 lines
						
					
					
						
							14 KiB
						
					
					
				
			
		
		
	
	
							383 lines
						
					
					
						
							14 KiB
						
					
					
				<?php | 
						|
/** | 
						|
 * @link http://www.yiiframework.com/ | 
						|
 * @copyright Copyright (c) 2008 Yii Software LLC | 
						|
 * @license http://www.yiiframework.com/license/ | 
						|
 */ | 
						|
 | 
						|
namespace yii\bootstrap; | 
						|
 | 
						|
use yii\helpers\Html; | 
						|
use yii\helpers\ArrayHelper; | 
						|
 | 
						|
/** | 
						|
 * A Bootstrap 3 enhanced version of [[\yii\widgets\ActiveField]]. | 
						|
 * | 
						|
 * This class adds some useful features to [[\yii\widgets\ActiveField|ActiveField]] to render all | 
						|
 * sorts of Bootstrap 3 form fields in different form layouts: | 
						|
 * | 
						|
 * - [[inputTemplate]] is an optional template to render complex inputs, for example input groups | 
						|
 * - [[horizontalCssClasses]] defines the CSS grid classes to add to label, wrapper, error and hint | 
						|
 *   in horizontal forms | 
						|
 * - [[inline]]/[[inline()]] is used to render inline [[checkboxList()]] and [[radioList()]] | 
						|
 * - [[enableError]] can be set to `false` to disable to the error | 
						|
 * - [[enableLabel]] can be set to `false` to disable to the label | 
						|
 * - [[label()]] can be used with a `boolean` argument to enable/disable the label | 
						|
 * | 
						|
 * There are also some new placeholders that you can use in the [[template]] configuration: | 
						|
 * | 
						|
 * - `{beginLabel}`: the opening label tag | 
						|
 * - `{labelTitle}`: the label title for use with `{beginLabel}`/`{endLabel}` | 
						|
 * - `{endLabel}`: the closing label tag | 
						|
 * - `{beginWrapper}`: the opening wrapper tag | 
						|
 * - `{endWrapper}`: the closing wrapper tag | 
						|
 * | 
						|
 * The wrapper tag is only used for some layouts and form elements. | 
						|
 * | 
						|
 * Note that some elements use slightly different defaults for [[template]] and other options. | 
						|
 * You may want to override those predefined templates for checkboxes, radio buttons, checkboxLists | 
						|
 * and radioLists in the [[\yii\widgets\ActiveForm::fieldConfig|fieldConfig]] of the | 
						|
 * [[\yii\widgets\ActiveForm]]: | 
						|
 * | 
						|
 * - [[checkboxTemplate]] the template for checkboxes in default layout | 
						|
 * - [[radioTemplate]] the template for radio buttons in default layout | 
						|
 * - [[horizontalCheckboxTemplate]] the template for checkboxes in horizontal layout | 
						|
 * - [[horizontalRadioTemplate]] the template for radio buttons in horizontal layout | 
						|
 * - [[inlineCheckboxListTemplate]] the template for inline checkboxLists | 
						|
 * - [[inlineRadioListTemplate]] the template for inline radioLists | 
						|
 * | 
						|
 * Example: | 
						|
 * | 
						|
 * ```php | 
						|
 * use yii\bootstrap\ActiveForm; | 
						|
 * | 
						|
 * $form = ActiveForm::begin(['layout' => 'horizontal']) | 
						|
 * | 
						|
 * // Form field without label | 
						|
 * echo $form->field($model, 'demo', [ | 
						|
 *     'inputOptions' => [ | 
						|
 *         'placeholder' => $model->getAttributeLabel('demo'), | 
						|
 *     ], | 
						|
 * ])->label(false); | 
						|
 * | 
						|
 * // Inline radio list | 
						|
 * echo $form->field($model, 'demo')->inline()->radioList($items); | 
						|
 * | 
						|
 * // Control sizing in horizontal mode | 
						|
 * echo $form->field($model, 'demo', [ | 
						|
 *     'horizontalCssClasses' => [ | 
						|
 *         'wrapper' => 'col-sm-2', | 
						|
 *     ] | 
						|
 * ]); | 
						|
 * | 
						|
 * // With 'default' layout you would use 'template' to size a specific field: | 
						|
 * // echo $form->field($model, 'demo', [ | 
						|
 * //     'template' => '{label} <div class="row"><div class="col-sm-4">{input}{error}{hint}</div></div>' | 
						|
 * // ]); | 
						|
 * | 
						|
 *  // Input group | 
						|
 *  echo $form->field($model, 'demo', [ | 
						|
 *      'inputTemplate' => '<div class="input-group"><span class="input-group-addon">@</span>{input}</div>', | 
						|
 *  ]); | 
						|
 * | 
						|
 *  ActiveForm::end(); | 
						|
 *  ``` | 
						|
 * | 
						|
 * @see \yii\bootstrap\ActiveForm | 
						|
 * @see http://getbootstrap.com/css/#forms | 
						|
 * | 
						|
 * @author Michael Härtl <haertl.mike@gmail.com> | 
						|
 * @since 2.0 | 
						|
 */ | 
						|
class ActiveField extends \yii\widgets\ActiveField | 
						|
{ | 
						|
    /** | 
						|
     * @var bool whether to render [[checkboxList()]] and [[radioList()]] inline. | 
						|
     */ | 
						|
    public $inline = false; | 
						|
    /** | 
						|
     * @var string|null optional template to render the `{input}` placeholder content | 
						|
     */ | 
						|
    public $inputTemplate; | 
						|
    /** | 
						|
     * @var array options for the wrapper tag, used in the `{beginWrapper}` placeholder | 
						|
     */ | 
						|
    public $wrapperOptions = []; | 
						|
    /** | 
						|
     * @var null|array CSS grid classes for horizontal layout. This must be an array with these keys: | 
						|
     *  - 'offset' the offset grid class to append to the wrapper if no label is rendered | 
						|
     *  - 'label' the label grid class | 
						|
     *  - 'wrapper' the wrapper grid class | 
						|
     *  - 'error' the error grid class | 
						|
     *  - 'hint' the hint grid class | 
						|
     */ | 
						|
    public $horizontalCssClasses; | 
						|
    /** | 
						|
     * @var string the template for checkboxes in default layout | 
						|
     */ | 
						|
    public $checkboxTemplate = "<div class=\"checkbox\">\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n{error}\n{hint}\n</div>"; | 
						|
    /** | 
						|
     * @var string the template for radios in default layout | 
						|
     */ | 
						|
    public $radioTemplate = "<div class=\"radio\">\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n{error}\n{hint}\n</div>"; | 
						|
    /** | 
						|
     * @var string the template for checkboxes in horizontal layout | 
						|
     */ | 
						|
    public $horizontalCheckboxTemplate = "{beginWrapper}\n<div class=\"checkbox\">\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n</div>\n{error}\n{endWrapper}\n{hint}"; | 
						|
    /** | 
						|
     * @var string the template for radio buttons in horizontal layout | 
						|
     */ | 
						|
    public $horizontalRadioTemplate = "{beginWrapper}\n<div class=\"radio\">\n{beginLabel}\n{input}\n{labelTitle}\n{endLabel}\n</div>\n{error}\n{endWrapper}\n{hint}"; | 
						|
    /** | 
						|
     * @var string the template for inline checkboxLists | 
						|
     */ | 
						|
    public $inlineCheckboxListTemplate = "{label}\n{beginWrapper}\n{input}\n{error}\n{endWrapper}\n{hint}"; | 
						|
    /** | 
						|
     * @var string the template for inline radioLists | 
						|
     */ | 
						|
    public $inlineRadioListTemplate = "{label}\n{beginWrapper}\n{input}\n{error}\n{endWrapper}\n{hint}"; | 
						|
    /** | 
						|
     * @var bool whether to render the error. Default is `true` except for layout `inline`. | 
						|
     */ | 
						|
    public $enableError = true; | 
						|
    /** | 
						|
     * @var bool whether to render the label. Default is `true`. | 
						|
     */ | 
						|
    public $enableLabel = true; | 
						|
 | 
						|
 | 
						|
    /** | 
						|
     * @inheritdoc | 
						|
     */ | 
						|
    public function __construct($config = []) | 
						|
    { | 
						|
        $layoutConfig = $this->createLayoutConfig($config); | 
						|
        $config = ArrayHelper::merge($layoutConfig, $config); | 
						|
        return parent::__construct($config); | 
						|
    } | 
						|
 | 
						|
    /** | 
						|
     * @inheritdoc | 
						|
     */ | 
						|
    public function render($content = null) | 
						|
    { | 
						|
        if ($content === null) { | 
						|
            if (!isset($this->parts['{beginWrapper}'])) { | 
						|
                $options = $this->wrapperOptions; | 
						|
                $tag = ArrayHelper::remove($options, 'tag', 'div'); | 
						|
                $this->parts['{beginWrapper}'] = Html::beginTag($tag, $options); | 
						|
                $this->parts['{endWrapper}'] = Html::endTag($tag); | 
						|
            } | 
						|
            if ($this->enableLabel === false) { | 
						|
                $this->parts['{label}'] = ''; | 
						|
                $this->parts['{beginLabel}'] = ''; | 
						|
                $this->parts['{labelTitle}'] = ''; | 
						|
                $this->parts['{endLabel}'] = ''; | 
						|
            } elseif (!isset($this->parts['{beginLabel}'])) { | 
						|
                $this->renderLabelParts(); | 
						|
            } | 
						|
            if ($this->enableError === false) { | 
						|
                $this->parts['{error}'] = ''; | 
						|
            } | 
						|
            if ($this->inputTemplate) { | 
						|
                $input = isset($this->parts['{input}']) ? | 
						|
                    $this->parts['{input}'] : Html::activeTextInput($this->model, $this->attribute, $this->inputOptions); | 
						|
                $this->parts['{input}'] = strtr($this->inputTemplate, ['{input}' => $input]); | 
						|
            } | 
						|
        } | 
						|
        return parent::render($content); | 
						|
    } | 
						|
 | 
						|
    /** | 
						|
     * @inheritdoc | 
						|
     */ | 
						|
    public function checkbox($options = [], $enclosedByLabel = true) | 
						|
    { | 
						|
        if ($enclosedByLabel) { | 
						|
            if (!isset($options['template'])) { | 
						|
                $this->template = $this->form->layout === 'horizontal' ? | 
						|
                    $this->horizontalCheckboxTemplate : $this->checkboxTemplate; | 
						|
            } else { | 
						|
                $this->template = $options['template']; | 
						|
                unset($options['template']); | 
						|
            } | 
						|
            if ($this->form->layout === 'horizontal') { | 
						|
                Html::addCssClass($this->wrapperOptions, $this->horizontalCssClasses['offset']); | 
						|
            } | 
						|
            $this->labelOptions['class'] = null; | 
						|
        } | 
						|
 | 
						|
        return parent::checkbox($options, false); | 
						|
    } | 
						|
 | 
						|
    /** | 
						|
     * @inheritdoc | 
						|
     */ | 
						|
    public function radio($options = [], $enclosedByLabel = true) | 
						|
    { | 
						|
        if ($enclosedByLabel) { | 
						|
            if (!isset($options['template'])) { | 
						|
                $this->template = $this->form->layout === 'horizontal' ? | 
						|
                    $this->horizontalRadioTemplate : $this->radioTemplate; | 
						|
            } else { | 
						|
                $this->template = $options['template']; | 
						|
                unset($options['template']); | 
						|
            } | 
						|
            if ($this->form->layout === 'horizontal') { | 
						|
                Html::addCssClass($this->wrapperOptions, $this->horizontalCssClasses['offset']); | 
						|
            } | 
						|
            $this->labelOptions['class'] = null; | 
						|
        } | 
						|
 | 
						|
        return parent::radio($options, false); | 
						|
    } | 
						|
 | 
						|
    /** | 
						|
     * @inheritdoc | 
						|
     */ | 
						|
    public function checkboxList($items, $options = []) | 
						|
    { | 
						|
        if ($this->inline) { | 
						|
            if (!isset($options['template'])) { | 
						|
                $this->template = $this->inlineCheckboxListTemplate; | 
						|
            } else { | 
						|
                $this->template = $options['template']; | 
						|
                unset($options['template']); | 
						|
            } | 
						|
            if (!isset($options['itemOptions'])) { | 
						|
                $options['itemOptions'] = [ | 
						|
                    'labelOptions' => ['class' => 'checkbox-inline'], | 
						|
                ]; | 
						|
            } | 
						|
        }  elseif (!isset($options['item'])) { | 
						|
            $options['item'] = function ($index, $label, $name, $checked, $value) { | 
						|
                return '<div class="checkbox">' . Html::checkbox($name, $checked, ['label' => $label, 'value' => $value]) . '</div>'; | 
						|
            }; | 
						|
        } | 
						|
        parent::checkboxList($items, $options); | 
						|
        return $this; | 
						|
    } | 
						|
 | 
						|
    /** | 
						|
     * @inheritdoc | 
						|
     */ | 
						|
    public function radioList($items, $options = []) | 
						|
    { | 
						|
        if ($this->inline) { | 
						|
            if (!isset($options['template'])) { | 
						|
                $this->template = $this->inlineRadioListTemplate; | 
						|
            } else { | 
						|
                $this->template = $options['template']; | 
						|
                unset($options['template']); | 
						|
            } | 
						|
            if (!isset($options['itemOptions'])) { | 
						|
                $options['itemOptions'] = [ | 
						|
                    'labelOptions' => ['class' => 'radio-inline'], | 
						|
                ]; | 
						|
            } | 
						|
        }  elseif (!isset($options['item'])) { | 
						|
            $options['item'] = function ($index, $label, $name, $checked, $value) { | 
						|
                return '<div class="radio">' . Html::radio($name, $checked, ['label' => $label, 'value' => $value]) . '</div>'; | 
						|
            }; | 
						|
        } | 
						|
        parent::radioList($items, $options); | 
						|
        return $this; | 
						|
    } | 
						|
 | 
						|
    /** | 
						|
     * @inheritdoc | 
						|
     */ | 
						|
    public function label($label = null, $options = []) | 
						|
    { | 
						|
        if (is_bool($label)) { | 
						|
            $this->enableLabel = $label; | 
						|
            if ($label === false && $this->form->layout === 'horizontal') { | 
						|
                Html::addCssClass($this->wrapperOptions, $this->horizontalCssClasses['offset']); | 
						|
            } | 
						|
        } else { | 
						|
            $this->enableLabel = true; | 
						|
            $this->renderLabelParts($label, $options); | 
						|
            parent::label($label, $options); | 
						|
        } | 
						|
        return $this; | 
						|
    } | 
						|
 | 
						|
    /** | 
						|
     * @param bool $value whether to render a inline list | 
						|
     * @return static the field object itself | 
						|
     * Make sure you call this method before [[checkboxList()]] or [[radioList()]] to have any effect. | 
						|
     */ | 
						|
    public function inline($value = true) | 
						|
    { | 
						|
        $this->inline = (bool)$value; | 
						|
        return $this; | 
						|
    } | 
						|
 | 
						|
    /** | 
						|
     * @param array $instanceConfig the configuration passed to this instance's constructor | 
						|
     * @return array the layout specific default configuration for this instance | 
						|
     */ | 
						|
    protected function createLayoutConfig($instanceConfig) | 
						|
    { | 
						|
        $config = [ | 
						|
            'hintOptions' => [ | 
						|
                'tag' => 'p', | 
						|
                'class' => 'help-block', | 
						|
            ], | 
						|
            'errorOptions' => [ | 
						|
                'tag' => 'p', | 
						|
                'class' => 'help-block help-block-error', | 
						|
            ], | 
						|
            'inputOptions' => [ | 
						|
                'class' => 'form-control', | 
						|
            ], | 
						|
        ]; | 
						|
 | 
						|
        $layout = $instanceConfig['form']->layout; | 
						|
 | 
						|
        if ($layout === 'horizontal') { | 
						|
            $config['template'] = "{label}\n{beginWrapper}\n{input}\n{error}\n{endWrapper}\n{hint}"; | 
						|
            $cssClasses = [ | 
						|
                'offset' => 'col-sm-offset-3', | 
						|
                'label' => 'col-sm-3', | 
						|
                'wrapper' => 'col-sm-6', | 
						|
                'error' => '', | 
						|
                'hint' => 'col-sm-3', | 
						|
            ]; | 
						|
            if (isset($instanceConfig['horizontalCssClasses'])) { | 
						|
                $cssClasses = ArrayHelper::merge($cssClasses, $instanceConfig['horizontalCssClasses']); | 
						|
            } | 
						|
            $config['horizontalCssClasses'] = $cssClasses; | 
						|
            $config['wrapperOptions'] = ['class' => $cssClasses['wrapper']]; | 
						|
            $config['labelOptions'] = ['class' => 'control-label ' . $cssClasses['label']]; | 
						|
            $config['errorOptions'] = ['class' => 'help-block help-block-error ' . $cssClasses['error']]; | 
						|
            $config['hintOptions'] = ['class' => 'help-block ' . $cssClasses['hint']]; | 
						|
        } elseif ($layout === 'inline') { | 
						|
            $config['labelOptions'] = ['class' => 'sr-only']; | 
						|
            $config['enableError'] = false; | 
						|
        } | 
						|
 | 
						|
        return $config; | 
						|
    } | 
						|
 | 
						|
    /** | 
						|
     * @param string|null $label the label or null to use model label | 
						|
     * @param array $options the tag options | 
						|
     */ | 
						|
    protected function renderLabelParts($label = null, $options = []) | 
						|
    { | 
						|
        $options = array_merge($this->labelOptions, $options); | 
						|
        if ($label === null) { | 
						|
            if (isset($options['label'])) { | 
						|
                $label = $options['label']; | 
						|
                unset($options['label']); | 
						|
            } else { | 
						|
                $attribute = Html::getAttributeName($this->attribute); | 
						|
                $label = Html::encode($this->model->getAttributeLabel($attribute)); | 
						|
            } | 
						|
        } | 
						|
        $this->parts['{beginLabel}'] = Html::beginTag('label', $options); | 
						|
        $this->parts['{endLabel}'] = Html::endTag('label'); | 
						|
        $this->parts['{labelTitle}'] = $label; | 
						|
    } | 
						|
}
 | 
						|
 |