DatabaseDoorsRepository.php 5.8 KB
Newer Older
1
2
3
4
5
<?php


namespace Source\Gateways\Doors;

6
use Carbon\Carbon;
7
use Source\Entities\Door;
Jacob Priddy's avatar
Jacob Priddy committed
8
use Source\Sanitize\CastsTo;
9
use Illuminate\Support\Facades\Log;
10
use Source\Entities\HashedSearchable;
11
use Source\Gateways\PostgresSQLCodes;
12
use Illuminate\Database\QueryException;
13
use Illuminate\Database\Eloquent\Builder;
Jacob Priddy's avatar
Jacob Priddy committed
14
use Source\Exceptions\EntityExistsException;
Jacob Priddy's avatar
Jacob Priddy committed
15
use Source\Exceptions\EntityNotFoundException;
16

Jacob Priddy's avatar
Jacob Priddy committed
17
18
class DatabaseDoorsRepository implements DoorsRepository
{
Jacob Priddy's avatar
Jacob Priddy committed
19
20
    use CastsTo;

21
    /**
22
23
24
     * @param \App\Door             $dbDoor
     * @param \Source\Entities\Door $door
     * @throws \Source\Exceptions\EntityExistsException
25
     */
26
    protected function saveDoor(\App\Door $dbDoor, Door $door): void
Jacob Priddy's avatar
Jacob Priddy committed
27
    {
28
29
30
        $dbDoor->setAttribute('location', $door->getLocation());
        $dbDoor->setAttribute('name', $door->getName());
        $dbDoor->setAttribute('api_token', $door->getToken()->getHash());
31
32
        $dbDoor->setAttribute('version', $door->getVersion());
        $dbDoor->setAttribute('last_seen_at', $door->getLastSeenAt());
33
34
        $dbDoor->setAttribute('public', $door->isPublic());
        $dbDoor->setAttribute('notes', $door->getNotes());
Jacob Priddy's avatar
Jacob Priddy committed
35

36
37
38
        try {
            $dbDoor->save();
        } catch (QueryException $e) {
39
40
            if ($e->getCode() === PostgresSQLCodes::UNIQUE_CONSTRAINT_VIOLATION) {
                throw new EntityExistsException('Cannot save door. Door already exists.');
41
42
            }

43
            Log::error('Failed saving door: ' . $e);
44
45

            throw $e;
Jacob Priddy's avatar
Jacob Priddy committed
46
        }
47
48
49
50
51
52
53
54
55
56
    }

    /**
     * @inheritDoc
     */
    public function create(Door $door): Door
    {
        $dbDoor = new \App\Door();

        $this->saveDoor($dbDoor, $door);
57

Jacob Priddy's avatar
Jacob Priddy committed
58
        return self::makeDoorFromDb($dbDoor);
59
60
    }

Jacob Priddy's avatar
Jacob Priddy committed
61
62
63
    public static function makeDoorFromDb(\App\Door $door): Door
    {
        return new Door(
64
65
66
67
            $door->getAttribute('id'),
            $door->getAttribute('location'),
            $door->getAttribute('name'),
            new HashedSearchable($door->getAttribute('api_token')),
68
69
            $door->getAttribute('public'),
            $door->getAttribute('notes'),
70
            $door->getAttribute('version'),
71
72
73
            $door->getAttribute('created_at'),
            $door->getAttribute('updated_at'),
            $door->getAttribute('last_seen_at')
Jacob Priddy's avatar
Jacob Priddy committed
74
75
76
        );
    }

77
78
79
    /**
     * @inheritDoc
     */
80
    public function getByToken(?HashedSearchable $token): ?Door
Jacob Priddy's avatar
Jacob Priddy committed
81
    {
Jacob Priddy's avatar
Jacob Priddy committed
82
83
84
85
        if (!$token) {
            return null;
        }

86
87
        /** @var \App\Door|null $door */
        $door = \App\Door::query()->where('api_token', $token->getHash())->first();
88

89
        if (!$door) {
90
91
92
            return null;
        }

93
        return self::makeDoorFromDb($door);
94
    }
95

96
97
98
99
100
101
102
103
104
105
106
107
108
109
    /**
     * @inheritDoc
     */
    public function touch(string $doorId, Carbon $seen, ?string $version): void
    {
        $updated = [
            'last_seen_at' => $seen,
        ];

        // If we have a new version to set, set it.
        if ($version) {
            $updated['version'] = $version;
        }

110
        \App\Door::query()->where('id', self::castToInt($doorId))->update($updated);
111
112
    }

113
114
115
116
117
    /**
     * @inheritDoc
     */
    public function get(string $doorId): ?Door
    {
118
        /** @var \App\Door|null $door */
119
        $door = \App\Door::query()->find(self::castToInt($doorId));
120
        if (!$door) {
Jacob Priddy's avatar
Jacob Priddy committed
121
122
123
124
125
126
127
128
129
            return null;
        }

        return self::makeDoorFromDb($door);
    }

    /**
     * @inheritDoc
     */
130
    public function search(?string $query = null, ?int $seenSinceSeconds = null, ?int $awolForSeconds = null): array
Jacob Priddy's avatar
Jacob Priddy committed
131
    {
Jacob Priddy's avatar
Jacob Priddy committed
132
        $doors = \App\Door::query()->orderBy('location');
133
134
135
136

        if ($seenSinceSeconds > 0) {
            $doors->whereNotNull('last_seen_at')
                ->where('last_seen_at', '>=', Carbon::now()->subRealSeconds($seenSinceSeconds));
137
        }
Jacob Priddy's avatar
Jacob Priddy committed
138

139
140
141
142
143
144
145
146
        if ($awolForSeconds > 0) {
            $doors->whereNotNull('last_seen_at')
                ->where('last_seen_at', '<', Carbon::now()->subRealSeconds($awolForSeconds));
        }

        if ($query) {
            $doors->where(static function (Builder $builder) use ($query) {
                $builder->where('location', 'ILIKE', "%$query%")
Jacob Priddy's avatar
Jacob Priddy committed
147
                    ->orWhere('id', '=', self::castToInt($query))
148
149
150
151
152
                    ->orWhere('name', 'ILIKE', "%$query%")
                    ->orWhere('version', 'ILIKE', "%$query%");
            });
        }

Jacob Priddy's avatar
Jacob Priddy committed
153
        return array_map(fn (\App\Door $door): Door => self::makeDoorFromDb($door), $doors->get()->values()->all());
154
    }
Jacob Priddy's avatar
Jacob Priddy committed
155
156
157
158
159
160
161
162
163
164

    /**
     * @inheritDoc
     */
    public function findByName(?string $name): ?Door
    {
        if (!$name) {
            return null;
        }

165
166
167
168
169
170
171
172
        /** @var \App\Door|null $door */
        $door = \App\Door::query()->where('name', $name)->first();

        if (!$door) {
            return null;
        }

        return self::makeDoorFromDb($door);
Jacob Priddy's avatar
Jacob Priddy committed
173
    }
174
175
176
177

    /**
     * @inheritDoc
     */
Jacob Priddy's avatar
Jacob Priddy committed
178
    public function update(string $doorId, Door $door): Door
179
    {
180
        /** @var \App\Door|null $dbDoor */
181
        $dbDoor = \App\Door::query()->find(self::castToInt($doorId));
182
183

        if (!$dbDoor) {
Jacob Priddy's avatar
Jacob Priddy committed
184
            throw new EntityNotFoundException('Could not update door. Door not found.');
185
186
        }

187
        $this->saveDoor($dbDoor, $door);
188
189
190

        return self::makeDoorFromDb($dbDoor);
    }
Jacob Priddy's avatar
Jacob Priddy committed
191
192
193
194

    /**
     * @inheritDoc
     */
Jacob Priddy's avatar
Jacob Priddy committed
195
    public function delete(string $doorId): int
Jacob Priddy's avatar
Jacob Priddy committed
196
    {
197
        return \App\Door::destroy(self::castToInt($doorId));
Jacob Priddy's avatar
Jacob Priddy committed
198
    }
199
200
201
202
203
204
205
206

    /**
     * @inheritDoc
     */
    public function exists(string $doorId): bool
    {
        return (bool)$this->get($doorId);
    }
207
208
209
210
211
212
213
214
215
216

    /**
     * @inheritDoc
     */
    public function searchByLocation(string $location): array
    {
        $doors = \App\Door::query()->where('location', '=', $location);

        return array_map(fn (\App\Door $door): Door => self::makeDoorFromDb($door), $doors->get()->values()->all());
    }
217
}