Passed
Push — develop ( a8f030...cce3fe )
by Francisco
02:56
created

Exchange::isOwnedBy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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