Table::__construct()   B
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 12
cts 12
cp 1
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 11
nc 5
nop 1
crap 5
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 for given conditions
96
     *
97
     * ```
98
     * [
99
     *     '{entity_property}' => '{property_value}'
100
     * ]
101
     * ```
102
     *
103
     * @param array $conditions
104
     *
105
     * @throws InvalidEntityPropertyException If entity does not have that property
106
     *
107
     * @return Entity[] Entities
108
     */
109 4
    public function findBy(array $conditions): array
110
    {
111
        /**
112
         * Check if entity have the properties in conditions
113
         *
114
         * @var Entity $entity
115
         */
116 4
        $reflector = new \ReflectionClass($this->entityClass);
117
118 4
        $entity = $reflector->newInstanceWithoutConstructor();
119
120 4
        foreach (array_keys($conditions) as $key) {
121 4
            if (!isset($entity::getColumns()[$key])) {
122 4
                throw new InvalidEntityPropertyException([$this->entityClass, $key]);
123
            }
124
        }
125
126
        /**
127
         * Validate values in conditions
128
         */
129 3
        foreach ($conditions as $key => $value) {
130 3
            $entityProperty = new EntityProperty($entity::getColumns()[$key]);
131 3
            $entityProperty->validateValue($value);
132
        }
133
134
        /**
135
         * Execute with key-value condition
136
         */
137 2
        $parameters = [];
138
139 2
        foreach ($conditions as $key => $value) {
140 2
            $parameters[":$key"] = $value;
141
        }
142
143 2
        $stmt = $this->pdo->prepare($this->sqlCommand->selectFromWhere(array_keys($conditions)));
144 2
        $stmt->execute($parameters);
145
146 2
        return $this->createEntitiesFromStatement($stmt);
147
    }
148
149
    /**
150
     * Insert entity into database
151
     *
152
     * @param Entity $entity Entity object
153
     *
154
     * @return Entity Inserted entity
155
     */
156 1
    public function insert(Entity $entity): Entity
157
    {
158 1
        $stmt = $this->pdo->prepare($this->sqlCommand->insertInto());
159
160 1
        $now = (new \DateTime())->format('Y-m-d H:i:s');
161
162 1
        $array = array_merge($entity->toArray(), [
163 1
            'modified' => $now,
164 1
            'created'  => $now
165
        ]);
166
167 1
        $stmt->execute($array);
168
169 1
        return $entity;
170
    }
171
172
    /**
173
     * Update entity in the database
174
     *
175
     * @param Entity $entity Entity object
176
     *
177
     * @return bool True if row was affected, otherwise false
178
     */
179 1
    public function update(Entity $entity): bool
180
    {
181 1
        if ($entity->isAltered()) {
182
            /**
183
             * Set modified to now
184
             */
185 1
            $entity->set('modified', new \DateTime());
186
187
            /**
188
             * Get altered columns for SQL command
189
             */
190 1
            $alteredColumns = array_keys($entity->getAlteredColumns());
191
192 1
            $stmt = $this->pdo->prepare($this->sqlCommand->updateSetWhere($alteredColumns));
193
194
            /**
195
             * Execute with altered array
196
             */
197 1
            $stmt->execute($entity->alteredToArray(true));
198
199
            // @todo return true if row is altered
200
        }
201
202 1
        return false;
203
    }
204
205
    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...
206
    {
207
        // @todo Implement
208
    }
209
210
    /**
211
     * Creates an array of Entity objects from statement
212
     *
213
     * @param \PDOStatement $statement
214
     *
215
     * @return Entity[] Entities from statement
216
     */
217
    protected function createEntitiesFromStatement(\PDOStatement $statement): array
218
    {
219
        $entities = [];
220
221
        $entityFactory = new EntityFactory($this->entityClass);
222
223
        foreach ($statement as $row) {
224
            $entities[] = $entityFactory->createFromArray($row);
225
        }
226
227
        return $entities;
228
    }
229
230
    /**
231
     * Create SQLCommand for this Table with provided Entity
232
     *
233
     * @return SQLCommand
234
     */
235 1
    protected function createCommand(): SQLCommand
236
    {
237 1
        $reflector = new \ReflectionClass($this->entityClass);
238
239 1
        $entity = $reflector->newInstanceWithoutConstructor();
240
241 1
        $columns = $entity::getColumns();
242
243 1
        return new SQLCommand($this->tableName, $columns);
244
    }
245
}
246