1
|
|
|
<?php namespace VojtaSvoboda\Reservations\Facades; |
2
|
|
|
|
3
|
|
|
use Auth; |
4
|
|
|
use Carbon\Carbon; |
5
|
|
|
use Config; |
6
|
|
|
use Event; |
7
|
|
|
use Illuminate\Database\Eloquent\Collection; |
8
|
|
|
use Illuminate\Support\Facades\DB; |
9
|
|
|
use October\Rain\Exception\ApplicationException; |
10
|
|
|
use October\Rain\Exception\ValidationException; |
11
|
|
|
use VojtaSvoboda\Reservations\Classes\DatesResolver; |
12
|
|
|
use VojtaSvoboda\Reservations\Mailers\ReservationAdminMailer; |
13
|
|
|
use VojtaSvoboda\Reservations\Mailers\ReservationMailer; |
14
|
|
|
use VojtaSvoboda\Reservations\Models\Reservation; |
15
|
|
|
use VojtaSvoboda\Reservations\Models\Settings; |
16
|
|
|
use VojtaSvoboda\Reservations\Models\Status; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Public reservations facade. |
20
|
|
|
* |
21
|
|
|
* Usage: App::make('vojtasvoboda.reservations.facade'); |
22
|
|
|
* |
23
|
|
|
* @package VojtaSvoboda\Reservations\Facades |
24
|
|
|
*/ |
25
|
|
|
class ReservationsFacade |
26
|
|
|
{ |
27
|
|
|
/** @var Reservation $reservations */ |
28
|
|
|
private $reservations; |
29
|
|
|
|
30
|
|
|
/** @var Status $statuses */ |
31
|
|
|
private $statuses; |
32
|
|
|
|
33
|
|
|
/** @var DatesResolver $datesResolver */ |
34
|
|
|
private $datesResolver; |
35
|
|
|
|
36
|
|
|
/** @var array $returningUsersCache */ |
37
|
|
|
private $returningUsersCache; |
38
|
|
|
|
39
|
|
|
/** @var ReservationMailer $mailer */ |
40
|
|
|
private $mailer; |
41
|
|
|
|
42
|
|
|
/** @var ReservationAdminMailer $adminMailer */ |
43
|
|
|
private $adminMailer; |
44
|
|
|
|
45
|
|
|
/** |
46
|
9 |
|
* ReservationsFacade constructor. |
47
|
|
|
* |
48
|
9 |
|
* @param Reservation $reservations |
49
|
9 |
|
* @param Status $statuses |
50
|
9 |
|
* @param DatesResolver $resolver |
51
|
9 |
|
* @param ReservationMailer $mailer |
52
|
|
|
* @param ReservationAdminMailer $adminMailer |
53
|
|
|
*/ |
54
|
|
|
public function __construct( |
55
|
|
|
Reservation $reservations, Status $statuses, DatesResolver $resolver, |
56
|
|
|
ReservationMailer $mailer, ReservationAdminMailer $adminMailer |
57
|
|
|
) { |
58
|
|
|
$this->reservations = $reservations; |
59
|
|
|
$this->statuses = $statuses; |
60
|
|
|
$this->datesResolver = $resolver; |
61
|
|
|
$this->mailer = $mailer; |
62
|
|
|
$this->adminMailer = $adminMailer; |
63
|
8 |
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
8 |
|
* Create and store reservation. |
67
|
|
|
* |
68
|
|
|
* @param array $data |
69
|
6 |
|
* |
70
|
|
|
* @return Reservation $reservation |
71
|
|
|
* |
72
|
6 |
|
* @throws ApplicationException |
73
|
|
|
* @throws ValidationException |
74
|
|
|
*/ |
75
|
6 |
|
public function storeReservation($data) |
76
|
|
|
{ |
77
|
|
|
// transform date and time to Carbon |
78
|
6 |
|
$data['date'] = $this->transformDateTime($data); |
79
|
|
|
|
80
|
6 |
|
// check date availability |
81
|
|
|
$this->checkDate($data); |
82
|
|
|
|
83
|
|
|
// create reservation |
84
|
|
|
$reservation = $this->reservations->create($data); |
85
|
|
|
|
86
|
|
|
// calculate reservations by same email |
87
|
|
|
$sameCount = $this->getReservationsWithSameEmailCount($reservation->email); |
88
|
|
|
|
89
|
|
|
// send reservation confirmation to customer |
90
|
|
|
$this->mailer->send($reservation, $sameCount); |
91
|
|
|
|
92
|
|
|
// send reservation confirmation to admin |
93
|
|
|
$this->adminMailer->send($reservation, $sameCount); |
94
|
|
|
|
95
|
|
|
return $reservation; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Get all reservations. |
100
|
|
|
* |
101
|
|
|
* @return Collection |
102
|
|
|
*/ |
103
|
|
|
public function getReservations() |
104
|
|
|
{ |
105
|
|
|
return $this->reservations->all(); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Get all active (not cancelled) reservations. |
110
|
|
|
* |
111
|
|
|
* @return Collection |
112
|
|
|
*/ |
113
|
|
|
public function getActiveReservations() |
114
|
|
|
{ |
115
|
|
|
return $this->reservations->notCancelled()->get(); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Get all reserved time slots. |
120
|
|
|
* |
121
|
|
|
* @return array |
122
|
|
|
*/ |
123
|
|
|
public function getReservedDates() |
124
|
|
|
{ |
125
|
|
|
$reservations = $this->getActiveReservations(); |
126
|
|
|
|
127
|
|
|
return $this->datesResolver->getDatesFromReservations($reservations); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Get all reservations by given date interval. |
132
|
|
|
* |
133
|
|
|
* @param Carbon $since Date from. |
134
|
|
|
* @param Carbon $till Date to. |
135
|
1 |
|
* |
136
|
|
|
* @return mixed |
137
|
1 |
|
*/ |
138
|
|
|
public function getReservationsByInterval(Carbon $since, Carbon $till) |
139
|
|
|
{ |
140
|
|
|
return $this->reservations->whereBetween('date', [$since, $till])->get(); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Get reservations count by one email. |
145
|
|
|
* |
146
|
|
|
* @param $email |
147
|
1 |
|
* |
148
|
|
|
* @return int |
149
|
|
|
*/ |
150
|
1 |
|
public function getReservationsWithSameEmailCount($email) |
151
|
1 |
|
{ |
152
|
|
|
return $this->reservations->where('email', $email)->notCancelled()->count(); |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
1 |
|
* Is user returning or not? You have to set this parameter at Backend Reservations setting. |
157
|
|
|
* |
158
|
1 |
|
* @param $email |
159
|
1 |
|
* |
160
|
1 |
|
* @return bool |
161
|
1 |
|
*/ |
162
|
|
|
public function isUserReturning($email) |
163
|
1 |
|
{ |
164
|
1 |
|
// if disabled, return always false |
|
|
|
|
165
|
|
|
$threshold = Settings::get('returning_mark', 0); |
166
|
|
|
if ($threshold < 1) { |
167
|
|
|
return false; |
168
|
1 |
|
} |
169
|
1 |
|
|
170
|
|
|
// load emails count |
171
|
1 |
|
if ($this->returningUsersCache === null) { |
172
|
|
|
$items = $this |
173
|
|
|
->reservations |
174
|
|
|
->select(DB::raw('email, count(*) as count')) |
175
|
|
|
->groupBy('email') |
176
|
|
|
->get(); |
177
|
|
|
// refactor to mapWithKeys after upgrade to Laravel 5.3. |
178
|
|
|
foreach($items as $item) { |
179
|
|
|
$this->returningUsersCache[$item['email']] = $item['count']; |
180
|
|
|
} |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
$returning = $this->returningUsersCache; |
184
|
|
|
$actual = isset($returning[$email]) ? $returning[$email] : 0; |
185
|
|
|
|
186
|
|
|
return $threshold > 0 && $actual >= $threshold; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* Bulk reservation state change. |
191
|
|
|
* |
192
|
|
|
* @param array $ids |
193
|
|
|
* @param string $ident |
194
|
|
|
*/ |
195
|
|
|
public function bulkStateChange($ids, $ident) |
196
|
|
|
{ |
197
|
|
|
// get state |
198
|
|
|
$status = $this->statuses->where('ident', $ident)->first(); |
199
|
|
|
if (!$status) { |
200
|
|
|
return; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
// go through reservations |
204
|
|
|
foreach ($ids as $id) |
205
|
|
|
{ |
206
|
|
|
$reservation = $this->reservations->find($id); |
207
|
|
|
if (!$reservation) { |
208
|
|
|
continue; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
$reservation->status = $status; |
212
|
|
|
$reservation->save(); |
213
|
|
|
} |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* Bulk reservations delete. |
218
|
|
|
* |
219
|
|
|
* @param array $ids |
220
|
|
|
*/ |
221
|
|
|
public function bulkDelete($ids) |
222
|
|
|
{ |
223
|
|
|
// go through reservations |
224
|
|
|
foreach ($ids as $id) |
225
|
|
|
{ |
226
|
|
|
$reservation = $this->reservations->find($id); |
227
|
|
|
if (!$reservation) { |
228
|
|
|
continue; |
229
|
9 |
|
} |
230
|
|
|
|
231
|
|
|
$reservation->delete(); |
232
|
9 |
|
} |
233
|
1 |
|
} |
234
|
|
|
|
235
|
8 |
|
/** |
236
|
1 |
|
* Transform date and time to DateTime string. |
237
|
|
|
* |
238
|
|
|
* @param $data |
239
|
7 |
|
* |
240
|
|
|
* @return Carbon |
241
|
7 |
|
* |
242
|
|
|
* @throws ApplicationException |
243
|
|
|
*/ |
244
|
|
|
public function transformDateTime($data) |
245
|
|
|
{ |
246
|
|
|
// validate date and time |
247
|
|
|
if (empty($data['date'])) { |
248
|
|
|
throw new ApplicationException('You have to select pickup date!'); |
249
|
|
|
|
250
|
|
|
} elseif (empty($data['time'])) { |
251
|
6 |
|
throw new ApplicationException('You have to select pickup hour!'); |
252
|
|
|
} |
253
|
|
|
|
254
|
6 |
|
$format = Config::get('vojtasvoboda.reservations::config.formats.datetime', 'd/m/Y H:i'); |
255
|
|
|
|
256
|
|
|
return Carbon::createFromFormat($format, trim($data['date'] . ' ' . $data['time'])); |
257
|
|
|
} |
258
|
|
|
|
259
|
6 |
|
/** |
260
|
1 |
|
* Check gived date and time. |
261
|
|
|
* |
262
|
6 |
|
* @param array $data |
263
|
|
|
* |
264
|
|
|
* @throws ApplicationException |
265
|
|
|
*/ |
266
|
|
|
public function checkDate($data) |
267
|
|
|
{ |
268
|
|
|
// check reservation sent limit |
269
|
|
|
if ($this->isSomeReservationExistsInLastTime()) { |
270
|
|
|
throw new ApplicationException('You can sent only one reservation per 30 seconds, please wait a second.'); |
271
|
6 |
|
} |
272
|
|
|
|
273
|
|
|
// check date availability |
274
|
6 |
|
if (!$this->isDateAvailable($data['date'])) { |
275
|
|
|
throw new ApplicationException($data['date']->format('d.m.Y H:i') . ' is already booked.'); |
276
|
|
|
} |
277
|
6 |
|
} |
278
|
6 |
|
|
279
|
6 |
|
/** |
280
|
|
|
* Returns if date is available to book. |
281
|
|
|
* |
282
|
6 |
|
* @param Carbon $date |
283
|
6 |
|
* |
284
|
6 |
|
* @return bool |
285
|
|
|
*/ |
286
|
|
|
public function isDateAvailable(Carbon $date) |
287
|
6 |
|
{ |
288
|
|
|
// get config |
289
|
6 |
|
$length = Config::get('vojtasvoboda.reservations::config.reservation.length', '2 hours'); |
290
|
|
|
|
291
|
|
|
// check time slot before |
292
|
|
|
$startDatetime = clone $date; |
293
|
|
|
$startDatetime->modify('-' . $length); |
294
|
|
|
$startDatetime->modify('+1 second'); |
295
|
|
|
|
296
|
|
|
// check time slot after |
297
|
6 |
|
$endDatetime = clone $date; |
298
|
|
|
$endDatetime->modify('+' . $length); |
299
|
6 |
|
$endDatetime->modify('-1 second'); |
300
|
|
|
|
301
|
|
|
// get all reservations in this date |
302
|
|
|
$reservations = $this->reservations->notCancelled()->whereBetween('date', [$startDatetime, $endDatetime])->get(); |
303
|
|
|
|
304
|
|
|
return $reservations->count() == 0; |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* Try to find some reservation in less then half minute. |
309
|
|
|
* |
310
|
|
|
* @return boolean |
311
|
|
|
*/ |
312
|
|
|
public function isSomeReservationExistsInLastTime() |
313
|
|
|
{ |
314
|
|
|
return $this->reservations->isExistInLastTime(); |
315
|
|
|
} |
316
|
|
|
} |
317
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.