Completed
Pull Request — master (#24)
by Quentin
03:30
created

DoctrineLoaderTrait::assertIsConfigured()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 17
ccs 0
cts 17
cp 0
rs 8.8571
cc 5
eloc 11
nc 3
nop 0
crap 30
1
<?php
2
3
namespace Majora\Framework\Loader\Bridge\Doctrine;
4
5
use Doctrine\Common\Collections\Collection;
6
use Doctrine\ORM\EntityRepository;
7
use Majora\Framework\Loader\LoaderTrait;
8
9
/**
10
 * Trait to use into Doctrine loaders to get a simple implementation of LoaderInterface.
11
 *
12
 * @property $entityRepository
13
 * @property $entityClass
14
 * @property $collectionClass
15
 * @property $filterResolver
16
 */
17
trait DoctrineLoaderTrait
18
{
19
    use LoaderTrait;
20
21
    /**
22
     * Construct.
23
     *
24
     * @param EntityRepository $entityRepository (optionnal)
25
     */
26
    public function __construct(EntityRepository $entityRepository = null)
27
    {
28
        $this->entityRepository = $entityRepository;
0 ignored issues
show
Documentation Bug introduced by
It seems like $entityRepository can also be of type object<Doctrine\ORM\EntityRepository>. However, the property $entityRepository is declared as type object<Majora\Framework\...ry\RepositoryInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
29
    }
30
31
    /**
32
     * Checks if loader is properly configured.
33
     *
34
     * @throws \RuntimeException if not configured
35
     */
36
    private function assertIsConfigured()
37
    {
38
        if (!$this->entityClass || !$this->collectionClass || !$this->filterResolver) {
39
            throw new \RuntimeException(sprintf(
40
                '%s methods cannot be used while it has not been initialize through %s::configureMetadata().',
41
                static::class,
42
                static::class
43
            ));
44
        }
45
        if (!$this->entityRepository) {
46
            throw new \RuntimeException(sprintf(
47
                'You must provide %s entity Doctrine repository throught %s::__construct() method to use this loader.',
48
                $this->entityClass,
49
                static::class
50
            ));
51
        }
52
    }
53
54
    /**
55
     * Hook called with every entity or collection loaded through this loader.
56
     *
57
     * @param CollectionnableInterface|EntityCollection $entity
58
     *
59
     * @return $object same entity or collection
0 ignored issues
show
Documentation introduced by
The doc-type $object could not be parsed: Unknown type name "$object" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
60
     */
61
    protected function onLoad($entity)
62
    {
63
        @trigger_error(__METHOD__.' is deprecated and will be removed in 2.0. Use full class delegate instead, see Majora\Framework\Loader\LazyLoaderInterface.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
64
65
        return $entity;
66
    }
67
68
    /**
69
     * Convert given array or Collection result set to managed entity collection class.
70
     *
71
     * @param array|Collection $result
72
     *
73
     * @return EntityCollection
74
     */
75
    protected function toEntityCollection($result)
76
    {
77
        switch (true) {
78
79
            // already a collection ?
80
            case is_object($result) && is_subclass_of($result, $this->collectionClass) :
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $this->collectionClass can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
81
                $collection = $result;
82
                break;
83
84
            // already a collection ?
85
            case $result instanceof Collection :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
86
                $collection = new $this->collectionClass($result->toArray());
87
                break;
88
89
            // simple related entity ?
90
            case is_object($result) && is_subclass_of($result, $this->entityClass) :
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $this->entityClass can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
91
                $collection = new $this->collectionClass(array($result));
92
                break;
93
94
            // simple array ?
95
            case is_array($result) :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
96
                $collection = new $this->collectionClass($result);
97
                break;
98
99
            default:
100
                $collection = new $this->collectionClass();
101
                break;
102
        }
103
104
        return $this->onLoad($collection);
105
    }
106
107
    /**
108
     * Create entity query.
109
     * Proxy to base query builder method to use to custom all queries from this loader.
110
     *
111
     * @param string $alias
112
     *
113
     * @return QueryBuilder
114
     */
115
    protected function createQuery($alias = 'entity')
116
    {
117
        return $this->entityRepository->createQueryBuilder($alias);
118
    }
119
120
    /**
121
     * create query an filter it with given data.
122
     *
123
     * @param array $filters
124
     *
125
     * @return Query
126
     */
127
    private function createFilteredQuery(array $filters)
128
    {
129
        $qb = $this->createQuery('entity');
130
131
        foreach ($filters as $field => $filter) {
132
            $qb->andWhere(is_array($filter)
133
                    ? sprintf('entity.%s in (:%s)', $field, $field)
134
                    : sprintf('entity.%s = :%s', $field, $field)
135
                )
136
                ->setParameter(sprintf(':%s', $field), $filter)
137
            ;
138
        }
139
140
        return $qb->getQuery();
141
    }
142
143
    /**
144
     * @see LoaderInterface::retrieveAll()
145
     */
146
    public function retrieveAll(array $filters = array(), $limit = null, $offset = null)
147
    {
148
        $this->assertIsConfigured();
149
150
        $query = $this->createFilteredQuery(
151
            $this->filterResolver->resolve($filters)
152
        );
153
154
        if ($limit) {
155
            $query->setMaxResults($limit);
156
        }
157
        if ($offset) {
158
            $query->setFirstResult($offset);
159
        }
160
161
        return $this->toEntityCollection(
162
            $query->getResult()
163
        );
164
    }
165
166
    /**
167
     * @see LoaderInterface::retrieveOne()
168
     */
169
    public function retrieveOne(array $filters = array())
170
    {
171
        $this->assertIsConfigured();
172
173
        return $this->onLoad($this->createFilteredQuery($filters)
174
            ->setMaxResults(1)
175
            ->getOneOrNullResult()
176
        );
177
    }
178
179
    /**
180
     * @see LoaderInterface::retrieve()
181
     */
182
    public function retrieve($id)
183
    {
184
        return $this->onLoad($this->retrieveOne(array('id' => $id)));
185
    }
186
}
187