Issues (104)

src/Orm/DataMappers/UserSqlDataMapper.php (8 issues)

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\Domain\Entities\IStringerEntity;
11
use Opulence\Orm\DataMappers\SqlDataMapper;
12
use Opulence\Orm\OrmException;
13
use Opulence\QueryBuilders\Expression;
14
use Opulence\QueryBuilders\InvalidQueryException;
15
use Opulence\QueryBuilders\MySql\QueryBuilder;
16
use Opulence\QueryBuilders\SelectQuery;
17
18
/** @phan-file-suppress PhanTypeMismatchArgument */
19
class UserSqlDataMapper extends SqlDataMapper implements IUserDataMapper
20
{
21
    use IdGeneratorUserTrait;
22
23
    /**
24
     * @param IStringerEntity $entity
25
     */
26
    public function add($entity)
27
    {
28
        assert($entity instanceof Entity, new \InvalidArgumentException());
29
30
        $query = (new QueryBuilder())
31
            ->insert(
32
                'users',
33
                [
34
                    'id'                  => [$entity->getId(), \PDO::PARAM_STR],
35
                    'username'            => [$entity->getUsername(), \PDO::PARAM_STR],
36
                    'email'               => [$entity->getEmail(), \PDO::PARAM_STR],
37
                    'password'            => [$entity->getPassword(), \PDO::PARAM_STR],
38
                    'user_language_id'    => [$entity->getUserLanguage()->getId(), \PDO::PARAM_STR],
39
                    'can_login'           => [$entity->canLogin(), \PDO::PARAM_INT],
40
                    'is_gravatar_allowed' => [$entity->isGravatarAllowed(), \PDO::PARAM_INT],
41
                ]
42
            );
43
44
        $sql    = $query->getSql();
45
        $params = $query->getParameters();
46
47
        $statement = $this->writeConnection->prepare($sql);
48
        $statement->bindValues($params);
49
        $statement->execute();
50
51
        $this->addUserGroups($entity);
52
    }
53
54
    /**
55
     * @param IStringerEntity $entity
56
     *
57
     * @throws InvalidQueryException
58
     */
59
    public function delete($entity)
60
    {
61
        assert($entity instanceof Entity, new \InvalidArgumentException());
62
63
        $this->deleteUserGroups($entity);
64
65
        $query = (new QueryBuilder())
66
            ->update(
67
                'users',
68
                'users',
69
                [
70
                    'deleted_at' => new Expression('NOW()'),
71
                    'username'   => new Expression('LEFT(MD5(RAND()), 8)'),
72
                    'email'      => new Expression('CONCAT(username, "@example.com")'),
73
                    'password'   => ['', \PDO::PARAM_STR],
74
                ]
75
            )
76
            ->where('id = ?')
77
            ->addUnnamedPlaceholderValue($entity->getId(), \PDO::PARAM_STR);
78
79
        $sql    = $query->getSql();
80
        $params = $query->getParameters();
81
82
        $statement = $this->writeConnection->prepare($sql);
83
        $statement->bindValues($params);
84
        $statement->execute();
85
    }
86
87
    /**
88
     * @return Entity[]
89
     * @throws OrmException
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
     * @throws OrmException
109
     */
110
    public function getPage(int $limitFrom, int $pageSize, array $orders, array $conditions, array $params): array
111
    {
112
        $query = $this->getBaseQuery()
113
            ->limit($pageSize)
114
            ->offset($limitFrom);
115
116
        if (!$orders) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $orders of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
117
            $query->orderBy('username ASC');
118
        }
119
        foreach ($orders as $order) {
120
            $query->addOrderBy($order);
121
        }
122
123
        foreach ($conditions as $condition) {
124
            $query->andWhere($condition);
125
        }
126
127
        $replaceCount = 1;
128
129
        $sql = $query->getSql();
130
        $sql = str_replace('SELECT', 'SELECT SQL_CALC_FOUND_ROWS', $sql, $replaceCount);
131
132
        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...
133
    }
134
135
    /**
136
     * @param int|string $id
137
     *
138
     * @return Entity|null
139
     * @throws OrmException
140
     */
141
    public function getById($id): ?Entity
142
    {
143
        $query = $this->getBaseQuery()->andWhere('users.id = :user_id');
144
145
        $parameters = ['user_id' => [$id, \PDO::PARAM_STR]];
146
147
        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...
148
    }
149
150
    /**
151
     * @param string $identifier
152
     *
153
     * @return Entity|null
154
     * @throws OrmException
155
     */
156
    public function find(string $identifier): ?Entity
157
    {
158
        $query = $this->getBaseQuery()->andWhere('(username = :identifier OR email = :identifier)');
159
160
        $sql    = $query->getSql();
161
        $params = [
162
            'identifier' => [$identifier, \PDO::PARAM_STR],
163
        ];
164
165
        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...
166
    }
167
168
    /**
169
     * @param string $clientId
170
     *
171
     * @return Entity|null
172
     * @throws OrmException
173
     */
174
    public function getByClientId(string $clientId): ?Entity
175
    {
176
        $query = $this->getBaseQuery()
177
            ->innerJoin(
178
                'api_clients',
179
                'ac',
180
                'ac.user_id = users.id AND ac.deleted_at IS NULL'
181
            )
182
            ->andWhere('ac.id = :client_id');
183
184
        $sql    = $query->getSql();
185
        $params = [
186
            'client_id' => [$clientId, \PDO::PARAM_STR],
187
        ];
188
189
        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...
190
    }
191
192
    /**
193
     * @param string $username
194
     *
195
     * @return Entity|null
196
     * @throws OrmException
197
     */
198
    public function getByUsername(string $username): ?Entity
199
    {
200
        $query = $this->getBaseQuery()->andWhere('`username` = :username');
201
202
        $sql    = $query->getSql();
203
        $params = [
204
            'username' => [$username, \PDO::PARAM_STR],
205
        ];
206
207
        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...
208
    }
209
210
    /**
211
     * @param string $email
212
     *
213
     * @return Entity|null
214
     * @throws OrmException
215
     */
216
    public function getByEmail(string $email): ?Entity
217
    {
218
        $query = $this->getBaseQuery()->andWhere('email = :email');
219
220
        $sql    = $query->getSql();
221
        $params = [
222
            'email' => [$email, \PDO::PARAM_STR],
223
        ];
224
225
        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...
226
    }
227
228
    /**
229
     * @param IStringerEntity $entity
230
     *
231
     * @throws InvalidQueryException
232
     */
233
    public function update($entity)
234
    {
235
        assert($entity instanceof Entity, new \InvalidArgumentException());
236
237
        $query = (new QueryBuilder())
238
            ->update(
239
                'users',
240
                'users',
241
                [
242
                    'username'            => [$entity->getUsername(), \PDO::PARAM_STR],
243
                    'email'               => [$entity->getEmail(), \PDO::PARAM_STR],
244
                    'password'            => [$entity->getPassword(), \PDO::PARAM_STR],
245
                    'user_language_id'    => [$entity->getUserLanguage()->getId(), \PDO::PARAM_STR],
246
                    'can_login'           => [$entity->canLogin(), \PDO::PARAM_INT],
247
                    'is_gravatar_allowed' => [$entity->isGravatarAllowed(), \PDO::PARAM_INT],
248
                ]
249
            )
250
            ->where('id = ?')
251
            ->addUnnamedPlaceholderValue($entity->getId(), \PDO::PARAM_STR);
252
253
        $sql    = $query->getSql();
254
        $params = $query->getParameters();
255
256
        $statement = $this->writeConnection->prepare($sql);
257
        $statement->bindValues($params);
258
        $statement->execute();
259
260
        $this->deleteUserGroups($entity);
261
        $this->addUserGroups($entity);
262
    }
263
264
    /**
265
     * @param array $data
266
     *
267
     * @return Entity
268
     */
269
    protected function loadEntity(array $data): Entity
270
    {
271
        $userLanguage = new UserLanguage(
272
            $data['user_language_id'],
273
            $data['user_language_identifier'],
274
            ''
275
        );
276
        $userGroups   = $this->loadUserGroups($data);
277
278
        return new Entity(
279
            $data['id'],
280
            $data['username'],
281
            $data['email'],
282
            $data['password'],
283
            (bool)$data['can_login'],
284
            (bool)$data['is_gravatar_allowed'],
285
            $userLanguage,
286
            $userGroups
287
        );
288
    }
289
290
    /**
291
     * @param array $data
292
     *
293
     * @return UserGroup[]
294
     */
295
    protected function loadUserGroups(array $data): array
296
    {
297
        if (empty($data['user_group_ids'])) {
298
            return [];
299
        }
300
301
        $ids         = explode(',', $data['user_group_ids']);
302
        $identifiers = explode(',', $data['user_group_identifiers']);
303
        $names       = explode(',', $data['user_group_names']);
304
305
        $userGroups = [];
306
        foreach ($ids as $idx => $userGroupId) {
307
            $userGroups[] = new UserGroup($userGroupId, $identifiers[$idx], $names[$idx]);
308
        }
309
310
        return $userGroups;
311
    }
312
313
    /**
314
     * @return SelectQuery
315
     */
316
    private function getBaseQuery(): SelectQuery
317
    {
318
        return (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_at IS NULL'
337
            )
338
            ->leftJoin('users_user_groups', 'uug', 'uug.user_id = users.id AND uug.deleted_at IS NULL')
339
            ->leftJoin('user_groups', 'ug', 'ug.id = uug.user_group_id AND ug.deleted_at IS NULL')
340
            ->groupBy('users.id')
341
            ->where('users.deleted_at IS NULL');
342
    }
343
344
    /**
345
     * @param Entity $entity
346
     *
347
     * @throws InvalidQueryException
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