FileloaderWidget.php 9.89 KB
<?php
    namespace common\modules\fileloader\widgets;

    use common\modules\fileloader\assets\FileloaderAsset;
    use common\modules\fileloader\behaviors\FileloaderBehavior;
    use common\modules\fileloader\models\Fileloader;
    use yii\db\ActiveRecord;
    use yii\helpers\ArrayHelper;
    use \yii\helpers\Html;
    use yii\web\View;

    /**
     * Class FileloaderWidget
     * @package common\modules\fileloader\widgets
     */
    class FileloaderWidget extends \yii\base\Widget
    {

        /**
         * @var array $labelOptions Label options.
         * <i>Special: 1. You cannot modify 'for' attribute</i>
         */
        public $labelOptions = [ ];

        /**
         * @var array $inputOptions Input options.
         * <i>
         * Special:
         * 1. You cannot modify 'id' attribute.
         * 2. Class will be appended by base input class
         * <i>
         */
        public $inputOptions = [ ];

        /**
         * @var array $hintOptions Hint options.
         * <i>
         * 1. tag will be used to generate container tag. Default to 'div'
         * 2. value will be used to generate content. If missing, component won't be rendered
         * </i>
         */
        public $hintOptions = [ ];

        /**
         * @var array $errorOptions Error options
         * <i>
         * 1. tag will be used to generate container tag. Default to 'div'
         * 2. class will be appended by base error class
         * </i>
         */
        public $errorOptions = [ ];

        /**
         * @var array $listOptions Items list options
         * <i>
         * 1. tag will be used to generate container tag. Default to 'div'
         * 2. class will be appended by base list class
         * </i>
         */
        public $listOptions = [ ];

        /**
         * @var array $options Container options
         * <i>
         * 1. tag will be used to generate container tag. Default to 'div'
         * 2. class will be appended by base wrapper class
         * </i>
         */
        public $options = [ ];

        /**
         * @var ActiveRecord $model Model where to insert files
         */
        public $model;

        /**
         * @var string $attribute Model attribute
         */
        public $attribute;

        /**
         * @var View $view Current view object
         */
        public $view;

        /**
         * @var string $template Widget template. Recognised components: {label}, {input}, {hint},
         *      {error}, {list}
         */
        public $template = '{label}{input}{hint}{error}{list}';

        /**
         * @var bool $displayLabel Whether to display label component or not
         */
        public $displayLabel = true;

        /**
         * @var bool $displayInput Whether to display input component or not
         */
        public $displayInput = true;

        /**
         * @var bool $displayHint Whether to display hint component or not
         */
        public $displayHint = true;

        /**
         * @var bool $displayError Whether to display error component or not
         */
        public $displayError = true;

        /**
         * @var bool $displayList Whether to display list component or not
         */
        public $displayList = true;

        protected $label = '';

        protected $input = '';

        protected $hint = '';

        protected $error = '';

        protected $list = '';

        /**
         * @var string Unique widget ID
         */
        protected $inputID;

        /**
         * @var Fileloader $fileloader Fileloader model
         */
        protected $fileloader;

        protected $output = '';

        /**
         * @var string $baseClass Will be added to input component
         */
        protected $baseClass = ' fileloader-input';

        /**
         * @var string $baseErrorClass Will be added to error container
         */
        protected $baseErrorClass = ' fileloader-error';

        /**
         * @var string $baseListClass Will be added to list container
         */
        protected $baseListClass = ' fileloader-list';

        /**
         * @var string $baseWrapperClass Will be added to Container
         */
        protected $baseWrapperClass = ' fileloader-wrapper';

        /**
         * @inheritdoc
         */
        public function init()
        {
            parent::init();
            FileloaderAsset::register($this->view);
        }

        /**
         * @inheritdoc
         * @return string
         */
        public function run()
        {
            $this->fileloader = new Fileloader();
            $this->inputID = 'fileloader-' . $this->getId();
            $this->createParts();
            $this->output = $this->renderLoader();
            $this->renderJS();
            return $this->output;
        }

        /**
         * Fills widget components with data
         */
        public function createParts()
        {
            if(!empty( $this->displayLabel ) && preg_match('/^.*\{label\}.*$/i', $this->template)) {
                $this->labelOptions[ 'for' ] = $this->inputID;
                $this->label = Html::activeLabel($this->fileloader, 'files', $this->labelOptions);
            }
            if(!empty( $this->displayInput ) && preg_match('/^.*\{input\}.*$/i', $this->template)) {
                if(empty( $this->inputOptions[ 'class' ] )) {
                    $this->inputOptions[ 'class' ] = $this->baseClass;
                } else {
                    $this->inputOptions[ 'class' ] .= $this->baseClass;
                }
                $this->inputOptions[ 'id' ] = $this->inputID;
                $this->input = Html::activeFileInput($this->fileloader, 'files', $this->inputOptions);
            }
            if(!empty( $this->displayHint ) && preg_match('/^.*\{hint\}.*$/i', $this->template) && !empty( $this->hintOptions[ 'value' ] )) {
                $this->hint = Html::tag(ArrayHelper::remove($this->hintOptions, 'tag', 'div'), ArrayHelper::remove($this->hintOptions, 'value'), $this->hintOptions);
            }
            if(!empty( $this->displayError ) && preg_match('/^.*\{error\}.*$/i', $this->template)) {
                if(empty( $this->errorOptions[ 'class' ] )) {
                    $this->errorOptions[ 'class' ] = $this->baseErrorClass;
                } else {
                    $this->errorOptions[ 'class' ] .= $this->baseErrorClass;
                }
                $this->error = Html::tag(ArrayHelper::remove($this->errorOptions, 'tag', 'div'), '', $this->errorOptions);
            }
            if(!empty( $this->displayList ) && preg_match('/^.*\{list\}.*$/i', $this->template)) {
                if(empty( $this->listOptions[ 'class' ] )) {
                    $this->listOptions[ 'class' ] = $this->baseListClass;
                } else {
                    $this->listOptions[ 'class' ] .= $this->baseListClass;
                }
                $list = '';
                $items = $this->getItems();
                if(!empty( $items )) {
                    foreach($items as $item) {
                        $list .= '<div class="fileloader-item-wrapper" data-id="' . $item->file_id . '">' . Html::activeHiddenInput($this->model, 'fileloader[]', [
                                'value' => $item->file_id,
                                'class' => 'fileloader-item-input',
                            ]) . Html::tag('p', Html::a($item->name, $item->dir, [ 'target' => '_blank' ]), [
                                'class' => 'fileloader-item-name',
                            ]) . Html::tag('span', '', [
                                'class' => 'fileloader-item-remove glyphicon glyphicon-remove',
                            ]) . '</div>';
                    }
                }
                $this->list = Html::tag(ArrayHelper::remove($this->listOptions, 'tag', 'div'), $list, $this->listOptions);
            }
        }

        /**
         * Fills template with components
         * @return string
         */
        public function renderLoader()
        {
            $template = $this->template;
            $template = preg_replace('/{label}/', $this->label, $template);
            $template = preg_replace('/{input}/', $this->input, $template);
            $template = preg_replace('/{hint}/', $this->hint, $template);
            $template = preg_replace('/{error}/', $this->error, $template);
            $template = preg_replace('/{list}/', $this->list, $template);
            if(empty( $this->options[ 'class' ] )) {
                $this->options[ 'class' ] = $this->baseWrapperClass;
            } else {
                $this->options[ 'class' ] .= $this->baseWrapperClass;
            }
            return Html::tag(ArrayHelper::remove($this->options, 'tag', 'div'), $template, $this->options);
        }

        /**
         * Get already set items
         * @return ActiveRecord[]
         */
        public function getItems()
        {
            /**
             * @var ActiveRecord $model
             */
            $model = $this->model;
            if($behavior = $model->getBehavior('fileloader')) {
                if($behavior instanceof FileloaderBehavior) {
                    return $model->getFileloaderFiles()
                                 ->all();
                } else {
                    return [ ];
                }
            } else {
                return [ ];
            }
        }

        /**
         * Render JS for widget
         */
        public function renderJS()
        {
            /**
             * @var View $view
             */
            $view = $this->view;
            $vars = json_encode([
                'id'    => $this->inputID,
                'model' => $this->model->className(),
            ]);
            $js = "if(fileloader === undefined) {
                var fileloader = [];
                fileloader[0] = " . $vars . ";
            } else {
                fileloader[fileloader.length] = " . $vars . ";
            }
            ";
            $view->registerJs($js, $view::POS_BEGIN);
        }

    }