Passed
Pull Request — master (#55)
by Thomas
02:28
created

EntityFetcherMock::count()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
namespace ORM\Testing;
4
5
use Mockery\MockInterface;
6
use ORM\Entity;
7
use ORM\EntityFetcher;
8
use ORM\EntityManager;
9
use ORM\Testing\EntityFetcherMock\Result;
10
11
class EntityFetcherMock extends EntityFetcher
12
{
13
    const RANDOM_KEY_MIN = 1000000000;
14
    const RANDOM_KEY_MAX = 1000999999;
15
16
    /** @var Entity[][]  */
17
    protected static $primaryKeyMap = [];
18
19
    /** @var Result[][] */
20
    protected static $results = [];
21
22
    /** @var array */
23
    protected $currentResult;
24
25
    /**
26
     * Add an entity to be fetched by primary key
27
     *
28
     * The entity needs to have a primary key if not it will be filled with random values between RANDOM_KEY_MIN and
29
     * RANDOM_KEY_MAX (at the time writing this it is 1000000000 and 1000999999).
30
     *
31
     * You can pass mocks from Entity too but we need to call `Entity::getPrimaryKey()`.
32
     *
33
     * @param Entity $entity
34
     */
35
    public static function addEntity(Entity $entity)
36
    {
37
        static::completePrimaryKeys($entity);
38
        $class = get_class($entity);
39
        if ($entity instanceof MockInterface) {
40
            $class = (new \ReflectionClass($entity))->getParentClass()->getName();
41
        }
42
43
        static::$primaryKeyMap[$class][static::buildChecksum($entity->getPrimaryKey())] = $entity;
44
    }
45
46
    /**
47
     * Retrieve an entity by $primaryKey
48
     *
49
     * @param string $class
50
     * @param array $primaryKey
51
     * @return Entity|null
52
     */
53
    public static function retrieve($class, array $primaryKey)
54
    {
55
        $checksum = static::buildChecksum($primaryKey);
56
        return isset(static::$primaryKeyMap[$class]) && isset(static::$primaryKeyMap[$class][$checksum]) ?
57
            static::$primaryKeyMap[$class][$checksum] : null;
58
    }
59
60
    /**
61
     * Create and add a EntityFetcherMock\Result for $class
62
     *
63
     * As the results are mocked to come from the database they will also get a primary key if they don't have already.
64
     *
65
     * @param $class
66
     * @param EntityManager $em
67
     * @param Entity ...$entities
68
     * @return Result
69
     */
70
    public static function addResult($class, EntityManager $em, Entity ...$entities)
71
    {
72
        $result = new Result($em, $class);
73
        $result->addEntities(...static::completePrimaryKeys(...$entities));
74
75
        if (!isset(static::$results[$class])) {
76
            static::$results[$class] = [spl_object_hash($result) => $result];
77
        } else {
78
            static::$results[$class][spl_object_hash($result)] = $result;
79
        }
80
81
        return $result;
82
    }
83
84
    /**
85
     * Get the results for $class and $query
86
     *
87
     * The EntityFetcherMock\Result gets a quality for matching this query. Only the highest quality will be used.
88
     *
89
     * @param string $class
90
     * @param EntityFetcher $fetcher
91
     * @return array
92
     */
93
    public static function getResults($class, EntityFetcher $fetcher)
94
    {
95
        if (!isset(static::$results[$class])) {
96
            return [];
97
        }
98
99
        $results = [];
100
        foreach (static::$results[$class] as $objHash => $result) {
101
            if ($quality = $result->compare($fetcher)) {
102
                $results[$objHash] = $quality;
103
            }
104
        }
105
106
        if (empty($results)) {
107
            return [];
108
        }
109
110
        arsort($results, SORT_DESC);
111
        $objHash = array_keys($results)[0];
112
        return static::$results[$class][$objHash]->getEntities();
113
    }
114
115
    /**
116
     * Fill the primary keys of $entities
117
     *
118
     * If the primary key is incomplete the missing attributes will be filled with a random integer between
119
     * RANDOM_KEY_MIN and RANDOM_KEY_MAX (at the time writing this it is 1000000000 and 1000999999).
120
     *
121
     * @param Entity ...$entities
122
     * @return Entity[]
123
     */
124
    public static function completePrimaryKeys(Entity ...$entities)
125
    {
126
        // complete the primary keys with random numbers
127
        foreach ($entities as $entity) {
128
            foreach ($entity::getPrimaryKeyVars() as $attribute) {
129
                if ($entity->$attribute === null) {
130
                    $entity->$attribute = mt_rand(static::RANDOM_KEY_MIN, static::RANDOM_KEY_MAX);
131
                }
132
            }
133
        }
134
135
        return $entities;
136
    }
137
138
    /**
139
     * Reset the statics from
140
     */
141
    public static function reset()
142
    {
143
        static::$primaryKeyMap = [];
144
        static::$results = [];
145
    }
146
147
    /** {@inheritDoc} */
148
    public function one()
149
    {
150
        if ($this->currentResult === null) {
151
            $this->currentResult = static::getResults($this->class, $this);
0 ignored issues
show
Bug introduced by
It seems like $this->class can also be of type ORM\Entity; however, parameter $class of ORM\Testing\EntityFetcherMock::getResults() 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

151
            $this->currentResult = static::getResults(/** @scrutinizer ignore-type */ $this->class, $this);
Loading history...
152
        }
153
154
        return array_shift($this->currentResult);
155
    }
156
157
    /** {@inheritDoc} */
158
    public function count()
159
    {
160
        return count(static::getResults($this->class, $this));
0 ignored issues
show
Bug introduced by
It seems like $this->class can also be of type ORM\Entity; however, parameter $class of ORM\Testing\EntityFetcherMock::getResults() 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

160
        return count(static::getResults(/** @scrutinizer ignore-type */ $this->class, $this));
Loading history...
161
    }
162
163
    /**
164
     * Build a checksum from $primaryKey
165
     *
166
     * @param array $primaryKey
167
     * @return string
168
     */
169
    protected static function buildChecksum(array $primaryKey)
170
    {
171
        return md5(serialize($primaryKey));
172
    }
173
}
174