Completed
Pull Request — master (#2)
by René
04:27 queued 02:19
created

Table::createEntitiesFromStatement()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 0
cts 6
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 1
crap 6
1
<?php
2
declare(strict_types = 1);
3
4
namespace Zortje\MVC\Model\Table;
5
6
use Zortje\MVC\Model\SQLCommand;
7
use Zortje\MVC\Model\Table\Entity\Entity;
8
use Zortje\MVC\Model\Table\Entity\EntityFactory;
9
use Zortje\MVC\Model\Table\Entity\EntityProperty;
10
use Zortje\MVC\Model\Table\Entity\Exception\EntityClassInvalidSuperclassException;
11
use Zortje\MVC\Model\Table\Entity\Exception\EntityClassNonexistentException;
12
use Zortje\MVC\Model\Table\Entity\Exception\EntityClassNotDefinedException;
13
use Zortje\MVC\Model\Table\Entity\Exception\InvalidEntityPropertyException;
14
use Zortje\MVC\Model\Table\Exception\TableNameNotDefinedException;
15
16
/**
17
 * Class Table
18
 *
19
 * @package Zortje\MVC\Model\Table
20
 */
21
abstract class Table
22
{
23
24
    /**
25
     * @var \PDO Connection
26
     */
27
    protected $pdo;
28
29
    /**
30
     * @var string Table name
31
     */
32
    protected $tableName;
33
34
    /**
35
     * @var String Entity class
36
     */
37
    protected $entityClass;
38
39
    /**
40
     * @var SQLCommand SQL Command
41
     */
42
    protected $sqlCommand;
43
44
    /**
45
     * @param \PDO $pdo
46
     *
47
     * @throws TableNameNotDefinedException If table name is not defined in subclass
48
     * @throws EntityClassNotDefinedException If entity class is not defined in subclass
49
     * @throws EntityClassNonexistentException If entity class is nonexistent
50
     * @throws EntityClassInvalidSuperclassException If entity class is not extending base entity class
51
     */
52 5
    public function __construct(\PDO $pdo)
53
    {
54 5
        if ($this->tableName === null) {
55 1
            throw new TableNameNotDefinedException([get_class($this)]);
56
        }
57
58 4
        if ($this->entityClass === null) {
59 1
            throw new EntityClassNotDefinedException([get_class($this)]);
60 3
        } elseif (!class_exists($this->entityClass)) {
61 1
            throw new EntityClassNonexistentException([get_class($this), $this->entityClass]);
62 2
        } elseif (!is_subclass_of($this->entityClass, Entity::class)) {
63 1
            throw new EntityClassInvalidSuperclassException([$this->entityClass]);
64
        }
65
66 1
        $this->pdo = $pdo;
67
68 1
        $this->sqlCommand = $this->createCommand();
69 1
    }
70
71
    /**
72
     * Get table name
73
     *
74
     * @return string Table name
75
     */
76 1
    public function getTableName(): string
77
    {
78 1
        return $this->tableName;
79
    }
80
81
    /**
82
     * Find all entities
83
     *
84
     * @return Entity[] Entities
85
     */
86 1
    public function findAll(): array
87
    {
88 1
        $stmt = $this->pdo->prepare($this->sqlCommand->selectFrom());
89 1
        $stmt->execute();
90
91 1
        return $this->createEntitiesFromStatement($stmt);
92
    }
93
94
    /**
95
     * Find all entities where key is equal to the given value
96
     *
97
     * @param string $key   Entity key
98
     * @param mixed  $value Value to search for
99
     *
100
     * @throws InvalidEntityPropertyException If entity does not have that property
101
     *
102
     * @return Entity[] Entities
103
     */
104 4
    public function findBy(string $key, $value): array
105
    {
106
        /**
107
         * Check if entity have the property
108
         *
109
         * @var Entity $entity
110
         */
111 4
        $reflector = new \ReflectionClass($this->entityClass);
112
113 4
        $entity = $reflector->newInstanceWithoutConstructor();
114
115 4
        if (!isset($entity::getColumns()[$key])) {
116 1
            throw new InvalidEntityPropertyException([$this->entityClass, $key]);
117
        }
118
119
        /**
120
         * Validate value
121
         */
122 3
        $entityProperty = new EntityProperty($entity::getColumns()[$key]);
123 3
        $entityProperty->validateValue($value);
124
125
        /**
126
         * Execute with key-value condition
127
         */
128 2
        $stmt = $this->pdo->prepare($this->sqlCommand->selectFromWhere([$key]));
129 2
        $stmt->execute([":$key" => $value]);
130
131 2
        return $this->createEntitiesFromStatement($stmt);
132
    }
133
134
    /**
135
     * Insert entity into database
136
     *
137
     * @param Entity $entity Entity object
138
     *
139
     * @return Entity Inserted entity
140
     */
141 1
    public function insert(Entity $entity): Entity
142
    {
143 1
        $stmt = $this->pdo->prepare($this->sqlCommand->insertInto());
144
145 1
        $now = (new \DateTime())->format('Y-m-d H:i:s');
146
147 1
        $array = array_merge($entity->toArray(), [
148 1
            'modified' => $now,
149 1
            'created'  => $now
150
        ]);
151
152 1
        $stmt->execute($array);
153
154 1
        return $entity;
155
    }
156
157
    /**
158
     * Update entity in the database
159
     *
160
     * @param Entity $entity Entity object
161
     *
162
     * @return bool True if row was affected, otherwise false
163
     */
164 1
    public function update(Entity $entity): bool
165
    {
166 1
        if ($entity->isAltered()) {
167
            /**
168
             * Set modified to now
169
             */
170 1
            $entity->set('modified', new \DateTime());
171
172
            /**
173
             * Get altered columns for SQL command
174
             */
175 1
            $alteredColumns = array_keys($entity->getAlteredColumns());
176
177 1
            $stmt = $this->pdo->prepare($this->sqlCommand->updateSetWhere($alteredColumns));
178
179
            /**
180
             * Execute with altered array
181
             */
182 1
            $stmt->execute($entity->alteredToArray(true));
183
184
            // @todo return true if row is altered
185
        }
186
187 1
        return false;
188
    }
189
190
    public function delete(Entity $entity)
0 ignored issues
show
Unused Code introduced by
The parameter $entity is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
191
    {
192
        // @todo Implement
193
    }
194
195
    /**
196
     * Creates an array of Entity objects from statement
197
     *
198
     * @param \PDOStatement $statement
199
     *
200
     * @return Entity[] Entities from statement
201
     */
202
    protected function createEntitiesFromStatement(\PDOStatement $statement): array
203
    {
204
        $entities = [];
205
206
        $entityFactory = new EntityFactory($this->entityClass);
207
208
        foreach ($statement as $row) {
209
            $entities[] = $entityFactory->createFromArray($row);
210
        }
211
212
        return $entities;
213
    }
214
215
    /**
216
     * Create SQLCommand for this Table with provided Entity
217
     *
218
     * @return SQLCommand
219
     */
220 1
    protected function createCommand(): SQLCommand
221
    {
222 1
        $reflector = new \ReflectionClass($this->entityClass);
223
224 1
        $entity = $reflector->newInstanceWithoutConstructor();
225
226 1
        $columns = $entity::getColumns();
227
228 1
        return new SQLCommand($this->tableName, $columns);
229
    }
230
}
231