Completed
Push — master ( 77c504...368143 )
by Maxim
02:10
created

AbstractEntityRepositoryDb::save()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 1
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace WebComplete\core\entity;
4
5
use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\Query\QueryBuilder;
7
use WebComplete\core\condition\ConditionDbParser;
8
use WebComplete\core\factory\ObjectFactory;
9
use WebComplete\core\utils\hydrator\HydratorInterface;
10
use WebComplete\core\condition\Condition;
11
12
abstract class AbstractEntityRepositoryDb extends AbstractEntityRepository
13
{
14
    const SERIALIZE_STRATEGY_JSON = 1;
15
    const SERIALIZE_STRATEGY_PHP  = 2;
16
17
    protected $table;
18
    /**
19
     * map: fieldName: propertyName
20
     * @var array|null
21
     */
22
    protected $map;
23
    protected $serializeFields = [];
24
    protected $serializeStrategy = self::SERIALIZE_STRATEGY_JSON;
25
26
    /** @var Connection */
27
    protected $db;
28
29
    /**
30
     * @var ConditionDbParser
31
     */
32
    protected $conditionParser;
33
34
    /**
35
     * @param ObjectFactory $factory
36
     * @param HydratorInterface $hydrator
37
     * @param ConditionDbParser $conditionParser
38
     * @param Connection $db
39
     */
40
    public function __construct(
41
        ObjectFactory $factory,
42
        HydratorInterface $hydrator,
43
        ConditionDbParser $conditionParser,
44
        Connection $db
45
    ) {
46
        parent::__construct($factory, $hydrator);
47
        $this->db = $db;
48
        $this->conditionParser = $conditionParser;
49
    }
50
51
    /**
52
     * @param \Closure $closure
53
     * @throws \Exception
54
     */
55
    public function transaction(\Closure $closure)
56
    {
57
        $this->db->transactional($closure);
58
    }
59
60
    /**
61
     * @param $id
62
     * @return AbstractEntity|null
63
     */
64
    public function findById($id)
65
    {
66
        return $this->findOne(new Condition(['t1.id' => $id]));
67
    }
68
69
    /**
70
     * @param Condition $condition
71
     * @return AbstractEntity|null
72
     */
73
    public function findOne(Condition $condition)
74
    {
75
        $result = null;
76
        $select = $this->selectQuery($condition);
77
        if ($row = $select->execute()->fetch()) {
78
            $result = $this->rowToEntity($row);
79
        }
80
81
        return $result;
82
    }
83
84
    /**
85
     * @param Condition $condition
86
     * @return AbstractEntity[]
87
     */
88
    public function findAll(Condition $condition = null): array
89
    {
90
        $result = [];
91
        $select = $this->selectQuery($condition);
92 View Code Duplication
        if ($rows = $select->execute()->fetchAll(\PDO::FETCH_ASSOC)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
93
            foreach ($rows as $row) {
94
                $entity = $this->rowToEntity($row);
95
                $result[$entity->getId()] = $entity;
96
            }
97
        }
98
99
        return $result;
100
    }
101
102
    /**
103
     * @param Condition|null $condition
104
     * @return int
105
     */
106
    public function count(Condition $condition = null): int
107
    {
108
        $select = $this->selectQuery($condition);
109
        return $select->select(['t1.id'])->execute()->rowCount();
110
    }
111
112
    /**
113
     * @param AbstractEntity $item
114
     */
115
    public function save(AbstractEntity $item)
116
    {
117
        $data = $this->hydrator->extract($item, $this->map);
118
        $this->beforeDataSave($data);
119
        $this->serializeFields($data);
120
        if ($id = $item->getId()) {
121
            $this->db->update($this->table, $data, ['t1.id' => $id]);
122
        } else {
123
            unset($data['id']);
124
            $this->db->insert($this->table, $data);
125
            $item->setId((int)$this->db->lastInsertId());
126
        }
127
    }
128
129
    /**
130
     * @param $id
131
     *
132
     * @throws \Doctrine\DBAL\Exception\InvalidArgumentException
133
     */
134
    public function delete($id)
135
    {
136
        $this->db->delete($this->table, ['t1.id' => $id]);
137
    }
138
139
    /**
140
     * @param Condition|null $condition
141
     */
142
    public function deleteAll(Condition $condition = null)
143
    {
144
        $query = $this->selectQuery($condition);
145
        $query->delete($this->table, 't1');
146
        $query->execute();
147
    }
148
149
    /**
150
     * @param string $field
151
     * @param string $key
152
     * @param Condition|null $condition
153
     *
154
     * @return array
155
     * @throws \Exception
156
     */
157
    public function getMap(string $field, string $key = 'id', Condition $condition = null): array
158
    {
159
        $result = [];
160
        $select = $this->selectQuery($condition);
161
        $select->select(['t1.' . $field, 't1.' . $key]);
162 View Code Duplication
        if ($rows = $select->execute()->fetchAll(\PDO::FETCH_ASSOC)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
163
            foreach ($rows as $row) {
164
                $this->unserializeFields($row);
165
                $result[$row[$key]] = $row[$field];
166
            }
167
        }
168
169
        return $result;
170
    }
171
172
    /**
173
     * @param Condition|null $condition
174
     * @return QueryBuilder
175
     */
176
    protected function selectQuery(Condition $condition = null): QueryBuilder
177
    {
178
        $queryBuilder = $this->db->createQueryBuilder();
179
        $queryBuilder->select(['t1.*'])->from($this->table, 't1');
180
        $this->conditionParser->parse($queryBuilder, $condition, $this->map);
181
        return $queryBuilder;
182
    }
183
184
    /**
185
     * Adjust data before save
186
     * @param $data
187
     */
188
    protected function beforeDataSave(&$data)
189
    {
190
    }
191
192
    /**
193
     * @param $data
194
     *
195
     * @return AbstractEntity
196
     */
197
    private function rowToEntity($data)
198
    {
199
        $this->unserializeFields($data);
200
        $entity = $this->factory->createFromData($data, $this->map);
201
        /** @var AbstractEntity $entity */
202
        return $entity;
203
    }
204
205
    /**
206
     * @param $data
207
     */
208
    private function serializeFields(&$data)
209
    {
210
        foreach ($this->serializeFields as $field) {
211
            if (isset($data[$field])) {
212
                $data[$field] = $this->serializeStrategy === self::SERIALIZE_STRATEGY_JSON
213
                    ? \json_encode($data[$field])
214
                    : \serialize($data[$field]);
215
            }
216
        }
217
    }
218
219
    /**
220
     * @param $row
221
     */
222
    private function unserializeFields(&$row)
223
    {
224
        foreach ($this->serializeFields as $field) {
225
            if (isset($row[$field])) {
226
                $row[$field] = $this->serializeStrategy === self::SERIALIZE_STRATEGY_JSON
227
                    ? \json_decode($row[$field], true)
228
                    : \unserialize($row[$field], ['allowed_classes' => true]);
0 ignored issues
show
Unused Code introduced by
The call to unserialize() has too many arguments starting with array('allowed_classes' => true). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

228
                    : /** @scrutinizer ignore-call */ \unserialize($row[$field], ['allowed_classes' => true]);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
229
            }
230
        }
231
    }
232
}
233