Completed
Pull Request — master (#18)
by
unknown
06:35
created

ReservationsFacade::bulkDelete()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 13
rs 9.4285
ccs 0
cts 6
cp 0
cc 3
eloc 6
nc 3
nop 1
crap 12
1
<?php namespace VojtaSvoboda\Reservations\Facades;
2
3
use Auth;
4
use Carbon\Carbon;
5
use Config;
6
use Event;
7
use Lang;
8
use Illuminate\Database\Eloquent\Collection;
9
use Illuminate\Support\Facades\DB;
10
use October\Rain\Exception\ApplicationException;
11
use October\Rain\Exception\ValidationException;
12
use VojtaSvoboda\Reservations\Classes\DatesResolver;
13
use VojtaSvoboda\Reservations\Mailers\ReservationAdminMailer;
14
use VojtaSvoboda\Reservations\Mailers\ReservationMailer;
15
use VojtaSvoboda\Reservations\Models\Reservation;
16
use VojtaSvoboda\Reservations\Models\Settings;
17
use VojtaSvoboda\Reservations\Models\Status;
18
19
/**
20
 * Public reservations facade.
21
 *
22
 * Usage: App::make(ReservationsFacade::class);
23
 *
24
 * @package VojtaSvoboda\Reservations\Facades
25
 */
26
class ReservationsFacade
27
{
28
    /** @var Reservation $reservations */
29
    private $reservations;
30
31
    /** @var Status $statuses */
32
    private $statuses;
33
34
    /** @var DatesResolver $datesResolver */
35
    private $datesResolver;
36
37
    /** @var array $returningUsersCache */
38
    private $returningUsersCache;
39
40
    /** @var ReservationMailer $mailer */
41
    private $mailer;
42
43
    /** @var ReservationAdminMailer $adminMailer */
44
    private $adminMailer;
45
46
    /**
47
     * ReservationsFacade constructor.
48
     *
49
     * @param Reservation $reservations
50
     * @param Status $statuses
51
     * @param DatesResolver $resolver
52
     * @param ReservationMailer $mailer
53
     * @param ReservationAdminMailer $adminMailer
54 14
     */
55
    public function __construct(
56
        Reservation $reservations, Status $statuses, DatesResolver $resolver,
57
        ReservationMailer $mailer, ReservationAdminMailer $adminMailer
58 14
    ) {
59 14
        $this->reservations = $reservations;
60 14
        $this->statuses = $statuses;
61 14
        $this->datesResolver = $resolver;
62 14
        $this->mailer = $mailer;
63 14
        $this->adminMailer = $adminMailer;
64
    }
65
66
    /**
67
     * Create and store reservation.
68
     *
69
     * @param array $data
70
     *
71
     * @return Reservation $reservation
72
     *
73
     * @throws ApplicationException
74
     * @throws ValidationException
75 7
     */
76
    public function storeReservation($data)
77
    {
78 7
        // check number of sends
79
        $this->checkLimits();
80
81 7
        // transform date and time to Carbon
82
        $data['date'] = $this->transformDateTime($data);
83
84 5
        // place for extending
85
        Event::fire('vojtasvoboda.reservations.processReservation', [&$data]);
86
87 5
        // create reservation
88
        $reservation = $this->reservations->create($data);
89
90 5
        // send mails to client and admin
91
        $this->sendMails($reservation);
92 5
93
        return $reservation;
94
    }
95
96
    /**
97
     * Send mail to client and admin.
98
     *
99
     * @param Reservation $reservation
100 5
     */
101
    public function sendMails($reservation)
102
    {
103 5
        // calculate reservations by same email
104
        $sameCount = $this->getReservationsCountByMail($reservation->email);
105
106 5
        // send reservation confirmation to customer
107
        $this->mailer->send($reservation, $sameCount);
108
109 5
        // send reservation confirmation to admin
110 5
        $this->adminMailer->send($reservation, $sameCount);
111
    }
112
113
    /**
114
     * Get all reservations.
115
     *
116
     * @return Collection
117
     */
118
    public function getReservations()
119
    {
120
        return $this->reservations->all();
121
    }
122
123
    /**
124
     * Get all active (not cancelled) reservations.
125
     *
126
     * @return Collection
127
     */
128
    public function getActiveReservations()
129
    {
130
        return $this->reservations->notCancelled()->get();
131
    }
132
133
    /**
134
     * Get all reserved time slots.
135
     *
136
     * @return array
137
     */
138
    public function getReservedDates()
139
    {
140
        $reservations = $this->getActiveReservations();
141
142
        return $this->datesResolver->getDatesFromReservations($reservations);
143
    }
144
145
    /**
146
     * Get all reservations by given date interval.
147
     *
148
     * @param Carbon $since Date from.
149
     * @param Carbon $till Date to.
150
     *
151
     * @return mixed
152
     */
153
    public function getReservationsByInterval(Carbon $since, Carbon $till)
154
    {
155
        return $this->reservations->whereBetween('date', [$since, $till])->get();
156
    }
157
158
    /**
159
     * Get reservations count by one email.
160
     *
161
     * @param $email
162
     *
163
     * @return int
164 5
     */
165
    public function getReservationsCountByMail($email)
166 5
    {
167
        return $this->reservations->where('email', $email)->notCancelled()->count();
168
    }
169
170
    /**
171
     * Is user returning or not? You have to set this parameter at Backend Reservations setting.
172
     *
173
     * @param $email
174
     *
175
     * @return bool
176 1
     */
177
    public function isUserReturning($email)
178
    {
179 1
        // when disabled, user is never returning
180 1
        $threshold = Settings::get('returning_mark', 0);
181
        if ($threshold < 1) {
182
            return false;
183
        }
184
185 1
        // load emails count
186
        if ($this->returningUsersCache === null) {
187 1
            $items = $this
188 1
                ->reservations
189 1
                ->select(DB::raw('email, count(*) as count'))
190 1
                ->groupBy('email')
191
                ->get();
192 1
            // refactor to mapWithKeys after upgrade to Laravel 5.3.
193 1
            foreach($items as $item) {
194
                $this->returningUsersCache[$item['email']] = $item['count'];
195
            }
196
        }
197 1
198 1
        $returning = $this->returningUsersCache;
199
        $actual = isset($returning[$email]) ? $returning[$email] : 0;
200 1
201
        return $threshold > 0 && $actual >= $threshold;
202
    }
203
204
    /**
205
     * Bulk reservation state change.
206
     *
207
     * @param array $ids
208
     * @param string $ident
209
     */
210
    public function bulkStateChange($ids, $ident)
211
    {
212
        // get state
213
        $status = $this->statuses->where('ident', $ident)->first();
214
        if (!$status) {
215
            return;
216
        }
217
218
        // go through reservations
219
        foreach ($ids as $id)
220
        {
221
            $reservation = $this->reservations->find($id);
222
            if (!$reservation) {
223
                continue;
224
            }
225
226
            $reservation->status = $status;
227
            $reservation->save();
228
        }
229
    }
230
231
    /**
232
     * Bulk reservations delete.
233
     *
234
     * @param array $ids
235
     */
236
    public function bulkDelete($ids)
237
    {
238
        // go through reservations
239
        foreach ($ids as $id)
240
        {
241
            $reservation = $this->reservations->find($id);
242
            if (!$reservation) {
243
                continue;
244
            }
245
246
            $reservation->delete();
247
        }
248
    }
249
250
    /**
251
     * Transform date and time to DateTime string.
252
     *
253
     * @param $data
254
     *
255
     * @return Carbon
256
     *
257
     * @throws ApplicationException
258 8
     */
259
    public function transformDateTime($data)
260
    {
261 8
        // validate date
262 1
        if (empty($data['date'])) {
263
            throw new ApplicationException(Lang::get('vojtasvoboda.reservations::lang.errors.empty_date'));
264
        }
265
266 7
        // validate time
267 1
        if (empty($data['time'])) {
268
            throw new ApplicationException(Lang::get('vojtasvoboda.reservations::lang.errors.empty_hour'));
269
        }
270 6
271
        $format = Settings::get(
272 6
            'formats_datetime',
273
            Config::get('vojtasvoboda.reservations::config.formats.datetime', 'd/m/Y H:i')
274
        );
275
276
        $dateTime = Carbon::createFromFormat($format, trim($data['date'] . ' ' . $data['time']));
277
278
        // validate date + time > current
279
        if ($dateTime->timestamp < Carbon::now()->timestamp) {
280
            throw new ApplicationException(Lang::get('vojtasvoboda.reservations::lang.errors.past_date'));
281
        }
282
283 11
        // validate days off
284
        if (!in_array($dateTime->dayOfWeek, $this->getWorkDays())) {
285
            throw new ApplicationException(Lang::get('vojtasvoboda.reservations::lang.errors.days_off'));
286 11
        }
287
288
        // validate out of hours
289 11
        $workTime = $this->getWorkTime();
290
        $timeToMinute = $dateTime->hour * 60 + $dateTime->minute;
291
        if (($timeToMinute < $workTime['from']['hour'] * 60 + $workTime['from']['minute'])
292 11
            || $timeToMinute > $workTime['to']['hour'] * 60 + $workTime['to']['minute']) {
293
            throw new ApplicationException(Lang::get('vojtasvoboda.reservations::lang.errors.out_of_hours'));
294
        }
295
296 11
        return $dateTime;
297
    }
298
299
    /**
300
     * Get work days of week
301
     *
302
     * @return array
303
     */
304 7
    public function getWorkDays()
305
    {
306 7
        $daysWorkInput = Settings::get('work_days', ['monday','tuesday','wednesday','thursday','friday']);
307 1
        $daysWork = [];
308
309 7
        foreach (['monday','tuesday','wednesday','thursday','friday','saturday','sunday'] as $index => $day) {
310
            if (in_array($day, $daysWorkInput)) {
311
                $daysWork[] = $index + 1;
312
            }
313
        }
314
315
        return $daysWork;
316 7
    }
317
318
    /**
319 7
     * Get work time of day
320 7
     *
321
     * @return array
322
     */
323 7
    public function getWorkTime()
324
    {
325 7
        $workTime = [];
326
327
        $work_time_from = explode(':', Settings::get('work_time_from', '10:00'));
328
        $workTime['from']['hour'] = (int)$work_time_from[0];
329
        $workTime['from']['minute'] = (isset($work_time_from[1]))?(int)$work_time_from[1]:0;
330
331
        $work_time_to = explode(':', Settings::get('work_time_to', '18:00'));
332
        $workTime['to']['hour'] = (int)$work_time_to[0];
333
        $workTime['to']['minute'] = (isset($work_time_to[1]))?(int)$work_time_to[1]:0;
334
335
        return $workTime;
336
    }
337
338
    /**
339
     * Returns if given date is available.
340
     *
341
     * @param Carbon $date
342
     * @param int $exceptId Except reservation ID.
343
     *
344
     * @return bool
345
     */
346
    public function isDateAvailable($date, $exceptId = null)
347
    {
348
        // get boundary dates for given reservation date
349
        $boundaries = $this->datesResolver->getBoundaryDates($date);
350
351
        // get all reservations in this date
352
        $query = $this->reservations->notCancelled()->whereBetween('date', $boundaries);
353
354
        // if updating reservation, we should skip existing reservation
355
        if ($exceptId !== null) {
356
            $query->where('id', '!=', $exceptId);
357
        }
358
359
        return $query->count() === 0;
360
    }
361
362
    /**
363
     * Check reservations amount limit per time.
364
     *
365
     * @throws ApplicationException
366
     */
367
    private function checkLimits()
368
    {
369
        if ($this->isCreatedWhileAgo()) {
370
            throw new ApplicationException(Lang::get('vojtasvoboda.reservations::lang.errors.please_wait'));
371
        }
372
    }
373
374
    /**
375
     * Try to find some reservation in less then given limit (default 30 seconds).
376
     *
377
     * @return boolean
378
     */
379
    public function isCreatedWhileAgo()
380
    {
381
        // protection time
382
        $time = Config::get('vojtasvoboda.reservations::config.protection_time', '-30 seconds');
383
        $timeLimit = Carbon::parse($time)->toDateTimeString();
384
385
        // try to find some message
386
        $item = $this->reservations->machine()->where('created_at', '>', $timeLimit)->first();
387
388
        return $item !== null;
389
    }
390
}
391