Passed
Push — develop ( ab926b...704199 )
by Nikolay
13:25 queued 08:47
created

SaveRecordAction   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 358
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 57
eloc 184
dl 0
loc 358
rs 5.04
c 1
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A saveUser() 0 24 5
B main() 0 85 9
B saveSip() 0 43 10
A deleteMobileNumber() 0 15 2
B saveForwardingRights() 0 31 9
A saveExternalPhones() 0 26 6
A sanitizeCallerId() 0 3 1
C saveExtension() 0 46 13
A generateSearchIndex() 0 11 2

How to fix   Complexity   

Complex Class

Complex classes like SaveRecordAction 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 SaveRecordAction, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright © 2017-2023 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\PBXCoreREST\Lib\Extensions;
21
22
use MikoPBX\Common\Handlers\CriticalErrorsHandler;
23
use MikoPBX\Common\Models\ExtensionForwardingRights;
24
use MikoPBX\Common\Models\Extensions;
25
use MikoPBX\Common\Models\ExternalPhones;
26
use MikoPBX\Common\Models\PbxSettings;
27
use MikoPBX\Common\Models\PbxSettingsConstants;
28
use MikoPBX\Common\Models\Sip;
29
use MikoPBX\Common\Models\Users;
30
use MikoPBX\Common\Providers\MainDatabaseProvider;
31
use MikoPBX\Common\Providers\ModelsMetadataProvider;
32
use MikoPBX\PBXCoreREST\Lib\PBXApiResult;
33
use Phalcon\Di;
34
use Phalcon\Di\Injectable;
35
use Phalcon\Security\Random;
36
37
/**
38
 * Class SaveRecord
39
 * Provides methods to save extension records with associated entities.
40
 *
41
 * @package MikoPBX\PBXCoreREST\Lib\Extensions
42
 */
43
class SaveRecordAction extends Injectable
44
{
45
    /**
46
     * Saves a record with associated entities.
47
     *
48
     * @param array $data Data to be saved.
49
     * @return PBXApiResult Result of the save operation.
50
     */
51
    public static function main(array $data): PBXApiResult
52
    {
53
        // Initialize the result object
54
        $res = new PBXApiResult();
55
        $res->processor = __METHOD__;
56
        $res->success = true;
57
58
        $di = Di::getDefault();
59
        $db = $di->get(MainDatabaseProvider::SERVICE_NAME);
60
        $db->begin();
61
62
        $dataStructure = new DataStructure($data);
63
64
        // Save user entity
65
        list($userEntity, $res->success) = self::saveUser($dataStructure);
66
        if (!$res->success) {
67
            // Handle errors and rollback
68
            $res->messages['error'][] = $userEntity->getMessages();
69
            $db->rollback();
70
            return $res;
71
        } else {
72
            $dataStructure->user_id = $userEntity->id;
73
        }
74
75
        // Save extension entity
76
        list($extension, $res->success) = self::saveExtension($dataStructure, false);
77
        if (!$res->success) {
78
            // Handle errors and rollback
79
            $res->messages['error'][] = implode($extension->getMessages());
80
            $db->rollback();
81
            return $res;
82
        }
83
84
        // Save SIP entity
85
        list($sipEntity, $res->success) = self::saveSip($dataStructure);
86
        if (!$res->success) {
87
            // Handle errors and rollback
88
            $res->messages['error'][] = implode($sipEntity->getMessages());
89
            $db->rollback();
90
            return $res;
91
        }
92
93
        // Save forwarding rights entity
94
        list($fwdEntity, $res->success) = self::saveForwardingRights($dataStructure);
95
        if (!$res->success) {
96
            // Handle errors and rollback
97
            $res->messages['error'][] = implode($fwdEntity->getMessages());
98
            $db->rollback();
99
            return $res;
100
        }
101
102
        // Check mobile number presence and save related entities
103
        if (!empty($dataStructure->mobile_number)) {
104
105
            // Save mobile extension
106
            list($mobileExtension, $res->success) = self::saveExtension($dataStructure, true);
107
            if (!$res->success) {
108
                // Handle errors and rollback
109
                $res->messages['error'][] = implode($mobileExtension->getMessages());
110
                $db->rollback();
111
                return $res;
112
            }
113
114
            // Save ExternalPhones for mobile number
115
            list($externalPhone, $res->success) = self::saveExternalPhones($dataStructure);
116
            if (!$res->success) {
117
                // Handle errors and rollback
118
                $res->messages['error'][] = implode($externalPhone->getMessages());
119
                $db->rollback();
120
                return $res;
121
            }
122
        } else {
123
            // Delete mobile number if it was associated with the user
124
            list($deletedMobileNumber, $res->success) = self::deleteMobileNumber($userEntity);
125
            if (!$res->success) {
126
                $res->messages['error'][] = implode($deletedMobileNumber->getMessages());
127
                $db->rollback();
128
                return $res;
129
            }
130
        }
131
        $db->commit();
132
133
        $res = GetRecordAction::main($extension->id);
134
        $res->processor = __METHOD__;
135
        return $res;
136
    }
137
138
    /**
139
     * Save parameters to the Users table
140
     *
141
     * @param DataStructure $dataStructure The data structure containing the input data.
142
     * @return array An array containing the saved Users entity and the save result.
143
     */
144
    private static function saveUser(DataStructure $dataStructure): array
145
    {
146
        $userEntity = Users::findFirstById($dataStructure->user_id);
147
        if ($userEntity === null) {
148
            $userEntity = new Users();
149
        }
150
151
        // Fill in user parameters
152
        $metaData = Di::getDefault()->get(ModelsMetadataProvider::SERVICE_NAME);
153
        foreach ($metaData->getAttributes($userEntity) as $name) {
154
            switch ($name) {
155
                case 'language':
156
                    $userEntity->$name = PbxSettings::getValueByKey(PbxSettingsConstants::PBX_LANGUAGE);
157
                    break;
158
                default:
159
                    $propertyKey = 'user_' . $name;
160
                    if (property_exists($dataStructure, $propertyKey)) {
161
                        $userEntity->$name = $dataStructure->$propertyKey;
162
                    }
163
            }
164
        }
165
166
        $result = $userEntity->save();
167
        return [$userEntity, $result];
168
    }
169
170
    /**
171
     * Save the extension for a user.
172
     * @param DataStructure $dataStructure The data structure containing the input data.
173
     * @param bool $isMobile Flag indicating if it's a mobile extension.
174
     *
175
     * @return array An array containing the saved Extensions entity and the save result.
176
     */
177
    private static function saveExtension(DataStructure $dataStructure, bool $isMobile = false): array
178
    {
179
        $parameters = [];
180
        $parameters['conditions'] = 'type=:type: AND is_general_user_number = "1" AND userid=:userid:';
181
        $parameters['bind']['type'] = $isMobile ? Extensions::TYPE_EXTERNAL : Extensions::TYPE_SIP;
182
        $parameters['bind']['userid'] = $dataStructure->user_id ;
183
184
        $extension = Extensions::findFirst($parameters);
185
        if ($extension === null) {
186
            $extension = new Extensions();
187
        }
188
        $metaData = Di::getDefault()->get(ModelsMetadataProvider::SERVICE_NAME);
189
        foreach ($metaData->getAttributes($extension) as $name) {
190
            switch ($name) {
191
                case 'id':
192
                    // Skip saving the 'id' field
193
                    break;
194
                case 'type':
195
                    // Set the 'type' based on the value of $isMobile
196
                    $extension->$name = $isMobile ? Extensions::TYPE_EXTERNAL : Extensions::TYPE_SIP;
197
                    break;
198
                case 'callerid':
199
                    // Sanitize the caller ID based on 'user_username'
200
                    $extension->$name = self::sanitizeCallerId( $dataStructure->user_username);
201
                    break;
202
                case 'userid':
203
                    // Set 'userid' to the ID of the user entity
204
                    $extension->$name = $dataStructure->user_id;
205
                    break;
206
                case 'number':
207
                    // Set 'number' based on the value of mobile_number or number
208
                    $extension->$name = $isMobile ? $dataStructure->mobile_number : $dataStructure->number;
209
                    break;
210
                case 'search-index':
211
                    // Generate search index for the extension
212
                    $extension->$name = self::generateSearchIndex($dataStructure, $isMobile);
213
                    break;
214
                default:
215
                    if (property_exists($dataStructure, $name)) {
216
                        // Set other fields based on the values in $data
217
                        $extension->$name = $dataStructure->$name;
218
                    }
219
            }
220
        }
221
        $result = $extension->save();
222
        return [$extension, $result];
223
    }
224
225
    /**
226
     * Save the SIP entity with the provided data.
227
     *
228
     * @param DataStructure $dataStructure The data structure containing the input data.
229
     * @return array An array containing the saved SIP entity and the save result.
230
     */
231
    private static function saveSip(DataStructure $dataStructure): array
232
    {
233
        $sipEntity = SIP::findFirstByUniqid($dataStructure->sip_uniqid);
234
        if ($sipEntity === null) {
235
            $sipEntity = new SIP();
236
        }
237
238
        $metaData = Di::getDefault()->get(ModelsMetadataProvider::SERVICE_NAME);
239
        foreach ($metaData->getAttributes($sipEntity) as $name) {
240
            switch ($name) {
241
                case 'weakSecret':
242
                    $sipEntity->$name = '0';
243
                    break;
244
                case 'networkfilterid':
245
                    if ($dataStructure->sip_networkfilterid === 'none') {
246
                        $sipEntity->$name = null;
247
                    } else {
248
                        $sipEntity->$name = $dataStructure->sip_networkfilterid;
249
                    }
250
                    break;
251
                case 'extension':
252
                    // Set 'extension' based on the value of number
253
                    $sipEntity->$name = $dataStructure->number;
254
                    break;
255
                case 'description':
256
                    // Set 'description' based on the value of user_username
257
                    $sipEntity->$name = $dataStructure->user_username;
258
                    break;
259
                case 'manualattributes':
260
                    // Set 'manualattributes' using the value of sip_manualattributes
261
                    $sipEntity->setManualAttributes($dataStructure->sip_manualattributes);
262
                    break;
263
                default:
264
                    $propertyKey = 'sip_' . $name;
265
                    if (property_exists($dataStructure, $propertyKey)) {
266
                        // Set other fields based on the other fields in $dataStructure
267
                        $sipEntity->$name = $dataStructure->$propertyKey;
268
                    }
269
            }
270
        }
271
272
        $result = $sipEntity->save();
273
        return [$sipEntity, $result];
274
    }
275
276
    /**
277
     * Save the ExtensionForwardingRights entity with the provided data.
278
     *
279
     * @param DataStructure $dataStructure The data structure containing the input data.
280
     * @return array An array containing the saved ExtensionForwardingRights entity and the save result.
281
     */
282
    private static function saveForwardingRights(DataStructure $dataStructure): array
283
    {
284
        $forwardingRight = ExtensionForwardingRights::findFirstByExtension($dataStructure->number);
285
        if ($forwardingRight === null) {
286
            $forwardingRight = new ExtensionForwardingRights();
287
        }
288
        $metaData = Di::getDefault()->get(ModelsMetadataProvider::SERVICE_NAME);
289
        foreach ($metaData->getAttributes($forwardingRight) as $name) {
290
            switch ($name) {
291
                case 'extension':
292
                    // Set 'extension' based on the value of number
293
                    $forwardingRight->$name = $dataStructure->number;
294
                    break;
295
                case 'ringlength':
296
                    $forwardingRight->ringlength = 0;
297
                    if (!empty($dataStructure->fwd_ringlength)) {
298
                        $forwardingRight->ringlength = $dataStructure->fwd_ringlength;
299
                    } elseif (!empty($dataStructure->fwd_forwarding)) {
300
                        $forwardingRight->ringlength = 45;
301
                    }
302
                    break;
303
                default:
304
                    $propertyKey = 'fwd_' . $name;
305
                    if (property_exists($dataStructure, $propertyKey)) {
306
                        // Set other fields based on the other fields in $dataStructure
307
                        $forwardingRight->$name = $dataStructure->$propertyKey === -1 ? '' : $dataStructure->$propertyKey;
308
                    }
309
            }
310
        }
311
        $result = $forwardingRight->save();
312
        return [$forwardingRight, $result];
313
    }
314
315
    /**
316
     * Save parameters to the ExternalPhones table for a mobile number.
317
     *
318
     * @param DataStructure $dataStructure The data structure containing the input data.
319
     * @return array An array containing the saved ExternalPhones entity and the save result.
320
     */
321
    private static function saveExternalPhones(DataStructure $dataStructure): array
322
    {
323
        $externalPhone = ExternalPhones::findFirstByUniqid($dataStructure->mobile_uniqid);
324
        if ($externalPhone === null) {
325
            $externalPhone = new ExternalPhones();
326
        }
327
        $metaData = Di::getDefault()->get(ModelsMetadataProvider::SERVICE_NAME);
328
        foreach ($metaData->getAttributes($externalPhone) as $name) {
329
            switch ($name) {
330
                case 'extension':
331
                    $externalPhone->$name = $dataStructure->mobile_number;
332
                    break;
333
                case 'description':
334
                    $externalPhone->$name = $dataStructure->user_username;
335
                    break;
336
                default:
337
                    $propertyKey = 'mobile_' . $name;
338
                    if (property_exists($dataStructure, $propertyKey)) {
339
                        // Set other fields based on the other fields in $dataStructure
340
                        $externalPhone->$name = $dataStructure->$propertyKey;
341
                    }
342
            }
343
        }
344
345
        $result = $externalPhone->save();
346
        return [$externalPhone, $result];
347
    }
348
349
    /**
350
     * Delete a mobile number associated with a user.
351
     *
352
     * @param Users $userEntity The user entity.
353
     * @return array An array containing the deleted mobile number entity and the deletion result.
354
     */
355
    private static function deleteMobileNumber(Users $userEntity): array
356
    {
357
        $parameters = [
358
            'conditions' => 'type="' . Extensions::TYPE_EXTERNAL . '" AND is_general_user_number = "1" AND userid=:userid:',
359
            'bind' => [
360
                'userid' => $userEntity->id,
361
            ],
362
        ];
363
        $deletedMobileNumber = Extensions::findFirst($parameters);
364
        $result = true;
365
        if ($deletedMobileNumber !== null) {
366
            // Delete the mobile number entity if found
367
            $result = $deletedMobileNumber->delete();
368
        }
369
        return [$deletedMobileNumber, $result];
370
    }
371
372
    /**
373
     * Generate a search index for the extension.
374
     *
375
     * @param DataStructure $dataStructure The data structure containing the input data.
376
     * @param bool $isMobile Flag indicating if it's a mobile extension.
377
     * @return string The generated search index.
378
     */
379
    private static function generateSearchIndex(DataStructure $dataStructure, bool $isMobile = false): string
380
    {
381
        // Collect data for the search index
382
        $username = mb_strtolower($dataStructure->user_username);
383
        $callerId = mb_strtolower(self::sanitizeCallerId($dataStructure->user_username));
384
        $email = mb_strtolower($dataStructure->user_email);
385
        $internalNumber = mb_strtolower($dataStructure->number);
386
        $mobileNumber = $isMobile ? mb_strtolower($dataStructure->mobile_number) : '';
387
388
        // Combine all fields into a single string
389
        return $username . ' ' . $callerId . ' ' . $email . ' ' . $internalNumber . ' ' . $mobileNumber;
390
    }
391
392
    /**
393
     * Sanitize the caller ID by removing non-alphanumeric characters.
394
     *
395
     * @param string $callerId
396
     * @return string
397
     */
398
    private static function sanitizeCallerId(string $callerId): string
399
    {
400
        return preg_replace('/[^a-zA-Zа-яА-Я0-9 ]/ui', '', $callerId);
401
    }
402
}