InternalObjectCombo   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 170
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 5
dl 0
loc 170
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 13 2
A generateConfigs() 0 6 2
A registerChangerScript() 0 7 1
A applyConfigByObjectClassName() 0 15 4
A fillRequiredAttributes() 0 6 1
A applyDefaultAttributes() 0 4 2
B registerSpecialAssets() 0 56 1
A reset() 0 9 2
1
<?php
2
/**
3
 * HiPanel core package
4
 *
5
 * @link      https://hipanel.com/
6
 * @package   hipanel-core
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2014-2019, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hipanel\widgets\combo;
12
13
use hiqdev\combo\Combo;
14
use ReflectionClass;
15
use yii\base\InvalidConfigException;
16
use yii\bootstrap\Html;
17
use yii\web\View;
18
19
/**
20
 * Class InternalObjectCombo.
21
 */
22
class InternalObjectCombo extends Combo
23
{
24
    /** {@inheritdoc} */
25
    public $type = 'hipanel/object';
26
27
    /** {@inheritdoc} */
28
    public $name;
29
30
    /** {@inheritdoc} */
31
    public $url;
32
33
    /** {@inheritdoc} */
34
    public $_return;
35
36
    /** {@inheritdoc} */
37
    public $_rename;
38
39
    /** {@inheritdoc} */
40
    public $_primaryFilter;
41
42
    /**
43
     * @uses ObjectCombo::getClasses()
44
     *
45
     * @var array
46
     */
47
    public $classes = [];
48
49
    /**
50
     * @var string
51
     */
52
    public $class_attribute;
53
54
    /**
55
     * @var string
56
     */
57
    public $class_attribute_name;
58
59
    /**
60
     * @var array
61
     */
62
    private $requiredAttributes = [];
63
64
    /** {@inheritdoc} */
65
    public function init()
66
    {
67
        if (empty($this->classes)) {
68
            throw new InvalidConfigException('Property `classes` is required for class InternalObjectCombo.');
69
        }
70
        $this->inputOptions = ['data-object-selector-field' => true, 'class' => 'object-selector-select'];
71
        $this->registerSpecialAssets();
72
        $this->fillRequiredAttributes();
73
        $this->generateConfigs();
74
        parent::init();
75
        $this->registerChangerScript();
76
        $this->applyDefaultAttributes();
77
    }
78
79
    private function generateConfigs()
80
    {
81
        foreach ($this->classes as $className => $options) {
82
            $this->applyConfigByObjectClassName($className);
83
        }
84
    }
85
86
    private function registerChangerScript()
87
    {
88
        $changerId = Html::getInputId($this->model, $this->class_attribute_name);
89
        $inputId = $this->inputOptions['id'];
90
91
        $this->view->registerJs("initObjectSelectorChanger('{$changerId}', '{$inputId}')");
92
    }
93
94
    private function applyConfigByObjectClassName($className)
95
    {
96
        $options = $this->classes[$className];
97
        if ($options['comboOptions']) {
98
            foreach ($this->requiredAttributes as $attribute) {
99
                if (isset($options['comboOptions'][$attribute->name])) {
100
                    $this->{$attribute->name} = $options['comboOptions'][$attribute->name];
101
                }
102
            }
103
        }
104
        $this->registerClientConfig();
105
        $varName = strtolower($this->model->formName()) . '_object_id_' . $className;
106
        $this->view->registerJsVar($varName, $this->configId, View::POS_END);
107
        $this->reset();
108
    }
109
110
    private function fillRequiredAttributes()
111
    {
112
        $this->requiredAttributes = array_filter((new ReflectionClass(get_class($this)))->getProperties(), function ($attr) {
113
            return $attr->class === get_class($this);
114
        });
115
    }
116
117
    private function applyDefaultAttributes()
118
    {
119
        $this->applyConfigByObjectClassName($this->class_attribute ?: 'client');
120
    }
121
122
    private function registerSpecialAssets()
123
    {
124
        // Fix validation styles
125
        $this->view->registerCss('
126
            .form-group.has-error .select2-selection { border-color: #dd4b39; box-shadow: none; }
127
            .form-group.has-success .select2-selection { border-color: #00a65a; box-shadow: none; }
128
            select.object-selector-select:not([data-select2-id]) { display: none; }
129
        ');
130
        $this->view->registerJs(<<<'JS'
131
            (function( $ ){
132
133
                var originalDynamicForm = $.fn.yiiDynamicForm;
134
                
135
                var methods = {
136
                    addItem : function(widgetOptions, e, elem) { 
137
                        originalDynamicForm('addItem', widgetOptions, e, elem);
138
                        var count = elem.closest('.' + widgetOptions.widgetContainer).find(widgetOptions.widgetItem).length;
139
140
                        // Reinit ObjectSelectorChanger after added new item
141
                        if (count < widgetOptions.limit) {
142
                            var objectSelectorInputs = $($newclone).find('[data-object-selector-field]');
143
                            objectSelectorInputs.each(function() {
144
                                var objectInputId = $(this).attr('id');
145
                                var changerInputId = $(this).prev('select').attr('id');
146
                                initObjectSelectorChanger(changerInputId, objectInputId);
147
                                $('#' + objectInputId).find('option').remove().end().val(null).trigger('change');
148
                            });
149
                        } 
150
                    }
151
                };
152
                
153
                $.fn.yiiDynamicForm = function(method) {
154
                    if (method === 'addItem') {
155
                        return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
156
                    }
157
                    originalDynamicForm.apply(this, arguments);
158
                }
159
                
160
            })(window.jQuery);
161
JS
162
        );
163
        $this->view->registerJs(<<<JS
164
            function initObjectSelectorChanger(changerInputId, objectInputId) {
165
                $('#' + changerInputId).change(function(e) {
166
                    var regexID = /^(.+?)([-\d-]{1,})(.+)$/i;
167
                    var matches = objectInputId.match(regexID);
168
                    var combo = $('#' + objectInputId).closest('form').combo();
169
                    if (combo) {
170
                        combo.register('#' + objectInputId, window[matches[1] + '_object_id_' + e.target.value]);
171
                        $('#' + objectInputId).val(null).trigger('change');
172
                    }
173
                });
174
            }
175
JS
176
        );
177
    }
178
179
    /**
180
     * Reset attributes which may remains from the previous combo-object which leads to incorrect JS configuration.
181
     */
182
    private function reset(): void
183
    {
184
        $attributes = [
185
            '_primaryFilter' => null,
186
        ];
187
        foreach ($attributes as $attribute => $defaultValue) {
188
            $this->$attribute = $defaultValue;
189
        }
190
    }
191
}
192