Completed
Push — master ( 707c53...e025a0 )
by Vojta
06:37
created

ReservationsFacade::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
rs 9.4285
ccs 7
cts 7
cp 1
cc 1
eloc 8
nc 1
nop 5
crap 1
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(ReservationsFacade::class);
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
     * ReservationsFacade constructor.
47
     *
48
     * @param Reservation $reservations
49
     * @param Status $statuses
50
     * @param DatesResolver $resolver
51
     * @param ReservationMailer $mailer
52
     * @param ReservationAdminMailer $adminMailer
53
     */
54 14
    public function __construct(
55
        Reservation $reservations, Status $statuses, DatesResolver $resolver,
56
        ReservationMailer $mailer, ReservationAdminMailer $adminMailer
57
    ) {
58 14
        $this->reservations = $reservations;
59 14
        $this->statuses = $statuses;
60 14
        $this->datesResolver = $resolver;
61 14
        $this->mailer = $mailer;
62 14
        $this->adminMailer = $adminMailer;
63 14
    }
64
65
    /**
66
     * Create and store reservation.
67
     *
68
     * @param array $data
69
     *
70
     * @return Reservation $reservation
71
     *
72
     * @throws ApplicationException
73
     * @throws ValidationException
74
     */
75 7
    public function storeReservation($data)
76
    {
77
        // check number of sends
78 7
        $this->checkLimits();
79
80
        // transform date and time to Carbon
81 7
        $data['date'] = $this->transformDateTime($data);
82
83
        // place for extending
84 5
        Event::fire('vojtasvoboda.reservations.processReservation', [&$data]);
85
86
        // create reservation
87 5
        $reservation = $this->reservations->create($data);
88
89
        // send mails to client and admin
90 5
        $this->sendMails($reservation);
91
92 5
        return $reservation;
93
    }
94
95
    /**
96
     * Send mail to client and admin.
97
     *
98
     * @param Reservation $reservation
99
     */
100 5
    public function sendMails($reservation)
101
    {
102
        // calculate reservations by same email
103 5
        $sameCount = $this->getReservationsCountByMail($reservation->email);
104
105
        // send reservation confirmation to customer
106 5
        $this->mailer->send($reservation, $sameCount);
107
108
        // send reservation confirmation to admin
109 5
        $this->adminMailer->send($reservation, $sameCount);
110 5
    }
111
112
    /**
113
     * Get all reservations.
114
     *
115
     * @return Collection
116
     */
117
    public function getReservations()
118
    {
119
        return $this->reservations->all();
120
    }
121
122
    /**
123
     * Get all active (not cancelled) reservations.
124
     *
125
     * @return Collection
126
     */
127
    public function getActiveReservations()
128
    {
129
        return $this->reservations->notCancelled()->get();
130
    }
131
132
    /**
133
     * Get all reserved time slots.
134
     *
135
     * @return array
136
     */
137
    public function getReservedDates()
138
    {
139
        $reservations = $this->getActiveReservations();
140
141
        return $this->datesResolver->getDatesFromReservations($reservations);
142
    }
143
144
    /**
145
     * Get all reservations by given date interval.
146
     *
147
     * @param Carbon $since Date from.
148
     * @param Carbon $till Date to.
149
     *
150
     * @return mixed
151
     */
152
    public function getReservationsByInterval(Carbon $since, Carbon $till)
153
    {
154
        return $this->reservations->whereBetween('date', [$since, $till])->get();
155
    }
156
157
    /**
158
     * Get reservations count by one email.
159
     *
160
     * @param $email
161
     *
162
     * @return int
163
     */
164 5
    public function getReservationsCountByMail($email)
165
    {
166 5
        return $this->reservations->where('email', $email)->notCancelled()->count();
167
    }
168
169
    /**
170
     * Is user returning or not? You have to set this parameter at Backend Reservations setting.
171
     *
172
     * @param $email
173
     *
174
     * @return bool
175
     */
176 1
    public function isUserReturning($email)
177
    {
178
        // when disabled, user is never returning
179 1
        $threshold = Settings::get('returning_mark', 0);
180 1
        if ($threshold < 1) {
181
            return false;
182
        }
183
184
        // load emails count
185 1
        if ($this->returningUsersCache === null) {
186
            $items = $this
187 1
                ->reservations
188 1
                ->select(DB::raw('email, count(*) as count'))
189 1
                ->groupBy('email')
190 1
                ->get();
191
            // refactor to mapWithKeys after upgrade to Laravel 5.3.
192 1
            foreach($items as $item) {
193 1
                $this->returningUsersCache[$item['email']] = $item['count'];
194
            }
195
        }
196
197 1
        $returning = $this->returningUsersCache;
198 1
        $actual = isset($returning[$email]) ? $returning[$email] : 0;
199
200 1
        return $threshold > 0 && $actual >= $threshold;
201
    }
202
203
    /**
204
     * Bulk reservation state change.
205
     *
206
     * @param array $ids
207
     * @param string $ident
208
     */
209
    public function bulkStateChange($ids, $ident)
210
    {
211
        // get state
212
        $status = $this->statuses->where('ident', $ident)->first();
213
        if (!$status) {
214
            return;
215
        }
216
217
        // go through reservations
218
        foreach ($ids as $id)
219
        {
220
            $reservation = $this->reservations->find($id);
221
            if (!$reservation) {
222
                continue;
223
            }
224
225
            $reservation->status = $status;
226
            $reservation->save();
227
        }
228
    }
229
230
    /**
231
     * Bulk reservations delete.
232
     *
233
     * @param array $ids
234
     */
235
    public function bulkDelete($ids)
236
    {
237
        // go through reservations
238
        foreach ($ids as $id)
239
        {
240
            $reservation = $this->reservations->find($id);
241
            if (!$reservation) {
242
                continue;
243
            }
244
245
            $reservation->delete();
246
        }
247
    }
248
249
    /**
250
     * Transform date and time to DateTime string.
251
     *
252
     * @param $data
253
     *
254
     * @return Carbon
255
     *
256
     * @throws ApplicationException
257
     */
258 8
    public function transformDateTime($data)
259
    {
260
        // validate date
261 8
        if (empty($data['date'])) {
262 1
            throw new ApplicationException('You have to select pickup date!');
263
        }
264
265
        // validate time
266 7
        if (empty($data['time'])) {
267 1
            throw new ApplicationException('You have to select pickup hour!');
268
        }
269
270 6
        $format = Config::get('vojtasvoboda.reservations::config.formats.datetime', 'd/m/Y H:i');
271
272 6
        return Carbon::createFromFormat($format, trim($data['date'] . ' ' . $data['time']));
273
    }
274
275
    /**
276
     * Returns if given date is available.
277
     *
278
     * @param Carbon $date
279
     * @param int $exceptId Except reservation ID.
280
     *
281
     * @return bool
282
     */
283 11
    public function isDateAvailable($date, $exceptId = null)
284
    {
285
        // get boundary dates for given reservation date
286 11
        $boundaries = $this->datesResolver->getBoundaryDates($date);
287
288
        // get all reservations in this date
289 11
        $query = $this->reservations->notCancelled()->whereBetween('date', $boundaries);
290
291
        // if updating reservation, we should skip existing reservation
292 11
        if ($exceptId !== null) {
293
            $query->where('id', '!=', $exceptId);
294
        }
295
296 11
        return $query->count() === 0;
297
    }
298
299
    /**
300
     * Check reservations amount limit per time.
301
     *
302
     * @throws ApplicationException
303
     */
304 7
    private function checkLimits()
305
    {
306 7
        if ($this->isCreatedWhileAgo()) {
307 1
            throw new ApplicationException('You can sent only one reservation per 30 seconds, please wait a second.');
308
        }
309 7
    }
310
311
    /**
312
     * Try to find some reservation in less then given limit (default 30 seconds).
313
     *
314
     * @return boolean
315
     */
316 7
    public function isCreatedWhileAgo()
317
    {
318
        // protection time
319 7
        $time = Config::get('vojtasvoboda.reservations::config.protection_time', '-30 seconds');
320 7
        $timeLimit = Carbon::parse($time)->toDateTimeString();
321
322
        // try to find some message
323 7
        $item = $this->reservations->machine()->where('created_at', '>', $timeLimit)->first();
324
325 7
        return $item !== null;
326
    }
327
}
328