Exchange::perform()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 0
dl 0
loc 15
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace App\Judite\Models;
4
5
use Illuminate\Support\Collection;
6
use Illuminate\Database\Eloquent\Model;
7
use App\Judite\Contracts\Registry\ExchangeRegistry;
8
use App\Exceptions\EnrollmentCannotBeExchangedException;
9
use App\Exceptions\ExchangeEnrollmentsOnDifferentCoursesException;
10
11
class Exchange extends Model
12
{
13
    /**
14
     * The relations to eager load on every query.
15
     *
16
     * @var array
17
     */
18
    protected $with = ['fromEnrollment', 'toEnrollment'];
19
20
    /**
21
     * The exchanges registry.
22
     *
23
     * @var \App\Judite\Contracts\Registry\ExchangeRegistry
24
     */
25
    private $registry;
26
27
    /**
28
     * Performed exchange indicator.
29
     *
30
     * @var bool
31
     */
32
    private $performed = false;
33
34
    /**
35
     * Create a new Exchange model instance.
36
     *
37
     * @param array $attributes
38
     */
39
    public function __construct(array $attributes = [])
40
    {
41
        parent::__construct($attributes);
42
        $this->registry = resolve(ExchangeRegistry::class);
43
    }
44
45
    /**
46
     * Scope a query to filter exchanges by owner.
47
     *
48
     * @param \Illuminate\Database\Eloquent\Builder $query
49
     * @param \App\Judite\Models\Student            $student
50
     *
51
     * @return \Illuminate\Database\Eloquent\Builder
52
     */
53
    public function scopeOwnedBy($query, Student $student)
54
    {
55
        $studentEnrollmentsQuery = $student->enrollments()
56
            ->select((new Enrollment())->getKeyName())
57
            ->getBaseQuery();
58
59
        return $query->whereIn('from_enrollment_id', $studentEnrollmentsQuery);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->whereIn('...tudentEnrollmentsQuery) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
60
    }
61
62
    /**
63
     * Set the enrollments of this exchange.
64
     *
65
     * @param \App\Judite\Models\Enrollment $from
66
     * @param \App\Judite\Models\Enrollment $to
67
     *
68
     * @throws \App\Exceptions\EnrollmentCannotBeExchangedException
69
     * @throws \App\Exceptions\ExchangeEnrollmentsOnDifferentCoursesException
70
     *
71
     * @return $this
72
     */
73
    public function setExchangeEnrollments(Enrollment $from, Enrollment $to) : self
74
    {
75
        if (! $from->availableForExchange() || is_null($to->shift)) {
76
            throw new EnrollmentCannotBeExchangedException();
77
        }
78
79
        if (! $from->course->is($to->course)) {
80
            throw new ExchangeEnrollmentsOnDifferentCoursesException();
81
        }
82
83
        $this->fromEnrollment()->associate($from);
84
        $this->toEnrollment()->associate($to);
85
86
        return $this;
87
    }
88
89
    /**
90
     * Check if an inverse exchange exists.
91
     *
92
     * @param \App\Judite\Models\Enrollment $from
93
     * @param \App\Judite\Models\Enrollment $to
94
     *
95
     * @return \App\Judite\Models\Exchange|null
96
     */
97
    public static function findMatchingExchange(Enrollment $from, Enrollment $to): ?self
98
    {
99
        $inverseMatch = [
100
            'from_enrollment_id' => $to->id,
101
            'to_enrollment_id' => $from->id,
102
        ];
103
104
        return self::where($inverseMatch)->first();
105
    }
106
107
    /**
108
     * Scope a query to only filter exchanges which source enrollment is in a set of values.
109
     *
110
     * @param \Illuminate\Database\Eloquent\Builder $query
111
     * @param mixed                                 $values
112
     *
113
     * @return \Illuminate\Database\Eloquent\Builder
114
     */
115
    public function scopeWhereFromEnrollmentIn($query, $values)
116
    {
117
        return $query->whereIn('from_enrollment_id', $values);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->whereIn('...nrollment_id', $values) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
118
    }
119
120
    /**
121
     * Scope a query to only filter exchanges which target enrollment is in a set of values.
122
     *
123
     * @param \Illuminate\Database\Eloquent\Builder $query
124
     * @param mixed                                 $values
125
     *
126
     * @return \Illuminate\Database\Eloquent\Builder
127
     */
128
    public function scopeWhereToEnrollmentIn($query, $values)
129
    {
130
        return $query->whereIn('to_enrollment_id', $values);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->whereIn('...nrollment_id', $values) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
131
    }
132
133
    /**
134
     * Perform the exchange and update the associated enrollments.
135
     *
136
     * @return $this
137
     */
138
    public function perform(): self
139
    {
140
        $fromEnrollmentCopy = clone $this->fromEnrollment;
141
        $toEnrollmentCopy = clone $this->toEnrollment;
142
143
        $this->fromEnrollment->exchange($this->toEnrollment);
144
145
        $exchangedEnrollments = collect([$this->fromEnrollment, $this->toEnrollment]);
146
        $this->deleteExchangesOfEnrollments($exchangedEnrollments);
147
148
        $this->registry->record($fromEnrollmentCopy, $toEnrollmentCopy);
149
150
        $this->performed = true;
151
152
        return $this;
153
    }
154
155
    /**
156
     * Check whether this exchange is performed.
157
     *
158
     * @return bool
159
     */
160
    public function isPerformed(): bool
161
    {
162
        return $this->performed;
163
    }
164
165
    /**
166
     * Get the source enrollment of this exchange.
167
     *
168
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
169
     */
170
    public function fromEnrollment()
171
    {
172
        return $this->belongsTo(Enrollment::class, 'from_enrollment_id');
173
    }
174
175
    /**
176
     * Get the target enrollment of this exchange.
177
     *
178
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
179
     */
180
    public function toEnrollment()
181
    {
182
        return $this->belongsTo(Enrollment::class, 'to_enrollment_id');
183
    }
184
185
    /**
186
     * Get the course of this exchange.
187
     *
188
     * @return \App\Judite\Models\Course
189
     */
190
    public function course(): Course
191
    {
192
        return $this->fromEnrollment->course;
193
    }
194
195
    /**
196
     * Get the source shift of this exchange.
197
     *
198
     * @return \App\Judite\Models\Shift
199
     */
200
    public function fromShift(): Shift
201
    {
202
        return $this->fromEnrollment->shift;
203
    }
204
205
    /**
206
     * Get the target shift of this exchange.
207
     *
208
     * @return \App\Judite\Models\Shift
209
     */
210
    public function toShift(): Shift
211
    {
212
        return $this->toEnrollment->shift;
213
    }
214
215
    /**
216
     * Get the source student of this exchange.
217
     *
218
     * @return \App\Judite\Models\Student
219
     */
220
    public function fromStudent(): Student
221
    {
222
        return $this->fromEnrollment->student;
223
    }
224
225
    /**
226
     * Get the target student of this exchange.
227
     *
228
     * @return \App\Judite\Models\Student
229
     */
230
    public function toStudent(): Student
231
    {
232
        return $this->toEnrollment->student;
233
    }
234
235
    /**
236
     * Get the target student of this exchange.
237
     *
238
     * @param \App\Judite\Models\Student $student
239
     *
240
     * @return bool
241
     */
242
    public function isOwnedBy(Student $student): bool
243
    {
244
        return $this->fromStudent()->is($student);
245
    }
246
247
    /**
248
     * Deletes all exchanges involving the given enrollments.
249
     *
250
     * @param \Illuminate\Support\Collection $enrollments
251
     */
252
    private function deleteExchangesOfEnrollments(Collection $enrollments)
253
    {
254
        $enrollmentIds = $enrollments->pluck('id');
255
256
        self::whereIn('from_enrollment_id', $enrollmentIds)
257
            ->orWhereIn('to_enrollment_id', $enrollmentIds)
258
            ->delete();
259
    }
260
}
261