StudentCrudController::setupListOperation()   B
last analyzed

Complexity

Conditions 6
Paths 5

Size

Total Lines 181
Code Lines 114

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 3 Features 0
Metric Value
cc 6
eloc 114
nc 5
nop 0
dl 0
loc 181
rs 7.3777
c 6
b 3
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace App\Http\Controllers\Admin;
4
5
use App\Events\UserCreated;
6
use App\Exceptions\UserSyncException;
7
use App\Http\Requests\StudentRequest;
8
use App\Models\Institution;
9
use App\Models\LeadType;
10
use App\Models\Period;
11
use App\Models\PhoneNumber;
12
use App\Models\Profession;
13
use App\Models\Student;
14
use App\Models\User;
15
use App\Traits\PeriodSelection;
16
use Backpack\CRUD\app\Http\Controllers\CrudController;
17
use Backpack\CRUD\app\Http\Controllers\Operations\CreateOperation;
18
use Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation;
19
use Backpack\CRUD\app\Http\Controllers\Operations\FetchOperation;
20
use Backpack\CRUD\app\Http\Controllers\Operations\ListOperation;
21
use Backpack\CRUD\app\Http\Controllers\Operations\ShowOperation;
22
use Backpack\CRUD\app\Http\Controllers\Operations\UpdateOperation;
23
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
24
use Backpack\CRUD\app\Library\Widget;
25
use Exception;
26
use Illuminate\Http\Request;
27
use Illuminate\Support\Facades\Hash;
28
use Illuminate\Support\Str;
29
30
class StudentCrudController extends CrudController
31
{
32
    use ListOperation;
0 ignored issues
show
introduced by
The trait Backpack\CRUD\app\Http\C...perations\ListOperation requires some properties which are not provided by App\Http\Controllers\Admin\StudentCrudController: $model, $entity_name_plural
Loading history...
33
    use ShowOperation { show as traitShow; }
0 ignored issues
show
introduced by
The trait Backpack\CRUD\app\Http\C...perations\ShowOperation requires some properties which are not provided by App\Http\Controllers\Admin\StudentCrudController: $route, $entity_name
Loading history...
34
    use UpdateOperation;
0 ignored issues
show
introduced by
The trait Backpack\CRUD\app\Http\C...rations\UpdateOperation requires some properties which are not provided by App\Http\Controllers\Admin\StudentCrudController: $entity_name, $model
Loading history...
35
    use CreateOperation { store as traitStore; }
0 ignored issues
show
Bug introduced by
The trait Backpack\CRUD\app\Http\C...rations\CreateOperation requires the property $entity_name which is not provided by App\Http\Controllers\Admin\StudentCrudController.
Loading history...
36
    use PeriodSelection;
0 ignored issues
show
Bug introduced by
The trait App\Traits\PeriodSelection requires the property $id which is not provided by App\Http\Controllers\Admin\StudentCrudController.
Loading history...
37
    use DeleteOperation { destroy as traitDelete; }
38
    use FetchOperation;
0 ignored issues
show
Bug introduced by
The trait Backpack\CRUD\app\Http\C...erations\FetchOperation requires the property $wheres which is not provided by App\Http\Controllers\Admin\StudentCrudController.
Loading history...
39
40
    public function __construct()
41
    {
42
        parent::__construct();
43
        $this->middleware('permission:enrollments.view', ['except' => ['dataAjax', 'show']]);
44
        $this->middleware('permission:student.edit', ['except' => ['index', 'show', 'search', 'dataAjax']]);
45
    }
46
47
    public function setup()
48
    {
49
        CRUD::setModel(Student::class);
50
        CRUD::setRoute(config('backpack.base.route_prefix').'/student');
51
        CRUD::setEntityNameStrings(__('student'), __('students'));
52
        CRUD::enableExportButtons();
53
54
        $permissions = backpack_user()->getAllPermissions();
0 ignored issues
show
Bug introduced by
The method getAllPermissions() does not exist on Illuminate\Contracts\Auth\Authenticatable. It seems like you code against a sub-type of Illuminate\Contracts\Auth\Authenticatable such as Illuminate\Foundation\Auth\User. ( Ignorable by Annotation )

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

54
        $permissions = backpack_user()->/** @scrutinizer ignore-call */ getAllPermissions();
Loading history...
55
        if ($permissions->contains('name', 'enrollments.edit')) {
56
            CRUD::addButtonFromView('line', 'selectCourse', 'selectCourse', 'beginning');
57
        }
58
    }
59
60
    public function setupListOperation()
61
    {
62
63
        // display lead status counts on page top
64
        foreach (LeadType::all() as $leadType) {
65
            if ($leadType->id === 4) {
66
                $count = Student::where('lead_type_id', $leadType->id)->orWhereNull('lead_type_id')->count();
67
            } else {
68
                $count = Student::where('lead_type_id', $leadType->id)->count();
69
            }
70
            if ($count > 0) {
71
                Widget::add([
72
                    'type' => 'view',
73
                    'view' => 'students.lead-type-insights-widget',
74
                    'studentCount' => $count,
75
                    'name' => Str::plural($leadType->name),
76
                    'icon' => $leadType->icon,
77
                    'leadTypeId' => $leadType->id,
78
                    'description' => $leadType->description,
79
                ])->to('before_content');
80
            }
81
        }
82
83
        // Columns.
84
        CRUD::setColumns([
85
            [
86
                'label' => __('ID number'),
87
                'type' => 'text',
88
                'name' => 'idnumber',
89
            ],
90
            [
91
                // 1-n relationship
92
                'label' => __('Last Name'),
93
                'type' => 'relationship',
94
                'key' => 'lastname',
95
                'name' => 'user',
96
                'attribute' => 'lastname',
97
                'model' => User::class,
98
                'orderable' => true,
99
                'orderLogic' => fn ($query, $column, $columnDirection) => $query->leftJoin('users', 'users.id', '=', 'students.id')
100
                    ->orderBy('users.lastname', $columnDirection)->select('students.*'),
101
                'searchLogic' => function ($query, $column, $searchTerm) {
102
                    $query->orWhereHas('user', function ($q) use ($searchTerm) {
103
                        $q->where('lastname', 'like', '%'.$searchTerm.'%');
104
                    });
105
                },
106
            ],
107
108
            [
109
                // 1-n relationship
110
                'label' => __('First Name'),
111
                'type' => 'relationship',
112
                'key' => 'firstname',
113
                'name' => 'user',
114
                'attribute' => 'firstname',
115
                'model' => User::class,
116
                'orderable' => true,
117
                'orderLogic' => fn ($query, $column, $columnDirection) => $query->leftJoin('users', 'users.id', '=', 'students.id')
118
                    ->orderBy('users.firstname', $columnDirection)->select('students.*'),
119
                'searchLogic' => function ($query, $column, $searchTerm) {
120
                    $query->orWhereHas('user', function ($q) use ($searchTerm) {
121
                        $q->where('firstname', 'like', '%'.$searchTerm.'%');
122
                    });
123
                },
124
            ],
125
126
            [
127
                // 1-n relationship
128
                'label' => __('Email'),
129
                'type' => 'relationship',
130
                'name' => 'user',
131
                'attribute' => 'email',
132
                'model' => User::class,
133
                'orderable' => true,
134
                'orderLogic' => fn ($query, $column, $columnDirection) => $query->leftJoin('users', 'users.id', '=', 'students.id')
135
                    ->orderBy('users.email', $columnDirection)->select('students.*'),
136
                'searchLogic' => function ($query, $column, $searchTerm) {
137
                    $query->orWhereHas('user', function ($q) use ($searchTerm) {
138
                        $q->where('email', 'like', '%'.$searchTerm.'%');
139
                    });
140
                },
141
            ],
142
143
            [
144
                'label' => __('Username'),
145
                'type' => 'relationship',
146
                'key' => 'username',
147
                'name' => 'user',
148
                'attribute' => 'username',
149
                'model' => User::class,
150
                'orderable' => false,
151
                'searchLogic' => false,
152
            ],
153
154
            [
155
                'label' => __('Age'),
156
                'name' => 'student_age',
157
            ],
158
159
            [
160
                'label' => __('Birthdate'),
161
                'name' => 'student_birthdate',
162
            ],
163
164
            [
165
                // n-n relationship (with pivot table)
166
                'label' => __('Phone number'),
167
                'type' => 'select_multiple',
168
                'name' => 'phone',
169
                'attribute' => 'phone_number',
170
                'model' => PhoneNumber::class,
171
            ],
172
173
            [
174
                // 1-n relationship
175
                'label' => __('Status'),
176
                'type' => 'text',
177
                'name' => 'lead_status_name',
178
                'orderable' => false,
179
            ],
180
181
        ]);
182
183
        CRUD::addFilter(
184
            [ // select2 filter
185
                'name' => 'enrolled',
186
                'type' => 'select2',
187
                'label' => __('Is Enrolled in'),
188
            ],
189
            fn () => Period::all()->pluck('name', 'id')->toArray(),
190
            function ($value) {
191
                $this->crud->query = $this->crud->query->whereHas('enrollments', fn ($query) => $query->whereHas('course', function ($q) use ($value) {
192
                    $q->where('period_id', $value);
193
                }));
194
            },
195
            function () { // if the filter is NOT active (the GET parameter "checkbox" does not exit)
196
            }
197
        );
198
199
        CRUD::addFilter([ // select2_multiple filter
200
            'name' => 'notenrolled',
201
            'type' => 'select2_multiple',
202
            'label' => __('Is Not Enrolled in'),
203
        ], fn () => Period::all()->pluck('name', 'id')->toArray(), function ($values) {
204
            foreach (json_decode($values, null, 512, JSON_THROW_ON_ERROR) as $value) {
205
                $this->crud->query = $this->crud->query->whereDoesntHave('enrollments', fn ($query) => $query->whereHas('course', function ($q) use ($value) {
206
                    $q->where('period_id', $value);
207
                }));
208
            }
209
        });
210
211
        CRUD::addFilter(
212
            [
213
                'name' => 'new_students',
214
                'type' => 'select2',
215
                'label' => __('New In'),
216
            ],
217
            fn () => Period::all()->pluck('name', 'id')->toArray(),
218
            function ($value) {
219
                CRUD::addClause('newInPeriod', $value);
220
            }
221
        );
222
223
        // select2 filter
224
        $this->crud->addFilter([
225
            'name' => 'institution_id',
226
            'type' => 'select2',
227
            'label' => __('Institution'),
228
        ], fn () => Institution::all()->pluck('name', 'id')->toArray(), function ($value) {
229
            $this->crud->addClause('where', 'institution_id', $value);
230
        });
231
232
        $this->crud->addFilter([
233
            'name' => 'lead_type_id',
234
            'type' => 'select2',
235
            'label' => __('Lead Status'),
236
        ], fn () => LeadType::all()->pluck('name', 'id')->toArray(), function ($value) {
237
            if ($value === '4') {
238
                $this->crud->query = $this->crud->query->where('lead_type_id', $value)->orWhere('lead_type_id', null);
239
            } else {
240
                $this->crud->addClause('where', 'lead_type_id', $value);
241
            }
242
        });
243
    }
244
245
    public function setupCreateOperation()
246
    {
247
        CRUD::setValidation(StudentRequest::class);
248
        CRUD::field('firstname')->label(__('Firstname'))->tab(__('Student Info'));
249
        CRUD::field('lastname')->label(__('Lastname'))->tab(__('Student Info'));
250
        CRUD::field('email')->label(__('Email'))->tab(__('Student Info'));
251
        CRUD::field('idnumber')->label(__('ID number'))->tab(__('Student Info'));
252
        CRUD::field('birthdate')->label(__('Birthdate'))->tab(__('Student Info'));
253
254
        $this->crud->addField([
255
            'name' => 'gender_id',
256
            'label' => __('Gender'),
257
            'type' => 'radio',
258
            'options' => [
259
                0 => __('Other / Rather not say'),
260
                1 => __('Female'),
261
                2 => __('Male'),
262
            ],
263
            'inline' => true,
264
            'tab' => __('Student Info'),
265
        ]);
266
267
        CRUD::addField([
268
            'type' => 'text',
269
            'name' => 'phone',
270
            'tab' => __('Student Info'),
271
            'label' => __('Phone'),
272
        ]);
273
274
        CRUD::addField([
275
            'type' => 'relationship',
276
            'name' => 'profession',
277
            'inline_create' => true,
278
            'tab' => __('Student Info'),
279
            'label' => __('Profession'),
280
            'attribute' => 'name',
281
        ]);
282
283
        CRUD::addField([
284
            'type' => 'relationship',
285
            'name' => 'institution',
286
            'inline_create' => true,
287
            'tab' => __('Student Info'),
288
            'label' => __('Institution'),
289
            'attribute' => 'name',
290
        ]);
291
292
        CRUD::field('address')->label(__('Address'))->tab(__('Address'));
293
        CRUD::field('zip_code')->label(__('zip'))->tab(__('Address'));
294
        CRUD::field('city')->label(__('City'))->tab(__('Address'));
295
        CRUD::field('state')->label(__('State'))->tab(__('Address'));
296
        CRUD::field('country')->label(__('Country'))->tab(__('Address'));
297
298
        CRUD::field('iban')->label('IBAN')->tab(__('Invoicing Info'));
299
        CRUD::field('bic')->label('BIC')->tab(__('Invoicing Info'));
300
    }
301
302
    public function setupUpdateOperation()
303
    {
304
        CRUD::setValidation(StudentRequest::class);
305
        CRUD::field('firstname')->label(__('Firstname'))->tab(__('Student Info'));
306
        CRUD::field('lastname')->label(__('Lastname'))->tab(__('Student Info'));
307
        CRUD::field('email')->label(__('Email'))->tab(__('Student Info'));
308
        CRUD::field('idnumber')->label(__('ID number'))->tab(__('Student Info'));
309
        CRUD::field('birthdate')->label(__('Birthdate'))->tab(__('Student Info'));
310
311
        $this->crud->addField([
312
            'name' => 'gender_id',
313
            'label' => __('Gender'),
314
            'type' => 'radio',
315
            'options' => [
316
                0 => __('Other / Rather not say'),
317
                1 => __('Female'),
318
                2 => __('Male'),
319
            ],
320
            'inline' => true,
321
            'tab' => __('Student Info'),
322
        ]);
323
324
        $this->crud->addField([
325
            'label' => __('Profile Picture'),
326
            'name' => 'image',
327
            'type' => 'image',
328
            'crop' => true,
329
            'tab' => __('Student Info'),
330
        ]);
331
332
        CRUD::addField([
333
            'type' => 'relationship',
334
            'name' => 'profession',
335
            'inline_create' => true,
336
            'tab' => __('Student Info'),
337
            'label' => __('Profession'),
338
            'attribute' => 'name',
339
        ]);
340
341
        CRUD::addField([
342
            'type' => 'relationship',
343
            'name' => 'institution',
344
            'inline_create' => [
345
                'entity' => 'institution',
346
                'force_select' => true,
347
                'include_main_form_fields' => ['name'],
348
            ],
349
            'tab' => __('Student Info'),
350
            'label' => __('Institution'),
351
            'attribute' => 'name',
352
        ]);
353
354
        CRUD::field('address')->label(__('Address'))->tab(__('Address'));
355
        CRUD::field('zip_code')->label(__('zip'))->tab(__('Address'));
356
        CRUD::field('city')->label(__('City'))->tab(__('Address'));
357
        CRUD::field('state')->label(__('State'))->tab(__('Address'));
358
        CRUD::field('country')->label(__('Country'))->tab(__('Address'));
359
360
        CRUD::field('iban')->label('IBAN')->tab(__('Invoicing Info'));
361
        CRUD::field('bic')->label('BIC')->tab(__('Invoicing Info'));
362
    }
363
364
    protected function generateUsername($fullName): string
365
    {
366
        $username_parts = array_filter(explode(' ', strtolower($fullName)));
367
        $username_parts = array_slice($username_parts, -2);
368
369
        $part1 = (! empty($username_parts[0])) ? substr($username_parts[0], 0, 3) : '';
370
        $part2 = (! empty($username_parts[1])) ? substr($username_parts[1], 0, 8) : '';
371
        $part3 = random_int(999, 9999);
372
373
        //str_shuffle to randomly shuffle all characters
374
375
        return $part1.$part2.$part3;
376
    }
377
378
    public function store(Request $request)
379
    {
380
        $request->validate([
381
            'firstname' => 'required|max:255',
382
            'lastname' => 'required|max:255',
383
            'email' => 'nullable|email',
384
        ]);
385
386
        if ($request->email && User::where('email', $request->email)->count() === 0) {
387
            $username = $request->email;
388
        } else {
389
            $username = $this->generateUsername($request->firstname.' '.$request->lastname);
390
        }
391
392
        // update the user info
393
        $user = User::create([
394
            'firstname' => $request->firstname,
395
            'lastname' => $request->lastname,
396
            'email' => $request->email ?? null,
397
            'username' => $username,
398
            'password' => Hash::make(Str::random(12)),
399
        ]);
400
401
        try {
402
            UserCreated::dispatch($user);
403
        } catch (Exception) {
404
            throw new UserSyncException();
405
        }
406
407
        // update the student info
408
409
        $student = Student::create([
410
            'id' => $user->id,
411
            'idnumber' => $request->idnumber,
412
            'address' => $request->address,
413
            'city' => $request->city,
414
            'state' => $request->state,
415
            'country' => $request->country,
416
            'birthdate' => $request->birthdate,
417
        ]);
418
419
        // save phone number
420
        if ($request->phone) {
421
            $phone_number = PhoneNumber::firstOrCreate([
422
                'phone_number' => $request->phone,
423
                'phoneable_id' => $student->id,
424
                'phoneable_type' => Student::class,
425
            ]);
426
427
            $student->phone()->save($phone_number);
428
        }
429
430
        // save profession and institution
431
        if ($request->profession) {
432
            $profession = Profession::firstOrCreate([
433
                'name' => $request->profession,
434
            ]);
435
436
            $student->update([
437
                'profession_id' => $profession->id,
438
            ]);
439
        }
440
441
        if ($request->institution) {
442
            $institution = Institution::firstOrCreate([
443
                'name' => $request->institution,
444
            ]);
445
446
            $student->update([
447
                'institution_id' => $institution->id,
448
            ]);
449
        }
450
451
        return redirect()->route('student.index');
452
    }
453
454
    public function show($student)
455
    {
456
        $student = Student::findOrFail($student);
457
458
        if (! backpack_user()->isTeacher() && ! backpack_user()->can('enrollments.view')) {
0 ignored issues
show
Bug introduced by
The method isTeacher() does not exist on Illuminate\Contracts\Auth\Authenticatable. It seems like you code against a sub-type of Illuminate\Contracts\Auth\Authenticatable such as Illuminate\Foundation\Auth\User. ( Ignorable by Annotation )

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

458
        if (! backpack_user()->/** @scrutinizer ignore-call */ isTeacher() && ! backpack_user()->can('enrollments.view')) {
Loading history...
459
            abort(403);
460
        }
461
462
        $comments = $student->comments;
463
464
        return view('students/show', [
465
            'student' => $student,
466
            'comments' => $comments,
467
            'lead_types' => LeadType::all(),
468
            'attendances' => $student->periodAttendance()->get(),
469
            'writeaccess' => backpack_user()->can('enrollments.edit') ?? 0,
470
        ]);
471
    }
472
473
    public function destroy($id)
474
    {
475
        $this->crud->hasAccessOrFail('delete');
476
477
        // get entry ID from Request (makes sure its the last ID for nested resources)
478
        $id = $this->crud->getCurrentEntryId() ?? $id;
479
480
        User::where('id', $id)->delete();
481
482
        return $this->crud->delete($id);
0 ignored issues
show
Bug introduced by
It seems like $id can also be of type boolean; however, parameter $id of Backpack\CRUD\app\Librar...nel\CrudPanel::delete() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

482
        return $this->crud->delete(/** @scrutinizer ignore-type */ $id);
Loading history...
483
    }
484
485
    protected function fetchInstitution()
486
    {
487
        return $this->fetch([
488
            'model' => Institution::class,
489
            'searchable_attributes' => ['name'],
490
        ]);
491
    }
492
493
    protected function fetchProfession()
494
    {
495
        return $this->fetch([
496
            'model' => Profession::class,
497
            'searchable_attributes' => ['name'],
498
        ]);
499
    }
500
}
501