ResultRepository::retrieve()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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