Passed
Push — master ( cd4631...f63432 )
by Peter
02:15
created

UserSqlDataMapper::getByClientId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
c 0
b 0
f 0
dl 0
loc 16
rs 9.9332
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AbterPhp\Admin\Orm\DataMappers;
6
7
use AbterPhp\Admin\Domain\Entities\User as Entity;
8
use AbterPhp\Admin\Domain\Entities\UserGroup;
9
use AbterPhp\Admin\Domain\Entities\UserLanguage;
10
use AbterPhp\Framework\Orm\DataMappers\IdGeneratorUserTrait;
11
use Opulence\Orm\DataMappers\SqlDataMapper;
12
use Opulence\QueryBuilders\MySql\QueryBuilder;
13
use Opulence\QueryBuilders\MySql\SelectQuery;
14
15
class UserSqlDataMapper extends SqlDataMapper implements IUserDataMapper
16
{
17
    use IdGeneratorUserTrait;
18
19
    /**
20
     * @param Entity $entity
21
     */
22
    public function add($entity)
23
    {
24
        if (!($entity instanceof Entity)) {
0 ignored issues
show
introduced by
$entity is always a sub-type of AbterPhp\Admin\Domain\Entities\User.
Loading history...
25
            throw new \InvalidArgumentException(__CLASS__ . ':' . __FUNCTION__ . ' expects a User entity.');
26
        }
27
28
        $query = (new QueryBuilder())
29
            ->insert(
30
                'users',
31
                [
32
                    'id'                  => [$entity->getId(), \PDO::PARAM_STR],
33
                    'username'            => [$entity->getUsername(), \PDO::PARAM_STR],
34
                    'email'               => [$entity->getEmail(), \PDO::PARAM_STR],
35
                    'password'            => [$entity->getPassword(), \PDO::PARAM_STR],
36
                    'user_language_id'    => [$entity->getUserLanguage()->getId(), \PDO::PARAM_STR],
37
                    'can_login'           => [$entity->canLogin(), \PDO::PARAM_INT],
38
                    'is_gravatar_allowed' => [$entity->isGravatarAllowed(), \PDO::PARAM_INT],
39
                ]
40
            );
41
42
        $sql    = $query->getSql();
43
        $params = $query->getParameters();
44
45
        $statement = $this->writeConnection->prepare($sql);
46
        $statement->bindValues($params);
47
        $statement->execute();
48
49
        $this->addUserGroups($entity);
50
    }
51
52
    /**
53
     * @param Entity $entity
54
     */
55
    public function delete($entity)
56
    {
57
        if (!($entity instanceof Entity)) {
0 ignored issues
show
introduced by
$entity is always a sub-type of AbterPhp\Admin\Domain\Entities\User.
Loading history...
58
            throw new \InvalidArgumentException(__CLASS__ . ':' . __FUNCTION__ . ' expects a User entity.');
59
        }
60
61
        $rand     = rand(0, PHP_INT_MAX);
62
        $username = sprintf('deleted-%d', $rand);
63
64
        $this->deleteUserGroups($entity);
65
66
        $query = (new QueryBuilder())
67
            ->update(
68
                'users',
69
                'users',
70
                [
71
                    'deleted'  => [1, \PDO::PARAM_INT],
72
                    'email'    => [sprintf('%[email protected]', $username), \PDO::PARAM_STR],
73
                    'username' => [$username, \PDO::PARAM_STR],
74
                    'password' => ['', \PDO::PARAM_STR],
75
                ]
76
            )
77
            ->where('id = ?')
78
            ->addUnnamedPlaceholderValue($entity->getId(), \PDO::PARAM_STR);
79
80
        $sql    = $query->getSql();
81
        $params = $query->getParameters();
82
83
        $statement = $this->writeConnection->prepare($sql);
84
        $statement->bindValues($params);
85
        $statement->execute();
86
    }
87
88
    /**
89
     * @return Entity[]
90
     */
91
    public function getAll(): array
92
    {
93
        $query = $this->getBaseQuery();
94
95
        $sql = $query->getSql();
96
97
        return $this->read($sql, [], self::VALUE_TYPE_ARRAY);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->read($sql,...self::VALUE_TYPE_ARRAY) could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
98
    }
99
100
    /**
101
     * @param int      $limitFrom
102
     * @param int      $pageSize
103
     * @param string[] $orders
104
     * @param array    $conditions
105
     * @param array    $params
106
     *
107
     * @return Entity[]
108
     */
109
    public function getPage(int $limitFrom, int $pageSize, array $orders, array $conditions, array $params): array
110
    {
111
        $query = $this->getBaseQuery()
112
            ->limit($pageSize)
113
            ->offset($limitFrom);
114
115
        foreach ($orders as $order) {
116
            $query->addOrderBy($order);
117
        }
118
119
        foreach ($conditions as $condition) {
120
            $query->andWhere($condition);
121
        }
122
123
        $replaceCount = 1;
124
125
        $sql = $query->getSql();
126
        $sql = str_replace('SELECT', 'SELECT SQL_CALC_FOUND_ROWS', $sql, $replaceCount);
127
128
        return $this->read($sql, $params, self::VALUE_TYPE_ARRAY);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->read($sql,...self::VALUE_TYPE_ARRAY) could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
129
    }
130
131
    /**
132
     * @param int|string $id
133
     *
134
     * @return Entity|null
135
     */
136
    public function getById($id): ?Entity
137
    {
138
        $query = $this->getBaseQuery()->andWhere('users.id = :user_id');
139
140
        $parameters = ['user_id' => [$id, \PDO::PARAM_STR]];
141
142
        return $this->read($query->getSql(), $parameters, self::VALUE_TYPE_ENTITY, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->read($quer...ALUE_TYPE_ENTITY, true) could return the type array which is incompatible with the type-hinted return AbterPhp\Admin\Domain\Entities\User|null. Consider adding an additional type-check to rule them out.
Loading history...
143
    }
144
145
    /**
146
     * @param string $identifier
147
     *
148
     * @return Entity|null
149
     */
150
    public function find(string $identifier): ?Entity
151
    {
152
        $query = $this->getBaseQuery()->andWhere('(username = :identifier OR email = :identifier)');
153
154
        $sql    = $query->getSql();
155
        $params = [
156
            'identifier' => [$identifier, \PDO::PARAM_STR],
157
        ];
158
159
        return $this->read($sql, $params, self::VALUE_TYPE_ENTITY);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->read($sql,...elf::VALUE_TYPE_ENTITY) could return the type array which is incompatible with the type-hinted return AbterPhp\Admin\Domain\Entities\User|null. Consider adding an additional type-check to rule them out.
Loading history...
160
    }
161
162
    /**
163
     * @param string $clientId
164
     *
165
     * @return Entity|null
166
     */
167
    public function getByClientId(string $clientId): ?Entity
168
    {
169
        $query = $this->getBaseQuery()
170
            ->innerJoin(
171
                'api_clients',
172
                'ac',
173
                'ac.user_id = users.id AND ac.deleted = 0'
174
            )
175
            ->andWhere('ac.id = :client_id');
176
177
        $sql    = $query->getSql();
178
        $params = [
179
            'client_id' => [$clientId, \PDO::PARAM_STR],
180
        ];
181
182
        return $this->read($sql, $params, self::VALUE_TYPE_ENTITY, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->read($sql,...ALUE_TYPE_ENTITY, true) could return the type array which is incompatible with the type-hinted return AbterPhp\Admin\Domain\Entities\User|null. Consider adding an additional type-check to rule them out.
Loading history...
183
    }
184
185
    /**
186
     * @param string $username
187
     *
188
     * @return Entity|null
189
     */
190
    public function getByUsername(string $username): ?Entity
191
    {
192
        $query = $this->getBaseQuery()->andWhere('`username` = :username');
193
194
        $sql    = $query->getSql();
195
        $params = [
196
            'username' => [$username, \PDO::PARAM_STR],
197
        ];
198
199
        return $this->read($sql, $params, self::VALUE_TYPE_ENTITY, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->read($sql,...ALUE_TYPE_ENTITY, true) could return the type array which is incompatible with the type-hinted return AbterPhp\Admin\Domain\Entities\User|null. Consider adding an additional type-check to rule them out.
Loading history...
200
    }
201
202
    /**
203
     * @param string $email
204
     *
205
     * @return Entity|null
206
     */
207
    public function getByEmail(string $email): ?Entity
208
    {
209
        $query = $this->getBaseQuery()->andWhere('email = :email');
210
211
        $sql    = $query->getSql();
212
        $params = [
213
            'email' => [$email, \PDO::PARAM_STR],
214
        ];
215
216
        return $this->read($sql, $params, self::VALUE_TYPE_ENTITY, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->read($sql,...ALUE_TYPE_ENTITY, true) could return the type array which is incompatible with the type-hinted return AbterPhp\Admin\Domain\Entities\User|null. Consider adding an additional type-check to rule them out.
Loading history...
217
    }
218
219
    /**
220
     * @param Entity $entity
221
     */
222
    public function update($entity)
223
    {
224
        if (!($entity instanceof Entity)) {
0 ignored issues
show
introduced by
$entity is always a sub-type of AbterPhp\Admin\Domain\Entities\User.
Loading history...
225
            throw new \InvalidArgumentException(__CLASS__ . ':' . __FUNCTION__ . ' expects a User entity.');
226
        }
227
228
        $query = (new QueryBuilder())
229
            ->update(
230
                'users',
231
                'users',
232
                [
233
                    'username'            => [$entity->getUsername(), \PDO::PARAM_STR],
234
                    'email'               => [$entity->getEmail(), \PDO::PARAM_STR],
235
                    'password'            => [$entity->getPassword(), \PDO::PARAM_STR],
236
                    'user_language_id'    => [$entity->getUserLanguage()->getId(), \PDO::PARAM_STR],
237
                    'can_login'           => [$entity->canLogin(), \PDO::PARAM_INT],
238
                    'is_gravatar_allowed' => [$entity->isGravatarAllowed(), \PDO::PARAM_INT],
239
                ]
240
            )
241
            ->where('id = ?')
242
            ->addUnnamedPlaceholderValue($entity->getId(), \PDO::PARAM_STR);
243
244
        $sql    = $query->getSql();
245
        $params = $query->getParameters();
246
247
        $statement = $this->writeConnection->prepare($sql);
248
        $statement->bindValues($params);
249
        $statement->execute();
250
251
        $this->deleteUserGroups($entity);
252
        $this->addUserGroups($entity);
253
    }
254
255
    /**
256
     * @param array $data
257
     *
258
     * @return Entity
259
     */
260
    protected function loadEntity(array $data): Entity
261
    {
262
        if (empty($data['id'])) {
263
            throw new \RuntimeException();
264
        }
265
266
        $userLanguage = new UserLanguage(
267
            $data['user_language_id'],
268
            $data['user_language_identifier'],
269
            ''
270
        );
271
        $userGroups   = $this->loadUserGroups($data);
272
273
        return new Entity(
274
            $data['id'],
275
            $data['username'],
276
            $data['email'],
277
            $data['password'],
278
            (bool)$data['can_login'],
279
            (bool)$data['is_gravatar_allowed'],
280
            $userLanguage,
281
            $userGroups
282
        );
283
    }
284
285
    /**
286
     * @param array $data
287
     *
288
     * @return UserGroup[]
289
     */
290
    protected function loadUserGroups(array $data): array
291
    {
292
        if (empty($data['user_group_ids'])) {
293
            return [];
294
        }
295
296
        $ids         = explode(',', $data['user_group_ids']);
297
        $identifiers = explode(',', $data['user_group_identifiers']);
298
        $names       = explode(',', $data['user_group_names']);
299
300
        if (count($ids) !== count($identifiers) || count($ids) !== count($names)) {
301
            throw new \LogicException();
302
        }
303
304
        $userGroups = [];
305
        foreach ($ids as $idx => $userGroupId) {
306
            $userGroups[] = new UserGroup($userGroupId, $identifiers[$idx], $names[$idx]);
307
        }
308
309
        return $userGroups;
310
    }
311
312
    /**
313
     * @return SelectQuery
314
     */
315
    private function getBaseQuery(): SelectQuery
316
    {
317
        /** @var SelectQuery $query */
318
        $query = (new QueryBuilder())
319
            ->select(
320
                'users.id',
321
                'users.username',
322
                'users.email',
323
                'users.password',
324
                'users.user_language_id',
325
                'ul.identifier AS user_language_identifier',
326
                'users.can_login',
327
                'users.is_gravatar_allowed',
328
                'GROUP_CONCAT(ug.id) AS user_group_ids',
329
                'GROUP_CONCAT(ug.identifier) AS user_group_identifiers',
330
                'GROUP_CONCAT(ug.name) AS user_group_names'
331
            )
332
            ->from('users')
333
            ->innerJoin(
334
                'user_languages',
335
                'ul',
336
                'ul.id = users.user_language_id AND ul.deleted = 0'
337
            )
338
            ->leftJoin('users_user_groups', 'uug', 'uug.user_id = users.id AND uug.deleted = 0')
339
            ->leftJoin('user_groups', 'ug', 'ug.id = uug.user_group_id AND ug.deleted = 0')
340
            ->groupBy('users.id')
341
            ->where('users.deleted = 0');
342
343
        return $query;
344
    }
345
346
    /**
347
     * @param Entity $entity
348
     */
349
    protected function deleteUserGroups(Entity $entity)
350
    {
351
        $query = (new QueryBuilder())
352
            ->delete('users_user_groups')
353
            ->where('user_id = ?')
354
            ->addUnnamedPlaceholderValue($entity->getId(), \PDO::PARAM_STR);
355
356
        $statement = $this->writeConnection->prepare($query->getSql());
357
        $statement->bindValues($query->getParameters());
358
        $statement->execute();
359
    }
360
361
    /**
362
     * @param Entity $entity
363
     */
364
    protected function addUserGroups(Entity $entity)
365
    {
366
        $idGenerator = $this->getIdGenerator();
367
368
        foreach ($entity->getUserGroups() as $userGroup) {
369
            $query = (new QueryBuilder())
370
                ->insert(
371
                    'users_user_groups',
372
                    [
373
                        'id'            => [$idGenerator->generate($entity), \PDO::PARAM_STR],
374
                        'user_id'       => [$entity->getId(), \PDO::PARAM_STR],
375
                        'user_group_id' => [$userGroup->getId(), \PDO::PARAM_STR],
376
                    ]
377
                );
378
379
            $statement = $this->writeConnection->prepare($query->getSql());
380
            $statement->bindValues($query->getParameters());
381
            $statement->execute();
382
        }
383
    }
384
}
385