Passed
Push — master ( 25098a...6e44a2 )
by Thomas
07:21
created

Student::getImageAttribute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
namespace App\Models;
4
5
use App\Events\LeadStatusUpdatedEvent;
6
use App\Events\StudentDeleting;
7
use App\Events\StudentUpdated;
8
use Backpack\CRUD\app\Models\Traits\CrudTrait;
9
use Carbon\Carbon;
10
use Illuminate\Database\Eloquent\Builder;
11
use Illuminate\Database\Eloquent\Model;
12
use Illuminate\Support\Facades\App;
13
use Illuminate\Support\Str;
14
use Spatie\Activitylog\Traits\LogsActivity;
15
use Spatie\Image\Manipulations;
16
use Spatie\MediaLibrary\HasMedia;
17
use Spatie\MediaLibrary\InteractsWithMedia;
18
use Spatie\MediaLibrary\MediaCollections\Models\Media;
19
20
class Student extends Model implements HasMedia
21
{
22
    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...
23
    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...
24
    use LogsActivity;
25
26
    protected $dispatchesEvents = [
27
        'deleting' => StudentDeleting::class,
28
        'updated' => StudentUpdated::class,
29
    ];
30
31
    public $timestamps = true;
32
33
    protected $guarded = [];
34
35
    public $incrementing = false;
36
37
    protected $with = ['user', 'phone', 'institution', 'profession'];
38
39
    protected $appends = ['email', 'name', 'firstname', 'lastname', 'student_age', 'student_birthdate', 'is_enrolled'];
40
41
    protected static bool $logUnguarded = true;
42
43
    public function scopeEnrolled($query)
44
    {
45
        return $query->whereHas('enrollments', function ($q) {
46
            return $q->whereHas('course', function ($q) {
47
                return $q->where('period_id', Period::get_default_period()->id);
48
            });
49
        });
50
    }
51
52
    public function scopeComputedLeadType($query, $leadTypeId)
53
    {
54
        return match ($leadTypeId) {
55
            1 => $query->whereHas('enrollments', fn ($query) => $query->whereHas('course', function ($q) {
56
                $q->where('period_id', Period::get_default_period()->id);
57
            })),
58
59
            2, 3 => $query->where('lead_type_id', $leadTypeId),
60
61
            4 => $query
62
                ->where('lead_type_id', $leadTypeId)
63
                ->orWhere(function ($query) {
64
                    $query
65
                        ->whereNull('lead_type_id')
66
                        ->whereHas('enrollments', fn ($query) => $query
67
                            ->whereHas('course', function ($q) {
68
                                $q->where('period_id', '!=', Period::get_default_period()->id);
69
                            }))
70
                        ->whereDoesntHave('enrollments', fn ($query) => $query
71
                            ->whereHas('course', function ($q) {
72
                                $q->where('period_id', Period::get_default_period()->id);
73
                            }));
74
                }
75
            ),
76
77
            default => $query,
78
        };
79
    }
80
81
    public function scopeNewInPeriod(Builder $query, int $periodId)
82
    {
83
        return $query->whereIn('id', Period::find($periodId)->newStudents()->pluck(['student_id'])->toArray());
0 ignored issues
show
Bug introduced by
array('student_id') of type array<integer,string> is incompatible with the type string expected by parameter $column of Illuminate\Database\Query\Builder::pluck(). ( Ignorable by Annotation )

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

83
        return $query->whereIn('id', Period::find($periodId)->newStudents()->pluck(/** @scrutinizer ignore-type */ ['student_id'])->toArray());
Loading history...
Bug introduced by
array('student_id') of type array<integer,string> is incompatible with the type Illuminate\Database\Query\Expression|string expected by parameter $column of Illuminate\Database\Eloquent\Builder::pluck(). ( Ignorable by Annotation )

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

83
        return $query->whereIn('id', Period::find($periodId)->newStudents()->pluck(/** @scrutinizer ignore-type */ ['student_id'])->toArray());
Loading history...
84
    }
85
86
    public function registerMediaConversions(Media $media = null): void
87
    {
88
        $this->addMediaConversion('thumb')
89
            ->fit(Manipulations::FIT_MAX, 1200, 1200)
90
            ->optimize();
91
    }
92
93
    /** relations */
94
    public function user()
95
    {
96
        return $this->belongsTo(User::class, 'id', 'id');
97
    }
98
99
    public function attendance()
100
    {
101
        return $this->hasMany(Attendance::class);
102
    }
103
104
    public function periodAbsences(Period $period = null)
105
    {
106
        if ($period == null) {
107
            $period = Period::get_default_period();
108
        }
109
110
        return $this->hasMany(Attendance::class)
111
        ->where('attendance_type_id', 4) // absence
112
        ->whereHas('event', fn ($q) => $q->whereHas('course', fn ($c) => $c->where('period_id', $period->id)));
113
    }
114
115
    public function periodAttendance(Period $period = null)
116
    {
117
        if ($period == null) {
118
            $period = Period::get_default_period();
119
        }
120
121
        return $this->hasMany(Attendance::class)
122
        ->whereHas('event', fn ($q) => $q->whereHas('course', fn ($c) => $c->where('period_id', $period->id)));
123
    }
124
125
    public function contacts()
126
    {
127
        return $this->hasMany(Contact::class, 'student_id');
128
    }
129
130
    public function comments()
131
    {
132
        return $this->morphMany(Comment::class, 'commentable');
133
    }
134
135
    public function phone()
136
    {
137
        return $this->morphMany(PhoneNumber::class, 'phoneable');
138
    }
139
140
    public function enrollments()
141
    {
142
        return $this->hasMany(Enrollment::class)
143
            ->with('course');
144
    }
145
146
    public function leadType()
147
    {
148
        return $this->belongsTo(LeadType::class);
149
    }
150
151
    public function institution()
152
    {
153
        return $this->belongsTo(Institution::class);
154
    }
155
156
    public function profession()
157
    {
158
        return $this->belongsTo(Profession::class);
159
    }
160
161
    public function books()
162
    {
163
        return $this->belongsToMany(Book::class)->withPivot('id', 'code', 'status_id', 'expiry_date');
164
    }
165
166
    /** attributes */
167
    public function getFirstnameAttribute(): string
168
    {
169
        return $this->user ? Str::title($this->user->firstname) : '';
170
    }
171
172
    public function getLastnameAttribute(): string
173
    {
174
        if ($this->user) {
175
            return Str::upper($this->user->lastname);
176
        }
177
178
        return '';
179
    }
180
181
    public function getEmailAttribute(): ?string
182
    {
183
        return $this?->user?->email;
184
    }
185
186
    public function getNameAttribute(): string
187
    {
188
        return $this->user ? "{$this->firstname} {$this->lastname}" : '';
189
    }
190
191
    public function getStudentAgeAttribute()
192
    {
193
        return $this->birthdate ? Carbon::parse($this->birthdate)->age . ' ' . __('years old') : '';
0 ignored issues
show
Bug introduced by
Are you sure __('years old') of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

193
        return $this->birthdate ? Carbon::parse($this->birthdate)->age . ' ' . /** @scrutinizer ignore-type */ __('years old') : '';
Loading history...
194
    }
195
196
    public function getStudentBirthdateAttribute()
197
    {
198
        return $this->birthdate ? Carbon::parse($this->birthdate)->locale(App::getLocale())->isoFormat('LL') : '';
199
    }
200
201
    public function getIsEnrolledAttribute()
202
    {
203
        // if the student is currently enrolled
204
        if ($this->enrollments()->whereHas('course', fn ($q) => $q->where('period_id', Period::get_default_period()->id))->count() > 0) {
205
            return 1;
206
        }
207
208
        return false;
209
    }
210
211
    public function getLeadStatusNameAttribute()
212
    {
213
        return LeadType::find($this->lead_type_id)->name ?? null;
214
    }
215
216
    public function lead_status()
217
    {
218
        // if the student is currently enrolled, they are CONVERTED
219
        if ($this->is_enrolled) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->is_enrolled of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false 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...
220
            return 1;
221
        }
222
223
        // if the student has a special status, return it
224
        if ($this->leadType != null) {
225
            return $this->leadType->id;
226
        }
227
228
        // if the student was previously enrolled, they must be potential students
229
        if ($this->has('enrollments')) {
230
            return 4;
231
        }
232
233
        // otherwise, their status cannot be determined and should be left blank
234
        return null;
235
    }
236
237
    /** functions */
238
239
    /**
240
     * enroll the student in a course.
241
     * If the course has any children, we also enroll the student in the children courses.
242
     */
243
    public function enroll(Course $course): int
244
    {
245
        // avoid duplicates by retrieving an potential existing enrollment for the same course
246
        $enrollment = Enrollment::firstOrCreate([
247
            'student_id' =>  $this->id,
248
            'course_id' => $course->id,
249
        ],
250
        [
251
            '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...
252
        ]);
253
254
        // if the course has children, enroll in children as well.
255
        if ($course->children_count > 0) {
256
            foreach ($course->children as $children_course) {
257
                Enrollment::firstOrCreate([
258
                    'student_id' =>  $this->id,
259
                    'course_id' => $children_course->id,
260
                    'parent_id' => $enrollment->id,
261
                ],
262
                [
263
                    'responsible_id' => backpack_user()->id ?? 1,
264
                ]);
265
            }
266
        }
267
268
        $this->update(['lead_type_id' => null]); // fallback to default (converted)
269
270
        // to subscribe the student to mailing lists and so on
271
        $listId = config('mailing-system.mailerlite.activeStudentsListId');
272
        LeadStatusUpdatedEvent::dispatch($this, $listId);
273
274
        foreach ($this->contacts as $contact) {
275
            LeadStatusUpdatedEvent::dispatch($contact, $listId);
276
        }
277
278
        return $enrollment->id;
279
    }
280
281
    /** SETTERS */
282
    public function setFirstnameAttribute($value)
283
    {
284
        $this->user->update(['firstname' => $value]);
285
    }
286
287
    public function setLastnameAttribute($value)
288
    {
289
        $this->user->update(['lastname' => $value]);
290
    }
291
292
    public function setEmailAttribute($value)
293
    {
294
        $this->user->update(['email' => $value]);
295
    }
296
297
    public function getImageAttribute(): ?string
298
    {
299
        return $this->getMedia('profile-picture')->last()?->getUrl('thumb');
300
    }
301
302
    public function setImageAttribute($value)
303
    {
304
        // if the image was erased
305
        if ($value==null) {
306
            $this->clearMediaCollection('profile-picture');
307
        }
308
309
        // if a base64 was sent, store it in the db
310
        if (Str::startsWith($value, 'data:image'))
311
        {
312
            $this->addMediaFromBase64($value)
313
                ->usingFileName('profilePicture.jpg')
314
                ->toMediaCollection('profile-picture');
315
        }
316
    }
317
}
318