Total Complexity | 60 |
Total Lines | 405 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like AutoAssign often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AutoAssign, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
21 | class AutoAssign extends Base |
||
22 | { |
||
23 | /** @var string Basic table name */ |
||
24 | public const TABLE_NAME = 's_#__auto_assign'; |
||
25 | /** @var array Members tables */ |
||
26 | public const MEMBERS_TABLES = ['s_#__auto_assign_users' => 'id', 's_#__auto_assign_groups' => 'id', 's_#__auto_assign_roles' => 'id']; |
||
27 | /** @var string Round robin table name */ |
||
28 | public const ROUND_ROBIN_TABLE = 'u_#__auto_assign_rr'; |
||
29 | |||
30 | /** @var int Status inactive */ |
||
31 | public const STATUS_INACTIVE = 0; |
||
32 | /** @var int Status active */ |
||
33 | public const STATUS_ACTIVE = 1; |
||
34 | |||
35 | /** @var int Manual mode */ |
||
36 | public const MODE_MANUAL = 1; |
||
37 | /** @var int Handler mode */ |
||
38 | public const MODE_HANDLER = 2; |
||
39 | /** @var int Workflow mode */ |
||
40 | public const MODE_WORKFLOW = 4; |
||
41 | |||
42 | /** @var int Load balance method */ |
||
43 | public const METHOD_LOAD_BALANCE = 0; |
||
44 | /** @var int Round robin method */ |
||
45 | public const METHOD_ROUND_ROBIN = 1; |
||
46 | |||
47 | /** |
||
48 | * Get all auto assign entries for module. |
||
49 | * |
||
50 | * @param string $moduleName |
||
51 | * @param int $mode A bitmask of one or more of the mode flags |
||
52 | * @param int $state |
||
53 | * |
||
54 | * @return array |
||
55 | */ |
||
56 | public static function getByModule(string $moduleName, int $mode = self::MODE_HANDLER | self::MODE_WORKFLOW | self::MODE_MANUAL, int $state = self::STATUS_ACTIVE): array |
||
57 | { |
||
58 | $query = (new Db\Query())->from(self::TABLE_NAME) |
||
59 | ->where(['tabid' => Module::getModuleId($moduleName), 'state' => $state]); |
||
60 | $mods = ['or']; |
||
61 | foreach ([self::MODE_MANUAL => 'gui', self::MODE_HANDLER => 'handler', self::MODE_WORKFLOW => 'workflow'] as $key => $column) { |
||
62 | if ($mode & $key) { |
||
63 | $mods[] = [$column => 1]; |
||
64 | } |
||
65 | } |
||
66 | $query->andWhere($mods); |
||
67 | |||
68 | return $query->all(); |
||
69 | } |
||
70 | |||
71 | /** |
||
72 | * Get all auto assign instances for module. |
||
73 | * |
||
74 | * @param string $moduleName |
||
75 | * @param int|null $mode A bitmask of one or more of the mode flags |
||
76 | * |
||
77 | * @return array |
||
78 | */ |
||
79 | public static function getInstancesByModule(string $moduleName, int $mode = null): array |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * Get auto assign instance for record. |
||
90 | * |
||
91 | * @param \Vtiger_Record_Model $recordModel |
||
92 | * @param int|null $mode A bitmask of one or more of the mode flags |
||
93 | * |
||
94 | * @return self|null |
||
95 | */ |
||
96 | public static function getAutoAssignForRecord(\Vtiger_Record_Model $recordModel, int $mode = null): ?self |
||
97 | { |
||
98 | $autoAssignInstance = null; |
||
99 | if (!\App\YetiForce\Shop::check('YetiForceAutoAssignment')) { |
||
100 | return $autoAssignInstance; |
||
101 | } |
||
102 | foreach (self::getByModule($recordModel->getModuleName(), $mode) as $autoAssignData) { |
||
103 | $conditions = Json::isEmpty($autoAssignData['conditions']) ? [] : Json::decode($autoAssignData['conditions']); |
||
104 | if (Condition::checkConditions($conditions, $recordModel)) { |
||
105 | $autoAssignInstance = self::getInstance($autoAssignData); |
||
106 | break; |
||
107 | } |
||
108 | } |
||
109 | return $autoAssignInstance; |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * Get auto assign instance by ID. |
||
114 | * |
||
115 | * @param int $id |
||
116 | * |
||
117 | * @return self|null |
||
118 | */ |
||
119 | public static function getInstanceById(int $id): ?self |
||
120 | { |
||
121 | $data = (new Db\Query())->from(self::TABLE_NAME)->where(['id' => $id])->one(); |
||
122 | return $data ? (new self())->setData($data) : null; |
||
123 | } |
||
124 | |||
125 | /** |
||
126 | * Get auto assign instance by data. |
||
127 | * |
||
128 | * @param array $data |
||
129 | * |
||
130 | * @return self|null |
||
131 | */ |
||
132 | public static function getInstance(array $data): ?self |
||
133 | { |
||
134 | return $data ? (new self())->setData($data) : null; |
||
135 | } |
||
136 | |||
137 | /** |
||
138 | * Function to get the Id. |
||
139 | * |
||
140 | * @return int |
||
141 | */ |
||
142 | public function getId(): int |
||
143 | { |
||
144 | return $this->get('id'); |
||
|
|||
145 | } |
||
146 | |||
147 | /** |
||
148 | * Get name of auto assign instance. |
||
149 | * |
||
150 | * @param bool $encode |
||
151 | * |
||
152 | * @return string |
||
153 | */ |
||
154 | public function getName(bool $encode = true): string |
||
155 | { |
||
156 | return Language::translate($this->get('subject'), 'Settings:AutomaticAssignment', false, $encode); |
||
157 | } |
||
158 | |||
159 | /** |
||
160 | * Get module name. |
||
161 | * |
||
162 | * @return string |
||
163 | */ |
||
164 | public function getModuleName(): string |
||
165 | { |
||
166 | return Module::getModuleName($this->get('tabid')); |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * Check conditions for record. |
||
171 | * |
||
172 | * @param \Vtiger_Record_Model $recordModel |
||
173 | * |
||
174 | * @return bool |
||
175 | */ |
||
176 | public function checkConditionForRecord(\Vtiger_Record_Model $recordModel): bool |
||
177 | { |
||
178 | $conditions = Json::isEmpty($this->get('conditions')) ? [] : Json::decode($this->get('conditions')); |
||
179 | return Condition::checkConditions($conditions, $recordModel); |
||
180 | } |
||
181 | |||
182 | /** |
||
183 | * Check if the instance is active in a given mode. |
||
184 | * |
||
185 | * @param int $mode |
||
186 | * |
||
187 | * @return bool |
||
188 | */ |
||
189 | public function isActive(int $mode): bool |
||
190 | { |
||
191 | switch ($mode) { |
||
192 | case self::MODE_MANUAL: |
||
193 | $result = !$this->isEmpty('gui'); |
||
194 | break; |
||
195 | case self::MODE_HANDLER: |
||
196 | $result = !$this->isEmpty('handler'); |
||
197 | break; |
||
198 | case self::MODE_WORKFLOW: |
||
199 | $result = !$this->isEmpty('workflow'); |
||
200 | break; |
||
201 | default: |
||
202 | $result = false; |
||
203 | break; |
||
204 | } |
||
205 | return $result && self::STATUS_ACTIVE === (int) $this->get('state'); |
||
206 | } |
||
207 | |||
208 | /** |
||
209 | * Get an automatic selected user ID. |
||
210 | * |
||
211 | * @return int |
||
212 | */ |
||
213 | public function getOwner(): ?int |
||
214 | { |
||
215 | switch ($this->get('method')) { |
||
216 | case self::METHOD_LOAD_BALANCE: |
||
217 | $owner = $this->getQueryByLoadBalance()->scalar() ?: null; |
||
218 | break; |
||
219 | case self::METHOD_ROUND_ROBIN: |
||
220 | $owner = $this->getQueryByRoundRobin()->scalar() ?: null; |
||
221 | break; |
||
222 | default: |
||
223 | $owner = null; |
||
224 | break; |
||
225 | } |
||
226 | |||
227 | return $owner ? $owner : $this->getDefaultOwner(); |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * Get automatic selected users. |
||
232 | * |
||
233 | * @return array |
||
234 | */ |
||
235 | public function getOwners(): array |
||
236 | { |
||
237 | switch ($this->get('method')) { |
||
238 | case self::METHOD_LOAD_BALANCE: |
||
239 | $owner = $this->getQueryByLoadBalance()->all(); |
||
240 | break; |
||
241 | case self::METHOD_ROUND_ROBIN: |
||
242 | $owner = $this->getQueryByRoundRobin()->all(); |
||
243 | break; |
||
244 | default: |
||
245 | $owner = []; |
||
246 | break; |
||
247 | } |
||
248 | |||
249 | return $owner; |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * Get default owner. |
||
254 | * |
||
255 | * @return int |
||
256 | */ |
||
257 | public function getDefaultOwner(): ?int |
||
258 | { |
||
259 | $owner = null; |
||
260 | $defaultOwner = (int) $this->get('default_assign'); |
||
261 | $ownerModel = Fields\Owner::getInstance($this->getModuleName()); |
||
262 | |||
263 | $type = $defaultOwner ? Fields\Owner::getType($defaultOwner) : null; |
||
264 | if ('Users' === $type) { |
||
265 | $owner = User::isExists($defaultOwner) ? $defaultOwner : $owner; |
||
266 | } elseif ($type) { |
||
267 | $owner = \array_key_exists($defaultOwner, $ownerModel->getAccessibleGroupForModule()) ? $defaultOwner : $owner; |
||
268 | } |
||
269 | |||
270 | return $owner; |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Query object for users allowed to be assigned by load balanced method. |
||
275 | * |
||
276 | * In order to correctly balance the entries attribution |
||
277 | * we need ot randomize the order in which they are returned. |
||
278 | * Otherwise, when multiple users have the same amount of entries |
||
279 | * it is always the first one in the results who will be assigned to new entry. |
||
280 | * |
||
281 | * @return Db\Query |
||
282 | */ |
||
283 | public function getQueryByLoadBalance(): Db\Query |
||
284 | { |
||
285 | return $this->getQuery()->orderBy(['count' => SORT_ASC, new \yii\db\Expression('RAND()')]); |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Query object for users allowed to be assigned by round robin. |
||
290 | * |
||
291 | * @return Db\Query |
||
292 | */ |
||
293 | public function getQueryByRoundRobin(): Db\Query |
||
294 | { |
||
295 | $robinTable = self::ROUND_ROBIN_TABLE; |
||
296 | $columnName = "{$robinTable}.datetime"; |
||
297 | $id = $this->getId(); |
||
298 | |||
299 | return $this->getQuery()->leftJoin($robinTable, "vtiger_users.id = {$robinTable}.user AND {$robinTable}.id={$id}") |
||
300 | ->addSelect([$columnName]) |
||
301 | ->addGroupBy($columnName) |
||
302 | ->orderBy([$columnName => SORT_ASC]); |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * Query object for users allowed for assignment. |
||
307 | * |
||
308 | * @return Db\Query |
||
309 | */ |
||
310 | public function getQuery(): Db\Query |
||
311 | { |
||
312 | $ownerFieldName = 'assigned_user_id'; |
||
313 | $queryGeneratorUsers = $this->getAvailableUsersQuery(); |
||
314 | |||
315 | $queryGenerator = (new QueryGenerator($this->getModuleName())); |
||
316 | $queryGenerator->permissions = false; |
||
317 | $conditions = Json::isEmpty($this->get('record_limit_conditions')) ? [] : Json::decode($this->get('record_limit_conditions')); |
||
318 | $queryGenerator->setFields([$ownerFieldName]) |
||
319 | ->setCustomColumn(['count' => new \yii\db\Expression('COUNT(*)')]) |
||
320 | ->setConditions($conditions) |
||
321 | ->setGroup($ownerFieldName) |
||
322 | ->addNativeCondition([$queryGenerator->getColumnName($ownerFieldName) => $queryGeneratorUsers->createQuery()]); |
||
323 | $subQuery = $queryGenerator->createQuery(); |
||
324 | |||
325 | $recordLimit = (int) $this->get('record_limit'); |
||
326 | if (0 === $recordLimit) { |
||
327 | $queryGeneratorUsers->setCustomColumn(['temp_limit' => $queryGeneratorUsers->getColumnName('records_limit')]); |
||
328 | } else { |
||
329 | $queryGeneratorUsers->setCustomColumn(['temp_limit' => new \yii\db\Expression($recordLimit)]); |
||
330 | } |
||
331 | $queryGeneratorUsers->setGroup('id')->setCustomGroup(['temp_limit', 'count']); |
||
332 | $query = $queryGeneratorUsers->createQuery(true); |
||
333 | $query->leftJoin(['crm_data_temp_table' => $subQuery], "crm_data_temp_table.{$ownerFieldName}={$queryGeneratorUsers->getColumnName('id')}"); |
||
334 | $query->addSelect(['crm_data_temp_table.count']); |
||
335 | $query->andHaving(['or', ['<', 'count', new \yii\db\Expression('temp_limit')], ['temp_limit' => 0], ['count' => null]]); |
||
336 | |||
337 | return $query; |
||
338 | } |
||
339 | |||
340 | /** |
||
341 | * Query generator object of available users. |
||
342 | * |
||
343 | * @return QueryGenerator |
||
344 | */ |
||
345 | public function getAvailableUsersQuery(): QueryGenerator |
||
380 | } |
||
381 | |||
382 | /** |
||
383 | * Get members. |
||
384 | * |
||
385 | * @return array |
||
386 | */ |
||
387 | public function getMembers(): array |
||
406 | } |
||
407 | |||
408 | /** |
||
409 | * Post process action. |
||
410 | * |
||
411 | * @param int $userId |
||
412 | * |
||
413 | * @return void |
||
414 | */ |
||
415 | public function postProcess(int $userId) |
||
426 | } |
||
427 | } |
||
428 | } |
||
429 | } |
||
430 |