Passed
Push — dev_2x ( 3e8772...896177 )
by Adrian
06:45
created

Mapper   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 204
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
wmc 29
eloc 63
c 5
b 0
f 0
dl 0
loc 204
rs 10

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getReadConnection() 0 3 1
A getWriteConnection() 0 3 1
A getConfig() 0 3 1
A __construct() 0 7 1
A init() 0 2 1
A patch() 0 3 1
A newEntity() 0 9 1
A newQuery() 0 5 1
A __call() 0 11 3
A newCollection() 0 9 3
A use() 0 5 2
A addRelation() 0 9 3
A getRelations() 0 3 1
A newSubselectQuery() 0 5 1
A without() 0 6 1
A getRelation() 0 11 3
A hasRelation() 0 3 1
A getHydrator() 0 3 1
A runActionInTransaction() 0 12 2
1
<?php
2
declare(strict_types=1);
3
4
namespace Sirius\Orm;
5
6
use Sirius\Orm\Behaviour\BehaviourInterface;
7
use Sirius\Orm\Collection\Collection;
8
use Sirius\Orm\Contract\EntityInterface;
9
use Sirius\Orm\Contract\HydratorInterface;
10
use Sirius\Orm\Entity\GenericHydrator;
11
use Sirius\Orm\Entity\Patcher;
12
use Sirius\Orm\Relation\Relation;
13
use Sirius\Sql\Bindings;
14
15
/**
16
 * @method Query where($column, $value, $condition)
17
 * @method Query orderBy(string $expr, string ...$exprs)
18
 */
19
class Mapper
20
{
21
    /**
22
     * @var Orm
23
     */
24
    protected $orm;
25
26
    /**
27
     * @var ConnectionLocator
28
     */
29
    protected $connectionLocator;
30
31
    /**
32
     * @var MapperConfig
33
     */
34
    protected $mapperConfig;
35
36
    /**
37
     * @var HydratorInterface
38
     */
39
    protected $hydrator;
40
41
    /**
42
     * @var Behaviours
43
     */
44
    protected $behaviours;
45
46
    /**
47
     * @var array
48
     */
49
    protected $relations = [];
50
51
    public function __construct(Orm $orm)
52
    {
53
        $this->orm               = $orm;
54
        $this->connectionLocator = $orm->getConnectionLocator();
55
        $this->behaviours        = new Behaviours();
56
        $this->hydrator          = new GenericHydrator($this->orm->getCastingManager());
57
        $this->init();
58
    }
59
60
    protected function init()
61
    {
62
    }
63
64
    public function __call(string $method, array $params)
65
    {
66
        switch ($method) {
67
            case 'where':
68
            case 'orderBy':
69
                $query = $this->newQuery();
70
71
                return $query->{$method}(...$params);
72
        }
73
74
        throw new \BadMethodCallException("Unknown method {$method} for class " . get_class($this));
75
    }
76
77
    /**
78
     * @return MapperConfig
79
     */
80
    public function getConfig(): MapperConfig
81
    {
82
        return $this->mapperConfig;
83
    }
84
85
    /**
86
     * @return HydratorInterface
87
     */
88
    public function getHydrator(): HydratorInterface
89
    {
90
        return $this->hydrator;
91
    }
92
93
    /**
94
     * Add behaviours to the mapper
95
     *
96
     * @param mixed ...$behaviours
97
     */
98
    public function use(...$behaviours)
99
    {
100
        /** @var BehaviourInterface $behaviour */
101
        foreach ($behaviours as $behaviour) {
102
            $this->behaviours->add($behaviour);
103
        }
104
    }
105
106
    /**
107
     * Create a clone of the mapper without the selected behaviour
108
     *
109
     * @param mixed ...$behaviours
110
     *
111
     * @return self
112
     */
113
    public function without(...$behaviours)
114
    {
115
        $mapper             = clone $this;
116
        $mapper->behaviours = $this->behaviours->without(...$behaviours);
117
118
        return $mapper;
119
    }
120
121
    public function newEntity(array $data): EntityInterface
122
    {
123
        $entity = $this->getHydrator()
124
                       ->hydrate(array_merge(
125
                           $this->getConfig()->getAttributeDefaults(),
126
                           $data
127
                       ));
128
129
        return $this->behaviours->apply($this, __FUNCTION__, $entity);
130
    }
131
132
    public function newCollection(array $datas = []): Collection
133
    {
134
        $entities = [];
135
        foreach ($datas as $data) {
136
            // if $data is an entity or some other object, keep it as is
137
            $entities[] = (is_array($data)) ? $this->newEntity($data) : $data;
138
        }
139
140
        return new Collection($entities, $this->hydrator);
141
    }
142
143
    public function patch($entity, array $data)
144
    {
145
        return (new Patcher($this))($entity, $data);
146
    }
147
148
    /**
149
     * @param string $name
150
     * @param array|Relation $relation
151
     */
152
    public function addRelation(string $name, $relation)
153
    {
154
        if (is_array($relation) || $relation instanceof Relation) {
0 ignored issues
show
introduced by
$relation is always a sub-type of Sirius\Orm\Relation\Relation.
Loading history...
155
            $this->relations[$name] = $relation;
156
157
            return;
158
        }
159
        throw new \InvalidArgumentException(
160
            sprintf('The relation has to be a Relation instance or an array of configuration options')
161
        );
162
    }
163
164
    public function hasRelation(string $name): bool
165
    {
166
        return isset($this->relations[$name]);
167
    }
168
169
    public function getRelation(string $name): Relation
170
    {
171
        if (! $this->hasRelation($name)) {
172
            throw new \InvalidArgumentException("Relation named {$name} is not registered for this mapper");
173
        }
174
175
        if (is_array($this->relations[$name])) {
176
            $this->relations[$name] = $this->orm->createRelation($this, $name, $this->relations[$name]);
177
        }
178
179
        return $this->relations[$name];
180
    }
181
182
    public function getRelations(): array
183
    {
184
        return array_keys($this->relations);
185
    }
186
187
    public function newQuery()
188
    {
189
        $query = new Query($this->getReadConnection(), $this);
190
191
        return $this->behaviours->apply($this, __FUNCTION__, $query);
192
    }
193
194
    public function newSubselectQuery(Connection $connection, Bindings $bindings, string $indent)
195
    {
196
        $query = new Query($connection, $this, $bindings, $indent);
197
198
        return $this->behaviours->apply($this, __FUNCTION__, $query);
199
    }
200
201
    public function getReadConnection(): Connection
202
    {
203
        return $this->connectionLocator->getRead();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->connectionLocator->getRead() returns the type Atlas\Pdo\Connection which includes types incompatible with the type-hinted return Sirius\Orm\Connection.
Loading history...
204
    }
205
206
    public function getWriteConnection(): Connection
207
    {
208
        return $this->connectionLocator->getWrite();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->connectionLocator->getWrite() returns the type Atlas\Pdo\Connection which includes types incompatible with the type-hinted return Sirius\Orm\Connection.
Loading history...
209
    }
210
211
    protected function runActionInTransaction(Action\BaseAction $action)
212
    {
213
        $this->connectionLocator->lockToWrite(true);
214
        $this->getWriteConnection()->beginTransaction();
215
        try {
216
            $action->run();
217
            $this->getWriteConnection()->commit();
218
219
            return true;
220
        } catch (\Exception $e) {
221
            $this->getWriteConnection()->rollBack();
222
            throw $e;
223
        }
224
    }
225
}
226