Completed
Pull Request — master (#20)
by
unknown
08:38
created

ReservationsFacade::getReservedDates()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 6
rs 9.4285
ccs 0
cts 2
cp 0
cc 1
eloc 3
nc 1
nop 0
crap 2
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
        //convert hour and minutes to minutes
291
        $timeToMinute = $dateTime->hour * 60 + $dateTime->minute;
292 11
        $workTimeFrom = $workTime['from']['hour'] * 60 + $workTime['from']['minute'];
293
        $workTimeTo   = $workTime['to']['hour'] * 60 + $workTime['to']['minute'];
294
295
        if ($timeToMinute < $workTimeFrom || $timeToMinute > $workTimeTo) {
296 11
            throw new ApplicationException(Lang::get('vojtasvoboda.reservations::lang.errors.out_of_hours'));
297
        }
298
299
        return $dateTime;
300
    }
301
302
    /**
303
     * Get work days of week
304 7
     *
305
     * @return array
306 7
     */
307 1
    public function getWorkDays()
308
    {
309 7
        $daysWorkInput = Settings::get('work_days', ['monday','tuesday','wednesday','thursday','friday']);
310
        $daysWork = [];
311
312
        foreach (['monday','tuesday','wednesday','thursday','friday','saturday','sunday'] as $index => $day) {
313
            if (in_array($day, $daysWorkInput)) {
314
                $daysWork[] = $index + 1;
315
            }
316 7
        }
317
318
        return $daysWork;
319 7
    }
320 7
321
    /**
322
     * Get work time of day
323 7
     *
324
     * @return array
325 7
     */
326
    public function getWorkTime()
327
    {
328
        $workTime = [];
329
330
        $work_time_from = explode(':', Settings::get('work_time_from', '10:00'));
331
        $workTime['from']['hour'] = (int)$work_time_from[0];
332
        $workTime['from']['minute'] = (isset($work_time_from[1]))?(int)$work_time_from[1]:0;
333
334
        $work_time_to = explode(':', Settings::get('work_time_to', '18:00'));
335
        $workTime['to']['hour'] = (int)$work_time_to[0];
336
        $workTime['to']['minute'] = (isset($work_time_to[1]))?(int)$work_time_to[1]:0;
337
338
        return $workTime;
339
    }
340
341
    /**
342
     * Returns if given date is available.
343
     *
344
     * @param Carbon $date
345
     * @param int $exceptId Except reservation ID.
346
     *
347
     * @return bool
348
     */
349
    public function isDateAvailable($date, $exceptId = null)
350
    {
351
        // get boundary dates for given reservation date
352
        $boundaries = $this->datesResolver->getBoundaryDates($date);
353
354
        // get all reservations in this date
355
        $query = $this->reservations->notCancelled()->whereBetween('date', $boundaries);
356
357
        // if updating reservation, we should skip existing reservation
358
        if ($exceptId !== null) {
359
            $query->where('id', '!=', $exceptId);
360
        }
361
362
        return $query->count() === 0;
363
    }
364
365
    /**
366
     * Check reservations amount limit per time.
367
     *
368
     * @throws ApplicationException
369
     */
370
    private function checkLimits()
371
    {
372
        if ($this->isCreatedWhileAgo()) {
373
            throw new ApplicationException(Lang::get('vojtasvoboda.reservations::lang.errors.please_wait'));
374
        }
375
    }
376
377
    /**
378
     * Try to find some reservation in less then given limit (default 30 seconds).
379
     *
380
     * @return boolean
381
     */
382
    public function isCreatedWhileAgo()
383
    {
384
        // protection time
385
        $time = Config::get('vojtasvoboda.reservations::config.protection_time', '-30 seconds');
386
        $timeLimit = Carbon::parse($time)->toDateTimeString();
387
388
        // try to find some message
389
        $item = $this->reservations->machine()->where('created_at', '>', $timeLimit)->first();
390
391
        return $item !== null;
392
    }
393
}
394