Completed
Push — master ( 2e76ce...3b64a8 )
by Beñat
02:16
created

SqlFileRepository::remove()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
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 count($aSpecification)
81
    {
82
        return $this->pdo->query('SELECT COUNT(*) FROM file')->fetchColumn();
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88 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...
89
    {
90
        $statement = $this->execute('SELECT * FROM file WHERE name = :name AND extension = :extension', [
91
            'name'      => $aName->name(),
92
            'extension' => $aName->extension(),
93
        ]);
94
        if ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
95
            return $this->buildFile($row);
96
        }
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102 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...
103
    {
104
        $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...
105
        if ($rows = $statement->fetch(\PDO::FETCH_ASSOC)) {
106
            return array_map(function ($row) {
107
                return $this->buildFile($row);
108
            }, $rows);
109
        }
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115
    public function persist(File $aFile)
116
    {
117
        ($this->exist($aFile)) ? $this->update($aFile) : $this->insert($aFile);
118
119
        if ($this->eventBus instanceof FileEventBus) {
120
            $this->handle($aFile->events());
121
        }
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127
    public function remove(File $aFile)
128
    {
129
        $this->execute('DELETE FROM file WHERE id = :id', ['id' => $aFile->id()->id()]);
130
131
        if ($this->eventBus instanceof FileEventBus) {
132
            $this->handle($aFile->events());
133
        }
134
    }
135
136
    /**
137
     * Loads the file schema into database create the table
138
     * with file attribute properties as columns.
139
     */
140
    public function initSchema()
141
    {
142
        $this->pdo->exec(<<<'SQL'
143
DROP TABLE IF EXISTS file;
144
CREATE TABLE file (
145
    id CHAR(36) PRIMARY KEY,
146
    name VARCHAR(255) NOT NULL,
147
    extension VARCHAR(100) NOT NULL,
148
    mime_type VARCHAR(255) NOT NULL,
149
    created_on DATETIME NOT NULL,
150
    updated_on DATETIME NOT NULL
151
)
152
SQL
153
        );
154
    }
155
156
    /**
157
     * Checks if the file given exists in the database.
158
     *
159
     * @param File $aFile The file
160
     *
161
     * @return bool
162
     */
163
    private function exist(File $aFile)
164
    {
165
        $count = $this->execute(
166
            'SELECT COUNT(*) FROM file WHERE id = :id', [':id' => $aFile->id()->id()]
167
        )->fetchColumn();
168
169
        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...
170
    }
171
172
    /**
173
     * Prepares the insert SQL with the file given.
174
     *
175
     * @param File $aFile The file
176
     */
177
    private function insert(File $aFile)
178
    {
179
        $sql = 'INSERT INTO file (id, name, extension, mime_type, created_on, updated_on) VALUES (:id, :name, :extension, :mimeType, :createdOn, :updatedOn)';
180
        $this->execute($sql, [
181
            'id'        => $aFile->id()->id(),
182
            'name'      => $aFile->name()->name(),
183
            'extension' => $aFile->name()->extension(),
184
            'mimeType'  => $aFile->mimeType(),
185
            'createdOn' => $aFile->createdOn()->format(self::DATE_FORMAT),
186
            'updatedOn' => $aFile->updatedOn()->format(self::DATE_FORMAT),
187
        ]);
188
    }
189
190
    /**
191
     * Prepares the update SQL with the file given.
192
     *
193
     * @param File $aFile The file
194
     */
195
    private function update(File $aFile)
196
    {
197
        $this->execute('UPDATE file SET name = :name, extension = :extension, mime_type = :mimeType, updated_on = :updatedOn WHERE id = :id', [
198
            'name'      => $aFile->name()->name(),
199
            'extension' => $aFile->name()->extension(),
200
            'mimeType'  => $aFile->mimeType(),
201
            'updatedOn' => $aFile->updatedOn(),
202
            'id'        => $aFile->id()->id(),
203
        ]);
204
    }
205
206
    /**
207
     * Wrapper that encapsulates the same logic about execute the query in PDO.
208
     *
209
     * @param string $aSql       The SQL
210
     * @param array  $parameters Array which contains the parameters of SQL
211
     *
212
     * @return \PDOStatement
213
     */
214
    private function execute($aSql, array $parameters)
215
    {
216
        $statement = $this->pdo->prepare($aSql);
217
        $statement->execute($parameters);
218
219
        return $statement;
220
    }
221
222
    /**
223
     * Builds the file with the given sql row attributes.
224
     *
225
     * @param array $row Array which contains attributes of file
226
     *
227
     * @return File
228
     */
229
    private function buildFile($row)
230
    {
231
        $file = new File(
232
            new FileId($row['id']),
233
            new FileName($row['name'] . '.' . $row['extension']),
234
            new FileMimeType($row['mime_type'])
235
        );
236
237
        $createdOn = new \DateTimeImmutable($row['created_on']);
238
        $updatedOn = new \DateTimeImmutable($row['updated_on']);
239
240
        $file = $this->set($file, 'createdOn', $createdOn);
241
        $file = $this->set($file, 'updatedOn', $updatedOn);
242
243
        return $file;
244
    }
245
246
    /**
247
     * Populates by Reflection the domain object with the given SQL plain values.
248
     *
249
     * @param File   $file          The file domain object
250
     * @param string $propertyName  The property name
251
     * @param mixed  $propertyValue The property value
252
     *
253
     * @return File
254
     */
255
    private function set(File $file, $propertyName, $propertyValue)
256
    {
257
        $reflectionFile = new \ReflectionClass($file);
258
        $reflectionCreatedOn = $reflectionFile->getProperty($propertyName);
259
        $reflectionCreatedOn->setAccessible(true);
260
        $reflectionCreatedOn->setValue($file, $propertyValue);
261
262
        return $file;
263
    }
264
265
    /**
266
     * Handles the given events with event bus.
267
     *
268
     * @param array $events A collection of file domain events
269
     */
270
    private function handle($events)
271
    {
272
        foreach ($events as $event) {
273
            $this->eventBus->handle($event);
274
        }
275
    }
276
}
277