Passed
Push — master ( 9bf1ea...1a4135 )
by Thomas
08:04
created

Student::scopeComputedLeadType()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 38
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 27
nc 5
nop 2
dl 0
loc 38
rs 9.1768
c 0
b 0
f 0
1
<?php
2
3
namespace App\Models;
4
5
use Backpack\CRUD\app\Models\Traits\CrudTrait;
6
use Carbon\Carbon;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Support\Facades\App;
9
use Illuminate\Support\Str;
10
use Spatie\Activitylog\Traits\LogsActivity;
11
use Spatie\Image\Manipulations;
12
use Spatie\MediaLibrary\HasMedia;
13
use Spatie\MediaLibrary\InteractsWithMedia;
14
use Spatie\MediaLibrary\MediaCollections\Models\Media;
15
16
class Student extends Model implements HasMedia
17
{
18
    use CrudTrait;
0 ignored issues
show
introduced by
The trait Backpack\CRUD\app\Models\Traits\CrudTrait requires some properties which are not provided by App\Models\Student: $fakeColumns, $identifiableAttribute, $Type
Loading history...
19
    use InteractsWithMedia;
0 ignored issues
show
introduced by
The trait Spatie\MediaLibrary\InteractsWithMedia requires some properties which are not provided by App\Models\Student: $fallbackPath, $mediaConversionRegistrations, $forceDeleting, $fallbackUrl, $media, $collection_name
Loading history...
20
    use LogsActivity;
21
22
    public $timestamps = true;
23
    protected $guarded = [];
24
    public $incrementing = false;
25
    protected $with = ['user', 'phone', 'institution', 'profession'];
26
    protected $appends = ['email', 'name', 'firstname', 'lastname', 'student_age', 'student_birthdate', 'lead_status', 'is_enrolled'];
27
    protected static $logUnguarded = true;
28
29
    protected static function boot()
30
    {
31
        parent::boot();
32
33
        // when deleting a student, also delete any enrollments, grades and attendance related to this student
34
        static::deleting(function (self $student) {
35
            Attendance::where('student_id', $student->id)->delete();
36
            Enrollment::where('student_id', $student->id)->delete();
37
        });
38
    }
39
40
    public function scopeComputedLeadType($query, $leadTypeId)
41
    {
42
        switch ($leadTypeId) {
43
            case 1: // active / enrolled students
44
                return $query->whereHas('enrollments', function ($query) use ($leadTypeId) {
45
                    return $query->whereHas('course', function ($q) use ($leadTypeId) {
0 ignored issues
show
Unused Code introduced by
The import $leadTypeId is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
46
                        $q->where('period_id', Period::get_default_period()->id);
47
                    });
48
                });
49
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
50
51
            case 2: // custom lead status
52
            case 3:
53
                return $query->where('lead_type_id', $leadTypeId);
54
                break;
55
56
            case 4: // old students who have at least one enrollment
57
                return $query
58
                    ->where('lead_type_id', $leadTypeId)
59
                    ->orWhere(function ($query) use ($leadTypeId) {
60
                        $query
61
                            ->whereNull('lead_type_id')
62
                            ->whereHas('enrollments', function ($query) use ($leadTypeId) {
63
                                return $query
64
                                    ->whereHas('course', function ($q) use ($leadTypeId) {
0 ignored issues
show
Unused Code introduced by
The import $leadTypeId is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
65
                                        $q->where('period_id', '!=', Period::get_default_period()->id);
66
                                    });
67
                            })
68
                            ->whereDoesntHave('enrollments', function ($query) use ($leadTypeId) {
69
                                return $query
70
                                    ->whereHas('course', function ($q) use ($leadTypeId) {
0 ignored issues
show
Unused Code introduced by
The import $leadTypeId is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
71
                                        $q->where('period_id', Period::get_default_period()->id);
72
                                    });
73
                            });
74
                    });
75
                break;
76
            default:
77
                return $query;
78
        }
79
    }
80
81
    public function registerMediaConversions(Media $media = null): void
82
    {
83
        $this->addMediaConversion('thumb')
84
            ->fit(Manipulations::FIT_MAX, 1200, 1200)
85
            ->optimize()->nonQueued();
86
    }
87
88
    /** relations */
89
    public function user()
90
    {
91
        return $this->belongsTo(User::class, 'id', 'id');
92
    }
93
94
    public function attendance()
95
    {
96
        return $this->hasMany(Attendance::class);
97
    }
98
99
    public function periodAbsences(Period $period = null)
100
    {
101
        if ($period == null) {
102
            $period = $this->currentPeriod;
0 ignored issues
show
Bug introduced by
The property currentPeriod does not seem to exist on App\Models\Student. 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...
103
        }
104
105
        return $this->hasMany(Attendance::class)
106
        ->where('attendance_type_id', 4) // absence
107
        ->whereHas('event', function ($q) use ($period) {
108
            return $q->whereHas('course', function ($c) use ($period) {
109
                return $c->where('period_id', $period->id);
110
            });
111
        });
112
    }
113
114
    public function periodAttendance(Period $period = null)
115
    {
116
        if ($period == null) {
117
            $period = Period::get_default_period();
118
        }
119
120
        return $this->hasMany(Attendance::class)
121
        ->whereHas('event', function ($q) use ($period) {
122
            return $q->whereHas('course', function ($c) use ($period) {
123
                return $c->where('period_id', $period->id);
124
            });
125
        });
126
    }
127
128
    public function contacts()
129
    {
130
        return $this->hasMany(Contact::class, 'student_id');
131
    }
132
133
    public function comments()
134
    {
135
        return $this->morphMany(Comment::class, 'commentable');
136
    }
137
138
    public function phone()
139
    {
140
        return $this->morphMany(PhoneNumber::class, 'phoneable');
141
    }
142
143
    public function enrollments()
144
    {
145
        return $this->hasMany(Enrollment::class)
146
            ->with('course');
147
    }
148
149
    public function leadType()
150
    {
151
        return $this->belongsTo(LeadType::class);
152
    }
153
154
    public function real_enrollments()
155
    {
156
        return $this->hasMany(Enrollment::class)
157
            ->with('course')
158
            ->whereIn('status_id', ['1', '2'])
159
            ->whereDoesntHave('childrenEnrollments');
160
    }
161
162
    public function institution()
163
    {
164
        return $this->belongsTo(Institution::class);
165
    }
166
167
    public function profession()
168
    {
169
        return $this->belongsTo(Profession::class);
170
    }
171
172
    /** attributes */
173
    public function getFirstnameAttribute()
174
    {
175
        if ($this->user) {
176
            return Str::title($this->user->firstname);
177
        }
178
    }
179
180
    public function getLastnameAttribute()
181
    {
182
        if ($this->user) {
183
            return Str::upper($this->user->lastname);
184
        }
185
    }
186
187
    public function getEmailAttribute()
188
    {
189
        if ($this->user) {
190
            return $this->user->email;
191
        }
192
    }
193
194
    public function getNameAttribute()
195
    {
196
        if ($this->user) {
197
            return $this->firstname.' '.$this->lastname;
198
        }
199
    }
200
201
    public function getStudentAgeAttribute()
202
    {
203
        return Carbon::parse($this->birthdate)->age ?? '';
204
    }
205
206
    public function getStudentBirthdateAttribute()
207
    {
208
        return Carbon::parse($this->birthdate)->locale(App::getLocale())->isoFormat('LL');
209
    }
210
211
    public function getIsEnrolledAttribute()
212
    {
213
        // if the student is currently enrolled
214
        if ($this->enrollments()->whereHas('course', function ($q) {
215
            return $q->where('period_id', Period::get_default_period()->id);
216
        })->count() > 0) {
217
            return 1;
218
        }
219
    }
220
221
    public function getLeadStatusNameAttribute()
222
    {
223
        return LeadType::find($this->lead_status)->name;
224
    }
225
226
    public function getLeadStatusAttribute()
227
    {
228
        // if the student is currently enrolled, they are CONVERTED
229
        if ($this->is_enrolled) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->is_enrolled of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
230
            return 1;
231
        }
232
233
        // if the student has a special status, return it
234
        if ($this->leadType != null) {
235
            return $this->leadType->id;
236
        }
237
        // if the student was previously enrolled, they must be potential students
238
        elseif ($this->has('enrollments')) {
239
            return 4;
240
        } else {
241
            return;
242
        }
243
        // otherwise, their status cannot be determined and should be left blank
244
    }
245
246
    /** functions */
247
248
    /**
249
     * enroll the student in a course.
250
     * If the course has any children, we also enroll the student in the children courses.
251
     */
252
    public function enroll(Course $course): int
253
    {
254
        // avoid duplicates by retrieving an potential existing enrollment for the same course
255
        $enrollment = Enrollment::firstOrNew([
256
            'student_id' =>  $this->id,
257
            'course_id' => $course->id,
258
        ]);
259
260
        $enrollment->responsible_id = backpack_user()->id ?? 1;
0 ignored issues
show
Bug introduced by
Accessing id on the interface Illuminate\Contracts\Auth\Authenticatable suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
261
        $enrollment->save();
262
263
        // if the course has children, enroll in children as well.
264
        if ($course->children_count > 0) {
265
            foreach ($course->children as $children_course) {
266
                $child_enrollment = Enrollment::firstOrNew([
267
                    'student_id' =>  $this->id,
268
                    'course_id' => $children_course->id,
269
                    'parent_id' => $enrollment->id,
270
                ]);
271
                $child_enrollment->responsible_id = backpack_user()->id ?? null;
272
                $child_enrollment->save();
273
            }
274
        }
275
276
        $this->update(['lead_type_id' => null]); // fallback to default (converted)
277
278
        return $enrollment->id;
279
    }
280
281
    /** SETTERS */
282
    public function setFirstnameAttribute($value)
283
    {
284
        $this->user->firstname = $value;
285
        $this->user->save();
286
    }
287
288
    public function setLastnameAttribute($value)
289
    {
290
        $this->user->lastname = $value;
291
        $this->user->save();
292
    }
293
294
    public function setEmailAttribute($value)
295
    {
296
        $this->user->email = $value;
297
        $this->user->save();
298
    }
299
}
300