Import   D
last analyzed

Complexity

Total Complexity 59

Size/Duplication

Total Lines 321
Duplicated Lines 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 179
dl 0
loc 321
rs 4.08
c 1
b 1
f 0
wmc 59

22 Methods

Rating   Name   Duplication   Size   Complexity  
A setStudentSubjects() 0 14 1
A array() 0 3 1
A checkKeys() 0 9 2
A map() 0 4 1
A validateColumnsToMap() 0 17 5
A __construct() 0 14 1
A validateColumns() 0 13 4
A batchSize() 0 16 4
A startRow() 0 3 1
A formateDate() 0 21 6
A headingRow() 0 3 1
A limit() 0 13 3
A updateSubjectCount() 0 7 1
A getUniqueOpenemisId() 0 3 1
A createOrUpdateGuardian() 0 9 4
A validateClass() 0 17 3
B mapFields() 0 31 8
A setGender() 0 13 3
A setRelation() 0 9 4
A getNode() 0 3 1
A insertSubject() 0 6 2
A insertOrUpdateSubjects() 0 13 2

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
namespace App\Imports;
4
5
use Exception;
6
use App\Models\User;
7
use Webpatser\Uuid\Uuid;
8
use App\Models\Nationality;
9
use App\Rules\admissionAge;
10
use App\Models\User_contact;
11
use Lsf\UniqueUid\UniqueUid;
12
use App\Models\Identity_type;
13
use App\Models\Security_user;
14
use App\Models\User_identity;
15
use App\Imports\StudentUpdate;
16
use App\Models\Import_mapping;
17
use App\Models\Security_group;
18
use App\Models\User_body_mass;
19
use App\Models\Academic_period;
20
use App\Models\Student_guardian;
21
use App\Models\User_nationality;
22
use App\Models\Institution_class;
23
use App\Models\User_special_need;
24
use App\Mail\StudentCountExceeded;
25
use App\Mail\StudentImportSuccess;
26
use Illuminate\Support\Facades\DB;
27
use App\Models\Area_administrative;
28
use App\Models\Institution_student;
29
use App\Models\Institution_subject;
30
use App\Models\Workflow_transition;
31
use Illuminate\Support\Facades\Log;
32
use Illuminate\Support\Facades\Mail;
33
use Illuminate\Support\Facades\Config;
34
use App\Models\Institution_class_grade;
35
use App\Models\Special_need_difficulty;
36
use Illuminate\Support\Facades\Request;
37
use Maatwebsite\Excel\Concerns\ToModel;
38
use App\Models\Education_grades_subject;
39
use App\Models\Institution_class_student;
40
use App\Models\Institution_class_subject;
41
use Illuminate\Support\Facades\Validator;
42
use Maatwebsite\Excel\Concerns\WithLimit;
43
use Maatwebsite\Excel\Events\AfterImport;
44
use Maatwebsite\Excel\Events\BeforeSheet;
45
use Maatwebsite\Excel\Validators\Failure;
46
use Maatwebsite\Excel\Concerns\Importable;
47
use Maatwebsite\Excel\Concerns\WithEvents;
48
use Maatwebsite\Excel\Events\BeforeImport;
49
use Maatwebsite\Excel\Jobs\AfterImportJob;
50
use App\Models\Institution_subject_student;
51
use Maatwebsite\Excel\Concerns\SkipsErrors;
52
use Maatwebsite\Excel\Concerns\WithMapping;
53
use Maatwebsite\Excel\Concerns\SkipsOnError;
54
use Maatwebsite\Excel\Concerns\WithStartRow;
55
use App\Models\Institution_student_admission;
56
use Maatwebsite\Excel\Concerns\SkipsFailures;
57
use Maatwebsite\Excel\Concerns\SkipsOnFailure;
58
use Maatwebsite\Excel\Concerns\WithHeadingRow;
59
use Maatwebsite\Excel\Concerns\WithValidation;
60
use Maatwebsite\Excel\Concerns\WithBatchInserts;
61
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
62
use Maatwebsite\Excel\Concerns\RegistersEventListeners;
63
use Maatwebsite\Excel\Exceptions\ConcernConflictException;
64
65
class Import
66
{
67
    //Parent class for import script
68
    use Importable,
0 ignored issues
show
introduced by
The trait Maatwebsite\Excel\Concerns\Importable requires some properties which are not provided by App\Imports\Import: $disk, $readerType, $filePath
Loading history...
69
        RegistersEventListeners;
70
71
    public function __construct($file)
72
    {
73
        $this->sheetNames = [];
0 ignored issues
show
Bug Best Practice introduced by
The property sheetNames does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
74
        $this->file = $file;
0 ignored issues
show
Bug Best Practice introduced by
The property file does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
75
        $this->sheetData = [];
0 ignored issues
show
Bug Best Practice introduced by
The property sheetData does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
76
        $this->template = false;
0 ignored issues
show
Bug Best Practice introduced by
The property template does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
77
        $this->worksheet = '';
0 ignored issues
show
Bug Best Practice introduced by
The property worksheet does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
78
        $this->failures = [];
0 ignored issues
show
Bug Best Practice introduced by
The property failures does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
79
        $this->request = new Request;
0 ignored issues
show
Bug Best Practice introduced by
The property request does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
80
        $this->maleStudentsCount = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property maleStudentsCount does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
81
        $this->femaleStudentsCount = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property femaleStudentsCount does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
82
        $this->highestRow = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property highestRow does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
83
        $this->isValidSheet = true;
0 ignored issues
show
Bug Best Practice introduced by
The property isValidSheet does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
84
        $this->uniqueUid = new UniqueUid();
0 ignored issues
show
Bug Best Practice introduced by
The property uniqueUid does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
85
    }
86
87
    public function limit(): int
88
    {
89
        $highestColumn = $this->worksheet->getHighestDataColumn(3);
90
        $higestRow = 0;
91
        for ($row = $this->startRow(); $row <= $this->highestRow; $row++) {
92
            $rowData = $this->worksheet->rangeToArray('A' . $row . ':' . $highestColumn . $row, NULL, TRUE, FALSE);
93
            if (isEmptyRow(reset($rowData))) {
94
                continue;
95
            } else {
96
                $higestRow += 1;
97
            }
98
        }
99
        return $higestRow;
100
    }
101
102
    public function validateColumns($column, $existingColumns)
0 ignored issues
show
Unused Code introduced by
The parameter $existingColumns is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

102
    public function validateColumns($column, /** @scrutinizer ignore-unused */ $existingColumns)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
103
    {
104
        $columns = Config::get('excel.columns');
105
        $error = \Illuminate\Validation\ValidationException::withMessages([]);
106
        $this->failures = [];
0 ignored issues
show
Bug Best Practice introduced by
The property failures does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
107
        if (($column !== "") && (!in_array($column, $columns))) {
108
            $this->isValidSheet = false;
0 ignored issues
show
Bug Best Practice introduced by
The property isValidSheet does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
109
            $this->error[] = 'Unsupported column found ,remove:' . $column;
0 ignored issues
show
Bug Best Practice introduced by
The property error does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
110
            $this->failure = new Failure(3, 'remark', $this->error, [null]);
0 ignored issues
show
Bug Best Practice introduced by
The property failure does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
111
            $this->failures = new \Maatwebsite\Excel\Validators\ValidationException($error, [$this->failure]);
112
        }
113
        if (is_object($this->failures)) {
114
            throw $this->failures;
115
        }
116
    }
117
118
    public function validateColumnsToMap($existingColumns)
119
    {
120
        $columns = Config::get('excel.columns');
121
        $optional_columns = Config::get('excel.optional_columns');
122
        $columns = array_diff($columns, $optional_columns);
123
        $error = \Illuminate\Validation\ValidationException::withMessages([]);
124
        $this->failures = [];
0 ignored issues
show
Bug Best Practice introduced by
The property failures does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
125
        foreach ($columns as  $column) {
126
            if (($column !== "") && (!in_array($column, $existingColumns))) {
127
                $this->isValidSheet = false;
0 ignored issues
show
Bug Best Practice introduced by
The property isValidSheet does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
128
                $this->error[] = 'Missing Column :' . $column . ' Not found';
0 ignored issues
show
Bug Best Practice introduced by
The property error does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
129
                $this->failure = new Failure(3, 'remark', $this->error, [null]);
0 ignored issues
show
Bug Best Practice introduced by
The property failure does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
130
                $this->failures = new \Maatwebsite\Excel\Validators\ValidationException($error, [$this->failure]);
131
            }
132
        }
133
        if (is_object($this->failures)) {
134
            throw $this->failures;
135
        }
136
    }
137
138
139
140
    public function batchSize(): int
141
    {
142
        $highestColumn = $this->worksheet->getHighestDataColumn(3);
143
        $higestRow = 1;
144
        for ($row = $this->startRow(); $row <= $this->highestRow; $row++) {
145
            $rowData = $this->worksheet->rangeToArray('A' . $row . ':' . $highestColumn . $row, NULL, TRUE, FALSE);
146
            if (isEmptyRow(reset($rowData))) {
147
                continue;
148
            } else {
149
                $higestRow += 1;
150
            }
151
        }
152
        if ($higestRow == 0) {
0 ignored issues
show
introduced by
The condition $higestRow == 0 is always false.
Loading history...
153
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
154
        } else {
155
            return $higestRow;
156
        }
157
    }
158
159
160
    public function startRow(): int
161
    {
162
        return 3;
163
    }
164
165
    public function headingRow(): int
166
    {
167
        return 2;
168
    }
169
170
171
    protected function formateDate($row, $column, $format = 'Y-m-d')
172
    {
173
        try {
174
            if (!empty($row[$column]) && ($row[$column] !== null)) {
175
                switch (gettype($row[$column])) {
176
                    case 'string':
177
                        $row[$column] = preg_replace('/[^A-Za-z0-9\-]/', '-', $row[$column]);
178
                        $row[$column] = date($format, strtotime($row[$column])); //date($row[$column]);
179
                        $row[$column] =  \Carbon\Carbon::createFromFormat($format, $row[$column]);
180
                        break;
181
                    case 'double';
182
                        $row[$column] =  \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[$column]);
183
                        break;
184
                }
185
            }
186
            return $row;
187
        } catch (Exception $e) {
188
            $error = \Illuminate\Validation\ValidationException::withMessages([]);
189
            $failure = new Failure(3, 'remark', [0 => 'Template is not valid for upload, use the template given in the system ' . $row[$column] . ' Not a valid date formate'], [null]);
190
            $failures = [0 => $failure];
191
            throw new \Maatwebsite\Excel\Validators\ValidationException($error, $failures);
192
        }
193
    }
194
195
196
    protected function mapFields($row)
197
    {
198
199
        $keys = array_keys($row);
200
201
        $this->validateColumnsToMap($keys);
202
        array_walk($keys, array($this, 'validateColumns'));
203
        $row = $this->formateDate($row, 'date_of_birth_yyyy_mm_dd');
204
        $row = $this->formateDate($row, 'bmi_date_yyyy_mm_dd');
205
        $row = $this->formateDate($row, 'start_date_yyyy_mm_dd');
206
        $row = $this->formateDate($row, 'fathers_date_of_birth_yyyy_mm_dd');
207
        $row = $this->formateDate($row, 'mothers_date_of_birth_yyyy_mm_dd');
208
        $row = $this->formateDate($row, 'guardians_date_of_birth_yyyy_mm_dd');
209
210
        $row['admission_no'] =  str_pad($row['admission_no'], 4, '0', STR_PAD_LEFT);
211
212
        if (array_key_exists('identity_type', $row)) {
213
            if ($row['identity_type'] == 'BC' && (!empty($row['birth_divisional_secretariat'])) && ($row['identity_number'] !== null) && $row['date_of_birth_yyyy_mm_dd'] !== null) {
214
                $row['identity_number'] =  str_pad($row['identity_number'], 4, '0', STR_PAD_LEFT);
215
                // dd(($row['date_of_birth_yyyy_mm_dd']));
216
                $BirthDivision = Area_administrative::where('name', 'like', '%' . $row['birth_divisional_secretariat'] . '%')->where('area_administrative_level_id', '=', 5)->first();
217
                if ($BirthDivision !== null) {
218
                    $BirthArea = Area_administrative::where('name', 'like', '%' . $row['birth_registrar_office_as_in_birth_certificate'] . '%')
219
                        ->where('parent_id', '=', $BirthDivision->id)->first();
220
                    if ($BirthArea !== null) {
221
                        $row['identity_number'] = $BirthArea->id . '' . $row['identity_number'] . '' . substr($row['date_of_birth_yyyy_mm_dd']->format("yy"), -2) . '' . $row['date_of_birth_yyyy_mm_dd']->format("m");
222
                    }
223
                }
224
            }
225
        }
226
        return $row;
227
    }
228
229
    protected function checkKeys($key, $count, $row)
230
    {
231
        if (array_key_exists($key, $row)) {
232
            return true;
233
        } else {
234
            $error = \Illuminate\Validation\ValidationException::withMessages([]);
235
            $failure = new Failure($count, 'remark', [0 => 'Template is not valid for upload, use the template given in the system ' . $key, ' Is missing form the template'], [null]);
236
            $failures = [0 => $failure];
237
            new \Maatwebsite\Excel\Validators\ValidationException($error, $failures);
238
        };
239
    }
240
241
242
    public function array(array $array)
243
    {
244
        $this->sheetData[] = $array;
0 ignored issues
show
Bug Best Practice introduced by
The property sheetData does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
245
    }
246
247
    /**
248
     * @param mixed $row
249
     * @return array
250
     * @throws \Exception
251
     */
252
    public function map($row): array
253
    {
254
        $row = $this->mapFields($row);
255
        return $row;
256
    }
257
258
259
    public function validateClass()
260
    {
261
262
        $institutionClass = Institution_class::find($this->file['institution_class_id']);
263
        $totalMaleStudents = $institutionClass->total_male_students;
0 ignored issues
show
Bug introduced by
The property total_male_students does not seem to exist on App\Models\Institution_class. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
264
        $totalFemaleStudents = $institutionClass->total_female_students;
0 ignored issues
show
Bug introduced by
The property total_female_students does not seem to exist on App\Models\Institution_class. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
265
        $totalStudents = $totalMaleStudents + $totalFemaleStudents;
266
267
        $exceededStudents = ($totalStudents + $this->limit()) > $institutionClass->no_of_students ? true : false;
0 ignored issues
show
Bug introduced by
The property no_of_students does not seem to exist on App\Models\Institution_class. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
268
        if ($exceededStudents == true) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
269
            $error = \Illuminate\Validation\ValidationException::withMessages([]);
270
            $failure = new Failure(3, 'remark', ['Class student count exceeded! Max number of students is' . $institutionClass->no_of_students], [null]);
271
            $failures = [0 => $failure];
272
            throw new \Maatwebsite\Excel\Validators\ValidationException($error, $failures);
273
            Log::info('email-sent', [$this->file]);
0 ignored issues
show
Unused Code introduced by
Illuminate\Support\Facad...t', array($this->file)) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
274
        } else {
275
            return true;
276
        }
277
    }
278
279
    public function getNode()
280
    {
281
        return $this->file['node'];
282
    }
283
284
    /**
285
     * @param array $options
286
     * @return string
287
     */
288
    public  function getUniqueOpenemisId($options = [])
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

288
    public  function getUniqueOpenemisId(/** @scrutinizer ignore-unused */ $options = [])

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
289
    {
290
        return Uuid::generate(4);
291
    }
292
293
294
    protected function updateSubjectCount($subject)
295
    {
296
        $totalStudents = Institution_subject_student::getStudentsCount($subject['institution_subject_id']);
297
        Institution_subject::where(['id' => $subject['institution_subject_id']])
298
            ->update([
299
                'total_male_students' => $totalStudents['total_male_students'],
300
                'total_female_students' => $totalStudents['total_female_students']
301
            ]);
302
    }
303
304
305
    /**
306
     *
307
     */
308
    protected function setStudentSubjects($subject)
309
    {
310
        return [
311
            'id' => (string) Uuid::generate(4),
312
            'student_id' => $this->student->student_id,
313
            'institution_class_id' => $this->student->institution_class_id,
314
            'institution_subject_id' => $subject['institution_subject_id'],
315
            'institution_id' => $this->student->institution_id,
316
            'academic_period_id' => $this->student->academic_period_id,
317
            'education_subject_id' => $subject['institution_subject']['education_subject_id'],
318
            'education_grade_id' => $this->student->education_grade_id,
319
            'student_status_id' => 1,
320
            'created_user_id' => $this->file['security_user_id'],
321
            'created' => now()
322
        ];
323
    }
324
325
    protected function insertSubject($subject)
326
    {
327
        if (!Institution_subject_student::isDuplicated($subject)) {
328
            Institution_subject_student::updateOrInsert($subject);
329
        }
330
        $this->updateSubjectCount($subject);
331
    }
332
333
    public function createOrUpdateGuardian($row, $student, $param)
334
    {
335
        if (!empty($row[$param . 's_full_name']) && ($row[$param . 's_date_of_birth_yyyy_mm_dd'] !== null)) {
336
            $guardian = Security_user::createOrUpdateGuardianProfile($row, $param, $this->file);
337
            if (!is_null($guardian)) {
338
                Security_user::where('id', '=', $guardian->id)
339
                    ->update(['is_guardian' => 1]);
340
                $guardian['guardian_relation_id'] = $this->setRelation($param, $guardian);
341
                Student_guardian::createStudentGuardian($student, $guardian, $this->file['security_user_id']);
342
            }
343
        }
344
    }
345
346
    protected function setRelation($param, $guardian)
0 ignored issues
show
Unused Code introduced by
The parameter $guardian is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

346
    protected function setRelation($param, /** @scrutinizer ignore-unused */ $guardian)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
347
    {
348
        switch ($param) {
349
            case 'father':
350
                return 1;
351
            case 'mother':
352
                return 2;
353
            case 'guardian':
354
                return 3;
355
        }
356
    }
357
358
    protected function setGender($row)
359
    {
360
        switch ($row['gender_mf']) {
361
            case 'M':
362
                $row['gender_mf'] = 1;
363
                $this->maleStudentsCount += 1;
364
                break;
365
            case 'F':
366
                $row['gender_mf'] = 2;
367
                $this->femaleStudentsCount += 1;
368
                break;
369
        }
370
        return $row;
371
    }
372
373
    protected function insertOrUpdateSubjects($row, $student, $institution)
374
    {
375
        $mandatorySubject = Institution_class_subject::getMandatorySubjects($this->file['institution_class_id']);
376
        $subjects = getMatchingKeys($row);
377
        $optionalSubjects =  Institution_class_subject::getStudentOptionalSubject($subjects, $student, $row, $institution);
378
        $allSubjects = array_merge_recursive($optionalSubjects, $mandatorySubject);
379
        if (!empty($allSubjects)) {
380
            $allSubjects = unique_multidim_array($allSubjects, 'institution_subject_id');
381
            $this->student = $student;
0 ignored issues
show
Bug Best Practice introduced by
The property student does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
382
            $allSubjects = array_map(array($this, 'setStudentSubjects'), $allSubjects);
383
            $allSubjects = unique_multidim_array($allSubjects, 'education_subject_id');
384
            array_walk($allSubjects, array($this, 'insertSubject'));
385
            array_walk($allSubjects, array($this, 'updateSubjectCount'));
386
        }
387
    }
388
}
389