ORMQueryBuilderLoader   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 91
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 41
dl 0
loc 91
ccs 0
cts 40
cp 0
rs 10
c 0
b 0
f 0
wmc 14

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getEntities() 0 3 1
A __construct() 0 3 1
C getEntitiesByIds() 0 65 12
1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of Biurad opensource projects.
5
 *
6
 * @copyright 2019 Biurad Group (https://biurad.com/)
7
 * @license   https://opensource.org/licenses/BSD-3-Clause License
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace Flange\Database\Doctrine\Form\ChoiceList;
14
15
use Doctrine\DBAL\Connection;
16
use Doctrine\DBAL\Types\ConversionException;
17
use Doctrine\DBAL\Types\Type;
18
use Doctrine\ORM\QueryBuilder;
19
use Symfony\Component\Form\Exception\TransformationFailedException;
20
21
/**
22
 * Loads entities using a {@link QueryBuilder} instance.
23
 *
24
 * @author Benjamin Eberlei <[email protected]>
25
 * @author Bernhard Schussek <[email protected]>
26
 */
27
class ORMQueryBuilderLoader implements EntityLoaderInterface
28
{
29
    /**
30
     * Contains the query builder that builds the query for fetching the
31
     * entities.
32
     *
33
     * This property should only be accessed through queryBuilder.
34
     */
35
    private $queryBuilder;
36
37
    public function __construct(QueryBuilder $queryBuilder)
38
    {
39
        $this->queryBuilder = $queryBuilder;
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45
    public function getEntities(): array
46
    {
47
        return $this->queryBuilder->getQuery()->execute();
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53
    public function getEntitiesByIds(string $identifier, array $values): array
54
    {
55
        if (null !== $this->queryBuilder->getMaxResults() || 0 < (int) $this->queryBuilder->getFirstResult()) {
56
            // an offset or a limit would apply on results including the where clause with submitted id values
57
            // that could make invalid choices valid
58
            $choices = [];
59
            $metadata = $this->queryBuilder->getEntityManager()->getClassMetadata(\current($this->queryBuilder->getRootEntities()));
60
61
            foreach ($this->getEntities() as $entity) {
62
                if (\in_array((string) \current($metadata->getIdentifierValues($entity)), $values, true)) {
63
                    $choices[] = $entity;
64
                }
65
            }
66
67
            return $choices;
68
        }
69
70
        $qb = clone $this->queryBuilder;
71
        $alias = \current($qb->getRootAliases());
72
        $parameter = 'ORMQueryBuilderLoader_getEntitiesByIds_'.$identifier;
73
        $parameter = \str_replace('.', '_', $parameter);
74
        $where = $qb->expr()->in($alias.'.'.$identifier, ':'.$parameter);
75
76
        // Guess type
77
        $entity = \current($qb->getRootEntities());
78
        $metadata = $qb->getEntityManager()->getClassMetadata($entity);
79
80
        if (\in_array($type = $metadata->getTypeOfField($identifier), ['integer', 'bigint', 'smallint'], true)) {
81
            $parameterType = Connection::PARAM_INT_ARRAY;
0 ignored issues
show
Bug introduced by
The constant Doctrine\DBAL\Connection::PARAM_INT_ARRAY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
82
83
            // Filter out non-integer values (e.g. ""). If we don't, some
84
            // databases such as PostgreSQL fail.
85
            $values = \array_values(\array_filter($values, fn ($v) => (string) $v === (string) (int) $v || \ctype_digit($v)));
86
        } elseif (\in_array($type, ['ulid', 'uuid', 'guid'], true)) {
87
            $parameterType = Connection::PARAM_STR_ARRAY;
0 ignored issues
show
Bug introduced by
The constant Doctrine\DBAL\Connection::PARAM_STR_ARRAY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
88
89
            // Like above, but we just filter out empty strings.
90
            $values = \array_values(\array_filter($values, fn ($v) => '' !== (string) $v));
91
92
            // Convert values into right type
93
            if (Type::hasType($type)) {
0 ignored issues
show
Bug introduced by
It seems like $type can also be of type null; however, parameter $name of Doctrine\DBAL\Types\Type::hasType() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

93
            if (Type::hasType(/** @scrutinizer ignore-type */ $type)) {
Loading history...
94
                $doctrineType = Type::getType($type);
0 ignored issues
show
Bug introduced by
It seems like $type can also be of type null; however, parameter $name of Doctrine\DBAL\Types\Type::getType() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

94
                $doctrineType = Type::getType(/** @scrutinizer ignore-type */ $type);
Loading history...
95
                $platform = $qb->getEntityManager()->getConnection()->getDatabasePlatform();
96
97
                foreach ($values as &$value) {
98
                    try {
99
                        $value = $doctrineType->convertToDatabaseValue($value, $platform);
100
                    } catch (ConversionException $e) {
101
                        throw new TransformationFailedException(\sprintf('Failed to transform "%s" into "%s".', $value, $type), 0, $e);
102
                    }
103
                }
104
                unset($value);
105
            }
106
        } else {
107
            $parameterType = Connection::PARAM_STR_ARRAY;
108
        }
109
110
        if (!$values) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $values of type array 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...
111
            return [];
112
        }
113
114
        return $qb->andWhere($where)
115
                  ->getQuery()
116
                  ->setParameter($parameter, $values, $parameterType)
117
                  ->getResult();
118
    }
119
}
120