Passed
Push — develop ( a18085...de8258 )
by Francisco
14:47
created

Exchange::deleteExchangesOfEnrollments()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 7
rs 9.4285
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);
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) : Exchange
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): ?Exchange
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);
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);
131
    }
132
133
    /**
134
     * Perform the exchange and update the associated enrollments.
135
     *
136
     * @return $this
137
     */
138
    public function perform(): Exchange
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
     * Deletes all exchanges involving the given enrollments.
187
     *
188
     * @param \Illuminate\Support\Collection $enrollments
189
     */
190
    private function deleteExchangesOfEnrollments(Collection $enrollments)
191
    {
192
        $enrollmentIds = $enrollments->pluck('id');
193
194
        self::whereIn('from_enrollment_id', $enrollmentIds)
195
            ->orWhereIn('to_enrollment_id', $enrollmentIds)
196
            ->delete();
197
    }
198
199
    /**
200
     * Get the course of this exchange.
201
     *
202
     * @return \App\Judite\Models\Course
203
     */
204
    public function course(): Course
205
    {
206
        return $this->fromEnrollment->course;
207
    }
208
209
    /**
210
     * Get the source shift of this exchange.
211
     *
212
     * @return \App\Judite\Models\Shift
213
     */
214
    public function fromShift(): Shift
215
    {
216
        return $this->fromEnrollment->shift;
217
    }
218
219
    /**
220
     * Get the target shift of this exchange.
221
     *
222
     * @return \App\Judite\Models\Shift
223
     */
224
    public function toShift(): Shift
225
    {
226
        return $this->toEnrollment->shift;
227
    }
228
229
    /**
230
     * Get the source student of this exchange.
231
     *
232
     * @return \App\Judite\Models\Student
233
     */
234
    public function fromStudent(): Student
235
    {
236
        return $this->fromEnrollment->student;
237
    }
238
239
    /**
240
     * Get the target student of this exchange.
241
     *
242
     * @return \App\Judite\Models\Student
243
     */
244
    public function toStudent(): Student
245
    {
246
        return $this->toEnrollment->student;
247
    }
248
249
    /**
250
     * Get the target student of this exchange.
251
     *
252
     * @param \App\Judite\Models\Student $student
253
     *
254
     * @return bool
255
     */
256
    public function isOwnedBy(Student $student): bool
257
    {
258
        return $this->fromStudent()->is($student);
259
    }
260
}
261