1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace devgroup\JsTreeWidget\actions\AdjacencyList; |
4
|
|
|
|
5
|
|
|
use DevGroup\TagDependencyHelper\NamingHelper; |
6
|
|
|
use Yii; |
7
|
|
|
use yii\base\Action; |
8
|
|
|
use yii\base\InvalidConfigException; |
9
|
|
|
use yii\caching\TagDependency; |
10
|
|
|
use yii\helpers\ArrayHelper; |
11
|
|
|
use yii\helpers\FileHelper; |
12
|
|
|
use yii\web\Response; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Helper action for retrieving tree data for jstree by ajax. |
16
|
|
|
* Example use in controller: |
17
|
|
|
* |
18
|
|
|
* ``` php |
19
|
|
|
* public function actions() |
20
|
|
|
* { |
21
|
|
|
* return [ |
22
|
|
|
* 'getTree' => [ |
23
|
|
|
* 'class' => AdjacencyFullTreeDataAction::class, |
24
|
|
|
* 'className' => Category::class, |
25
|
|
|
* 'modelLabelAttribute' => 'defaultTranslation.name', |
26
|
|
|
* |
27
|
|
|
* ], |
28
|
|
|
* ... |
29
|
|
|
* ]; |
30
|
|
|
* } |
31
|
|
|
* ``` |
32
|
|
|
*/ |
33
|
|
|
class FullTreeDataAction extends Action |
34
|
|
|
{ |
35
|
|
|
|
36
|
|
|
public $className = null; |
37
|
|
|
|
38
|
|
|
public $modelIdAttribute = 'id'; |
39
|
|
|
|
40
|
|
|
public $modelLabelAttribute = 'name'; |
41
|
|
|
|
42
|
|
|
public $modelParentAttribute = 'parent_id'; |
43
|
|
|
|
44
|
|
|
public $varyByTypeAttribute = null; |
45
|
|
|
|
46
|
|
|
public $queryParentAttribute = 'id'; |
47
|
|
|
|
48
|
|
|
public $querySortOrder = 'sort_order'; |
49
|
|
|
|
50
|
|
|
public $querySelectedAttribute = 'selected_id'; |
51
|
|
|
/** |
52
|
|
|
* Additional conditions for retrieving tree(ie. don't display nodes marked as deleted) |
53
|
|
|
* @var array|\Closure |
54
|
|
|
*/ |
55
|
|
|
public $whereCondition = []; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Cache key prefix. Should be unique if you have multiple actions with different $whereCondition |
59
|
|
|
* @var string|\Closure |
60
|
|
|
*/ |
61
|
|
|
public $cacheKey = 'FullTree'; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Cache lifetime for the full tree |
65
|
|
|
* @var int |
66
|
|
|
*/ |
67
|
|
|
public $cacheLifeTime = 86400; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @var bool|int If int - means cache life time for plain json cache files, false |
71
|
|
|
*/ |
72
|
|
|
public $cacheJson = false; |
73
|
|
|
|
74
|
|
View Code Duplication |
public function init() |
|
|
|
|
75
|
|
|
{ |
76
|
|
|
if (!isset($this->className)) { |
77
|
|
|
throw new InvalidConfigException("Model name should be set in controller actions"); |
78
|
|
|
} |
79
|
|
|
if (!class_exists($this->className)) { |
80
|
|
|
throw new InvalidConfigException("Model class does not exists"); |
81
|
|
|
} |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
public function run() |
85
|
|
|
{ |
86
|
|
|
Yii::$app->response->format = Response::FORMAT_JSON; |
87
|
|
|
|
88
|
|
|
/** @var \yii\db\ActiveRecord $class */ |
89
|
|
|
$class = $this->className; |
90
|
|
|
|
91
|
|
View Code Duplication |
if (null === $current_selected_id = Yii::$app->request->get($this->querySelectedAttribute)) { |
|
|
|
|
92
|
|
|
$current_selected_id = Yii::$app->request->get($this->queryParentAttribute); |
93
|
|
|
} |
94
|
|
|
$cacheKey = $this->cacheKey instanceof \Closure ? call_user_func($this->cacheKey) : $this->cacheKey; |
95
|
|
|
$cacheKey = "AdjacencyFullTreeData:{$cacheKey}:{$class}:{$this->querySortOrder}"; |
96
|
|
|
|
97
|
|
|
$filename = ''; |
98
|
|
|
if ($this->cacheJson) { |
99
|
|
|
$cacheKey = md5($cacheKey); |
100
|
|
|
$filename = Yii::getAlias("@runtime/tree-cache/$cacheKey.json"); |
101
|
|
|
if (file_exists($filename) && (time() - filemtime($filename) < $this->cacheJson)) { |
102
|
|
|
Yii::$app->response->format = Response::FORMAT_RAW; |
103
|
|
|
header('Content-Type: application/json'); |
104
|
|
|
|
105
|
|
|
return file_get_contents($filename); |
106
|
|
|
} |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
|
110
|
|
|
Yii::beginProfile('Get tree'); |
111
|
|
|
if (false === $result = Yii::$app->cache->get($cacheKey)) { |
112
|
|
|
Yii::beginProfile('Build tree'); |
113
|
|
|
$query = $class::find() |
114
|
|
|
->orderBy([$this->querySortOrder => SORT_ASC]); |
115
|
|
|
|
116
|
|
View Code Duplication |
if ($this->whereCondition instanceof \Closure) { |
|
|
|
|
117
|
|
|
$query->where(call_user_func($this->whereCondition)); |
118
|
|
|
} elseif (count($this->whereCondition) > 0) { |
119
|
|
|
$query->where($this->whereCondition); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
if (null === $rows = $query->asArray()->all()) { |
123
|
|
|
return []; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
$result = []; |
127
|
|
|
|
128
|
|
View Code Duplication |
foreach ($rows as $row) { |
|
|
|
|
129
|
|
|
$parent = ArrayHelper::getValue($row, $this->modelParentAttribute, 0); |
130
|
|
|
$item = [ |
131
|
|
|
'id' => ArrayHelper::getValue($row, $this->modelIdAttribute, 0), |
132
|
|
|
'parent' => ($parent) ? $parent : '#', |
133
|
|
|
'text' => ArrayHelper::getValue($row, $this->modelLabelAttribute, 'item'), |
134
|
|
|
'a_attr' => [ |
135
|
|
|
'data-id' => $row[$this->modelIdAttribute], |
136
|
|
|
'data-parent_id' => $row[$this->modelParentAttribute] |
137
|
|
|
], |
138
|
|
|
]; |
139
|
|
|
|
140
|
|
|
if (null !== $this->varyByTypeAttribute) { |
141
|
|
|
$item['type'] = $row[$this->varyByTypeAttribute]; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
$result[$row[$this->modelIdAttribute]] = $item; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
if ($this->cacheJson) { |
148
|
|
|
$encoded = json_encode(array_values($result)); |
149
|
|
|
FileHelper::createDirectory(basename($filename)); |
150
|
|
|
|
151
|
|
|
file_put_contents($filename, $encoded); |
152
|
|
|
|
153
|
|
|
Yii::$app->response->format = Response::FORMAT_RAW; |
154
|
|
|
header('Content-Type: application/json'); |
155
|
|
|
return $encoded; |
156
|
|
|
} |
157
|
|
|
Yii::$app->cache->set( |
158
|
|
|
$cacheKey, |
159
|
|
|
$result, |
160
|
|
|
86400, |
161
|
|
|
new TagDependency([ |
162
|
|
|
'tags' => [ |
163
|
|
|
NamingHelper::getCommonTag($class), |
164
|
|
|
], |
165
|
|
|
]) |
166
|
|
|
); |
167
|
|
|
|
168
|
|
|
Yii::endProfile('Build tree'); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
View Code Duplication |
if (array_key_exists($current_selected_id, $result)) { |
|
|
|
|
172
|
|
|
$result[$current_selected_id] = array_merge( |
173
|
|
|
$result[$current_selected_id], |
174
|
|
|
['state' => ['opened' => true, 'selected' => true]] |
175
|
|
|
); |
176
|
|
|
} |
177
|
|
|
Yii::endProfile('Get tree'); |
178
|
|
|
|
179
|
|
|
header('Content-Type: application/json'); |
180
|
|
|
echo json_encode(array_values($result)); |
181
|
|
|
Yii::$app->end(); |
182
|
|
|
return array_values($result); |
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.