Completed
Push — master ( e8851b...557727 )
by Beñat
03:22
created

SqlFileRepository::update()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 1
1
<?php
2
3
/*
4
 * This file is part of the BenGorFile package.
5
 *
6
 * (c) Beñat Espiña <[email protected]>
7
 * (c) Gorka Laucirica <[email protected]>
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace BenGorFile\File\Infrastructure\Persistence\Sql;
14
15
use BenGorFile\File\Domain\Model\File;
16
use BenGorFile\File\Domain\Model\FileId;
17
use BenGorFile\File\Domain\Model\FileMimeType;
18
use BenGorFile\File\Domain\Model\FileName;
19
use BenGorFile\File\Domain\Model\FileRepository;
20
use BenGorFile\File\Infrastructure\Domain\Model\FileEventBus;
21
22
/**
23
 * Sql file repository class.
24
 *
25
 * @author Beñat Espiña <[email protected]>
26
 * @author Gorka Laucirica <[email protected]>
27
 */
28
final class SqlFileRepository implements FileRepository
29
{
30
    const DATE_FORMAT = 'Y-m-d H:i:s';
31
32
    /**
33
     * The pdo instance.
34
     *
35
     * @var \PDO
36
     */
37
    private $pdo;
38
39
    /**
40
     * The file event bus, it can be null.
41
     *
42
     * @var FileEventBus|null
43
     */
44
    private $eventBus;
45
46
    /**
47
     * Constructor.
48
     *
49
     * @param \PDO              $aPdo       The pdo instance
50
     * @param FileEventBus|null $anEventBus The file event bus, it can be null
51
     */
52
    public function __construct(\PDO $aPdo, FileEventBus $anEventBus = null)
53
    {
54
        $this->pdo = $aPdo;
55
        $this->eventBus = $anEventBus;
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61 View Code Duplication
    public function fileOfId(FileId $anId)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
62
    {
63
        $statement = $this->execute('SELECT * FROM file WHERE id = :id', ['id' => $anId->id()]);
64
        if ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
65
            return $this->buildFile($row);
66
        }
67
    }
68
69
    /**
70
     * {@inheritdoc}
71
     */
72
    public function query($aSpecification)
73
    {
74
        throw new \LogicException('This method is not implemented yet, maybe you can propose a PR :)');
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    public function singleResultQuery($aSpecification)
81
    {
82
        throw new \LogicException('This method is not implemented yet, maybe you can propose a PR :)');
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88
    public function count($aSpecification)
89
    {
90
        return $this->pdo->query('SELECT COUNT(*) FROM file')->fetchColumn();
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96 View Code Duplication
    public function fileOfName(FileName $aName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
97
    {
98
        $statement = $this->execute('SELECT * FROM file WHERE name = :name AND extension = :extension', [
99
            'name'      => $aName->name(),
100
            'extension' => $aName->extension(),
101
        ]);
102
        if ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
103
            return $this->buildFile($row);
104
        }
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110 View Code Duplication
    public function all()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
111
    {
112
        $statement = $this->execute('SELECT * FROM file');
0 ignored issues
show
Bug introduced by
The call to execute() misses a required argument $parameters.

This check looks for function calls that miss required arguments.

Loading history...
113
        if ($rows = $statement->fetch(\PDO::FETCH_ASSOC)) {
114
            return array_map(function ($row) {
115
                return $this->buildFile($row);
116
            }, $rows);
117
        }
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123
    public function persist(File $aFile)
124
    {
125
        ($this->exist($aFile)) ? $this->update($aFile) : $this->insert($aFile);
126
127
        if ($this->eventBus instanceof FileEventBus) {
128
            $this->handle($aFile->events());
129
        }
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135
    public function remove(File $aFile)
136
    {
137
        $this->execute('DELETE FROM file WHERE id = :id', ['id' => $aFile->id()->id()]);
138
139
        if ($this->eventBus instanceof FileEventBus) {
140
            $this->handle($aFile->events());
141
        }
142
    }
143
144
    /**
145
     * Loads the file schema into database create the table
146
     * with file attribute properties as columns.
147
     */
148
    public function initSchema()
149
    {
150
        $this->pdo->exec(<<<'SQL'
151
DROP TABLE IF EXISTS file;
152
CREATE TABLE file (
153
    id CHAR(36) PRIMARY KEY,
154
    name VARCHAR(255) NOT NULL,
155
    extension VARCHAR(100) NOT NULL,
156
    mime_type VARCHAR(255) NOT NULL,
157
    created_on DATETIME NOT NULL,
158
    updated_on DATETIME NOT NULL
159
)
160
SQL
161
        );
162
    }
163
164
    /**
165
     * Checks if the file given exists in the database.
166
     *
167
     * @param File $aFile The file
168
     *
169
     * @return bool
170
     */
171
    private function exist(File $aFile)
172
    {
173
        $count = $this->execute(
174
            'SELECT COUNT(*) FROM file WHERE id = :id', [':id' => $aFile->id()->id()]
175
        )->fetchColumn();
176
177
        return $count === 1;
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $count (string) and 1 (integer) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
178
    }
179
180
    /**
181
     * Prepares the insert SQL with the file given.
182
     *
183
     * @param File $aFile The file
184
     */
185
    private function insert(File $aFile)
186
    {
187
        $sql = 'INSERT INTO file (id, name, extension, mime_type, created_on, updated_on) VALUES (:id, :name, :extension, :mimeType, :createdOn, :updatedOn)';
188
        $this->execute($sql, [
189
            'id'        => $aFile->id()->id(),
190
            'name'      => $aFile->name()->name(),
191
            'extension' => $aFile->name()->extension(),
192
            'mimeType'  => $aFile->mimeType(),
193
            'createdOn' => $aFile->createdOn()->format(self::DATE_FORMAT),
194
            'updatedOn' => $aFile->updatedOn()->format(self::DATE_FORMAT),
195
        ]);
196
    }
197
198
    /**
199
     * Prepares the update SQL with the file given.
200
     *
201
     * @param File $aFile The file
202
     */
203
    private function update(File $aFile)
204
    {
205
        $this->execute('UPDATE file SET name = :name, extension = :extension, mime_type = :mimeType, updated_on = :updatedOn WHERE id = :id', [
206
            'name'      => $aFile->name()->name(),
207
            'extension' => $aFile->name()->extension(),
208
            'mimeType'  => $aFile->mimeType(),
209
            'updatedOn' => $aFile->updatedOn(),
210
            'id'        => $aFile->id()->id(),
211
        ]);
212
    }
213
214
    /**
215
     * Wrapper that encapsulates the same logic about execute the query in PDO.
216
     *
217
     * @param string $aSql       The SQL
218
     * @param array  $parameters Array which contains the parameters of SQL
219
     *
220
     * @return \PDOStatement
221
     */
222
    private function execute($aSql, array $parameters)
223
    {
224
        $statement = $this->pdo->prepare($aSql);
225
        $statement->execute($parameters);
226
227
        return $statement;
228
    }
229
230
    /**
231
     * Builds the file with the given sql row attributes.
232
     *
233
     * @param array $row Array which contains attributes of file
234
     *
235
     * @return File
236
     */
237
    private function buildFile($row)
238
    {
239
        $file = new File(
240
            new FileId($row['id']),
241
            new FileName($row['name'] . '.' . $row['extension']),
242
            new FileMimeType($row['mime_type'])
243
        );
244
245
        $createdOn = new \DateTimeImmutable($row['created_on']);
246
        $updatedOn = new \DateTimeImmutable($row['updated_on']);
247
248
        $file = $this->set($file, 'createdOn', $createdOn);
249
        $file = $this->set($file, 'updatedOn', $updatedOn);
250
251
        return $file;
252
    }
253
254
    /**
255
     * Populates by Reflection the domain object with the given SQL plain values.
256
     *
257
     * @param File   $file          The file domain object
258
     * @param string $propertyName  The property name
259
     * @param mixed  $propertyValue The property value
260
     *
261
     * @return File
262
     */
263
    private function set(File $file, $propertyName, $propertyValue)
264
    {
265
        $reflectionFile = new \ReflectionClass($file);
266
        $reflectionCreatedOn = $reflectionFile->getProperty($propertyName);
267
        $reflectionCreatedOn->setAccessible(true);
268
        $reflectionCreatedOn->setValue($file, $propertyValue);
269
270
        return $file;
271
    }
272
273
    /**
274
     * Handles the given events with event bus.
275
     *
276
     * @param array $events A collection of file domain events
277
     */
278
    private function handle($events)
279
    {
280
        foreach ($events as $event) {
281
            $this->eventBus->handle($event);
282
        }
283
    }
284
}
285