Completed
Pull Request — master (#12)
by Elan
04:17
created

DoctrineReader::setQueryBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Port\Doctrine;
4
5
use Doctrine\Common\Persistence\ObjectManager;
6
use Doctrine\ORM\Internal\Hydration\IterableResult;
7
use Doctrine\ORM\Query;
8
use Doctrine\ORM\QueryBuilder;
9
use Port\Reader\CountableReader;
10
11
/**
12
 * Reads entities through the Doctrine ORM
13
 *
14
 * @author David de Boer <[email protected]>
15
 */
16
class DoctrineReader implements CountableReader
17
{
18
    /**
19
     * @var ObjectManager
20
     */
21
    protected $objectManager;
22
23
    /**
24
     * @var string
25
     */
26
    protected $objectName;
27
28
    /**
29
     * @var IterableResult
30
     */
31
    protected $iterableResult;
32
33
    /** @var QueryBuilder */
34
    protected $queryBuilder;
35
36 3
    /**
37
     * @param ObjectManager $objectManager
38 3
     * @param string $objectName e.g. YourBundle:YourEntity
39 3
     */
40 3
    public function __construct(ObjectManager $objectManager, $objectName)
41
    {
42
        $this->objectManager = $objectManager;
43
        $this->objectName = $objectName;
44
    }
45 1
46
    public function setQueryBuilder(QueryBuilder $queryBuilder)
47 1
    {
48 1
        $this->queryBuilder = $queryBuilder;
49
50
        return $this;
51
    }
52
53
    public function getQueryBuilder()
54 1
    {
55
        if ($this->queryBuilder === null) {
56 1
            $this->queryBuilder = $this->objectManager->createQueryBuilder()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Common\Persistence\ObjectManager as the method createQueryBuilder() does only exist in the following implementations of said interface: Doctrine\ODM\MongoDB\DocumentManager, Doctrine\ORM\Decorator\EntityManagerDecorator, Doctrine\ORM\EntityManager.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
57
                ->from($this->objectName, 'o');
58
        }
59
60
        return clone $this->queryBuilder;
61
    }
62 1
63
    /**
64 1
     * {@inheritdoc}
65 1
     */
66
    public function getFields()
67
    {
68
        return $this->objectManager->getClassMetadata($this->objectName)
69
            ->getFieldNames();
70
    }
71
72
    /**
73
     * {@inheritdoc}
74
     */
75
    public function current()
76
    {
77
        return current($this->iterableResult->current());
78 1
    }
79
80 1
    /**
81
     * {@inheritdoc}
82
     */
83
    public function next()
84
    {
85
        $this->iterableResult->next();
86 1
    }
87
88 1
    /**
89
     * {@inheritdoc}
90 1
     */
91 1
    public function key()
92 1
    {
93 1
        return $this->iterableResult->key();
94
    }
95 1
96 1
    /**
97
     * {@inheritdoc}
98 1
     */
99 1
    public function valid()
100
    {
101
        return $this->iterableResult->valid();
102
    }
103
104 1
    /**
105
     * {@inheritdoc}
106 1
     */
107 1
    public function rewind()
108 1
    {
109 1
        if (!$this->iterableResult) {
110
            $query = $this->getQueryBuilder()->select('o')->getQuery();
111 1
112
            $this->iterableResult = $query->iterate([], Query::HYDRATE_ARRAY);
113
        }
114
115
        $this->iterableResult->rewind();
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function count()
122
    {
123
        $query = $this->getQueryBuilder()->select('count(o)')->getQuery();
124
125
        return $query->getSingleScalarResult();
126
    }
127
}
128