Passed
Push — develop ( 578d35...1edb03 )
by Nikolay
13:40 queued 12s
created

SaveRecord   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 319
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 52
eloc 170
c 1
b 0
f 0
dl 0
loc 319
rs 7.44

7 Methods

Rating   Name   Duplication   Size   Complexity  
A deleteMobileNumber() 0 15 2
B main() 0 85 9
A saveExternalPhones() 0 26 6
B saveForwardingRights() 0 31 9
B saveSip() 0 40 9
C saveExtension() 0 41 12
A saveUser() 0 24 5

How to fix   Complexity   

Complex Class

Complex classes like SaveRecord 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 SaveRecord, 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\Sip;
28
use MikoPBX\Common\Models\Users;
29
use MikoPBX\Common\Providers\MainDatabaseProvider;
30
use MikoPBX\Common\Providers\ModelsMetadataProvider;
31
use MikoPBX\PBXCoreREST\Lib\PBXApiResult;
32
use Phalcon\Di;
33
use Phalcon\Di\Injectable;
34
use Phalcon\Security\Random;
35
36
/**
37
 * Class SaveRecord
38
 * Provides methods to save extension records with associated entities.
39
 *
40
 * @package MikoPBX\PBXCoreREST\Lib\Extensions
41
 */
42
class SaveRecord extends Injectable
43
{
44
    /**
45
     * Saves a record with associated entities.
46
     *
47
     * @param array $data Data to be saved.
48
     * @return PBXApiResult Result of the save operation.
49
     */
50
    public static function main(array $data): PBXApiResult
51
    {
52
        // Initialize the result object
53
        $res = new PBXApiResult();
54
        $res->processor = __METHOD__;
55
        $res->success = true;
56
57
        $di = Di::getDefault();
58
        $db = $di->get(MainDatabaseProvider::SERVICE_NAME);
59
        $db->begin();
60
61
        $dataStructure = new DataStructure($data);
62
63
        // Save user entity
64
        list($userEntity, $res->success) = self::saveUser($dataStructure);
65
        if (!$res->success) {
66
            // Handle errors and rollback
67
            $res->messages['error'][] = $userEntity->getMessages();
68
            $db->rollback();
69
            return $res;
70
        } else {
71
            $dataStructure->user_id = $userEntity->id;
72
        }
73
74
        // Save extension entity
75
        list($extension, $res->success) = self::saveExtension($dataStructure, false);
76
        if (!$res->success) {
77
            // Handle errors and rollback
78
            $res->messages['error'][] = implode($extension->getMessages());
79
            $db->rollback();
80
            return $res;
81
        }
82
83
        // Save SIP entity
84
        list($sipEntity, $res->success) = self::saveSip($dataStructure);
85
        if (!$res->success) {
86
            // Handle errors and rollback
87
            $res->messages['error'][] = implode($sipEntity->getMessages());
88
            $db->rollback();
89
            return $res;
90
        }
91
92
        // Save forwarding rights entity
93
        list($fwdEntity, $res->success) = self::saveForwardingRights($dataStructure);
94
        if (!$res->success) {
95
            // Handle errors and rollback
96
            $res->messages['error'][] = implode($fwdEntity->getMessages());
97
            $db->rollback();
98
            return $res;
99
        }
100
101
        // Check mobile number presence and save related entities
102
        if (!empty($dataStructure->mobile_number)) {
103
104
            // Save mobile extension
105
            list($mobileExtension, $res->success) = self::saveExtension($dataStructure, true);
106
            if (!$res->success) {
107
                // Handle errors and rollback
108
                $res->messages['error'][] = implode($mobileExtension->getMessages());
109
                $db->rollback();
110
                return $res;
111
            }
112
113
            // Save ExternalPhones for mobile number
114
            list($externalPhone, $res->success) = self::saveExternalPhones($dataStructure);
115
            if (!$res->success) {
116
                // Handle errors and rollback
117
                $res->messages['error'][] = implode($externalPhone->getMessages());
118
                $db->rollback();
119
                return $res;
120
            }
121
        } else {
122
            // Delete mobile number if it was associated with the user
123
            list($deletedMobileNumber, $res->success) = self::deleteMobileNumber($userEntity);
124
            if (!$res->success) {
125
                $res->messages['error'][] = implode($deletedMobileNumber->getMessages());
126
                $db->rollback();
127
                return $res;
128
            }
129
        }
130
        $db->commit();
131
132
        $res = GetRecord::main($extension->id);
133
        $res->processor = __METHOD__;
134
        return $res;
135
    }
136
137
    /**
138
     * Save parameters to the Users table
139
     *
140
     * @param DataStructure $dataStructure The data structure containing the input data.
141
     * @return array An array containing the saved Users entity and the save result.
142
     */
143
    private static function saveUser(DataStructure $dataStructure): array
144
    {
145
        $userEntity = Users::findFirstById($dataStructure->user_id);
146
        if ($userEntity === null) {
147
            $userEntity = new Users();
148
        }
149
150
        // Fill in user parameters
151
        $metaData = Di::getDefault()->get(ModelsMetadataProvider::SERVICE_NAME);
152
        foreach ($metaData->getAttributes($userEntity) as $name) {
153
            switch ($name) {
154
                case 'language':
155
                    $userEntity->$name = PbxSettings::getValueByKey('PBXLanguage');
156
                    break;
157
                default:
158
                    $propertyKey = 'user_' . $name;
159
                    if (property_exists($dataStructure, $propertyKey)) {
160
                        $userEntity->$name = $dataStructure->$propertyKey;
161
                    }
162
            }
163
        }
164
165
        $result = $userEntity->save();
166
        return [$userEntity, $result];
167
    }
168
169
    /**
170
     * Save the extension for a user.
171
     * @param DataStructure $dataStructure The data structure containing the input data.
172
     * @param bool $isMobile Flag indicating if it's a mobile extension.
173
     *
174
     * @return array An array containing the saved Extensions entity and the save result.
175
     */
176
    private static function saveExtension(DataStructure $dataStructure, bool $isMobile = false): array
177
    {
178
        $parameters['conditions'] = 'type=:type: AND is_general_user_number = "1" AND userid=:userid:';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$parameters was never initialized. Although not strictly required by PHP, it is generally a good practice to add $parameters = array(); before regardless.
Loading history...
179
        $parameters['bind']['type'] = $isMobile ? Extensions::TYPE_EXTERNAL : Extensions::TYPE_SIP;
180
        $parameters['bind']['userid'] = $dataStructure->user_id ;
181
182
        $extension = Extensions::findFirst($parameters);
183
        if ($extension === null) {
184
            $extension = new Extensions();
185
        }
186
        $metaData = Di::getDefault()->get(ModelsMetadataProvider::SERVICE_NAME);
187
        foreach ($metaData->getAttributes($extension) as $name) {
188
            switch ($name) {
189
                case 'id':
190
                    // Skip saving the 'id' field
191
                    break;
192
                case 'type':
193
                    // Set the 'type' based on the value of $isMobile
194
                    $extension->$name = $isMobile ? Extensions::TYPE_EXTERNAL : Extensions::TYPE_SIP;
195
                    break;
196
                case 'callerid':
197
                    // Sanitize the caller ID based on 'user_username' on model before save function
198
                    $extension->$name = $dataStructure->user_username;
199
                    break;
200
                case 'userid':
201
                    // Set 'userid' to the ID of the user entity
202
                    $extension->$name = $dataStructure->user_id;
203
                    break;
204
                case 'number':
205
                    // Set 'number' based on the value of mobile_number or number
206
                    $extension->$name = $isMobile ? $dataStructure->mobile_number : $dataStructure->number;
207
                    break;
208
                default:
209
                    if (property_exists($dataStructure, $name)) {
210
                        // Set other fields based on the values in $data
211
                        $extension->$name = $dataStructure->$name;
212
                    }
213
            }
214
        }
215
        $result = $extension->save();
216
        return [$extension, $result];
217
    }
218
219
    /**
220
     * Save the SIP entity with the provided data.
221
     *
222
     * @param DataStructure $dataStructure The data structure containing the input data.
223
     * @return array An array containing the saved SIP entity and the save result.
224
     */
225
    private static function saveSip(DataStructure $dataStructure): array
226
    {
227
        $sipEntity = SIP::findFirstByUniqid($dataStructure->sip_uniqid);
228
        if ($sipEntity === null) {
229
            $sipEntity = new SIP();
230
        }
231
232
        $metaData = Di::getDefault()->get(ModelsMetadataProvider::SERVICE_NAME);
233
        foreach ($metaData->getAttributes($sipEntity) as $name) {
234
            switch ($name) {
235
                case 'networkfilterid':
236
                    if ($dataStructure->sip_networkfilterid === 'none') {
237
                        $sipEntity->$name = null;
238
                    } else {
239
                        $sipEntity->$name = $dataStructure->sip_networkfilterid;
240
                    }
241
                    break;
242
                case 'extension':
243
                    // Set 'extension' based on the value of number
244
                    $sipEntity->$name = $dataStructure->number;
245
                    break;
246
                case 'description':
247
                    // Set 'description' based on the value of user_username
248
                    $sipEntity->$name = $dataStructure->user_username;
249
                    break;
250
                case 'manualattributes':
251
                    // Set 'manualattributes' using the value of sip_manualattributes
252
                    $sipEntity->setManualAttributes($dataStructure->sip_manualattributes);
253
                    break;
254
                default:
255
                    $propertyKey = 'sip_' . $name;
256
                    if (property_exists($dataStructure, $propertyKey)) {
257
                        // Set other fields based on the other fields in $dataStructure
258
                        $sipEntity->$name = $dataStructure->$propertyKey;
259
                    }
260
            }
261
        }
262
263
        $result = $sipEntity->save();
264
        return [$sipEntity, $result];
265
    }
266
267
    /**
268
     * Save the ExtensionForwardingRights entity with the provided data.
269
     *
270
     * @param DataStructure $dataStructure The data structure containing the input data.
271
     * @return array An array containing the saved ExtensionForwardingRights entity and the save result.
272
     */
273
    private static function saveForwardingRights(DataStructure $dataStructure): array
274
    {
275
        $forwardingRight = ExtensionForwardingRights::findFirstByExtension($dataStructure->number);
276
        if ($forwardingRight === null) {
277
            $forwardingRight = new ExtensionForwardingRights();
278
        }
279
        $metaData = Di::getDefault()->get(ModelsMetadataProvider::SERVICE_NAME);
280
        foreach ($metaData->getAttributes($forwardingRight) as $name) {
281
            switch ($name) {
282
                case 'extension':
283
                    // Set 'extension' based on the value of number
284
                    $forwardingRight->$name = $dataStructure->number;
285
                    break;
286
                case 'ringlength':
287
                    $forwardingRight->ringlength = '';
0 ignored issues
show
Documentation Bug introduced by
It seems like '' of type string is incompatible with the declared type integer|null of property $ringlength.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
288
                    if (!empty($dataStructure->fwd_ringlength)) {
289
                        $forwardingRight->ringlength = $dataStructure->fwd_ringlength;
290
                    } elseif (!empty($forwardingRight->fwd_forwarding)) {
0 ignored issues
show
Bug Best Practice introduced by
The property fwd_forwarding does not exist on MikoPBX\Common\Models\ExtensionForwardingRights. Since you implemented __get, consider adding a @property annotation.
Loading history...
291
                        $forwardingRight->ringlength = '45';
292
                    }
293
                    break;
294
                default:
295
                    $propertyKey = 'fwd_' . $name;
296
                    if (property_exists($dataStructure, $propertyKey)) {
297
                        // Set other fields based on the other fields in $dataStructure
298
                        $forwardingRight->$name = $dataStructure->$propertyKey === -1 ? '' : $dataStructure->$propertyKey;
299
                    }
300
            }
301
        }
302
        $result = $forwardingRight->save();
303
        return [$forwardingRight, $result];
304
    }
305
306
    /**
307
     * Save parameters to the ExternalPhones table for a mobile number.
308
     *
309
     * @param DataStructure $dataStructure The data structure containing the input data.
310
     * @return array An array containing the saved ExternalPhones entity and the save result.
311
     */
312
    private static function saveExternalPhones(DataStructure $dataStructure): array
313
    {
314
        $externalPhone = ExternalPhones::findFirstByUniqid($dataStructure->mobile_uniqid);
315
        if ($externalPhone === null) {
316
            $externalPhone = new ExternalPhones();
317
        }
318
        $metaData = Di::getDefault()->get(ModelsMetadataProvider::SERVICE_NAME);
319
        foreach ($metaData->getAttributes($externalPhone) as $name) {
320
            switch ($name) {
321
                case 'extension':
322
                    $externalPhone->$name = $dataStructure->mobile_number;
323
                    break;
324
                case 'description':
325
                    $externalPhone->$name = $dataStructure->user_username;
326
                    break;
327
                default:
328
                    $propertyKey = 'mobile_' . $name;
329
                    if (property_exists($dataStructure, $propertyKey)) {
330
                        // Set other fields based on the other fields in $dataStructure
331
                        $externalPhone->$name = $dataStructure->$propertyKey;
332
                    }
333
            }
334
        }
335
336
        $result = $externalPhone->save();
337
        return [$externalPhone, $result];
338
    }
339
340
    /**
341
     * Delete a mobile number associated with a user.
342
     *
343
     * @param Users $userEntity The user entity.
344
     * @return array An array containing the deleted mobile number entity and the deletion result.
345
     */
346
    private static function deleteMobileNumber(Users $userEntity): array
347
    {
348
        $parameters = [
349
            'conditions' => 'type="' . Extensions::TYPE_EXTERNAL . '" AND is_general_user_number = "1" AND userid=:userid:',
350
            'bind' => [
351
                'userid' => $userEntity->id,
352
            ],
353
        ];
354
        $deletedMobileNumber = Extensions::findFirst($parameters);
355
        $result = true;
356
        if ($deletedMobileNumber !== null) {
357
            // Delete the mobile number entity if found
358
            $result = $deletedMobileNumber->delete();
359
        }
360
        return [$deletedMobileNumber, $result];
361
    }
362
}