Completed
Pull Request — master (#2)
by René
04:42
created

Table   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 210
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 70.37%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 15
c 3
b 0
f 0
lcom 1
cbo 7
dl 0
loc 210
ccs 38
cts 54
cp 0.7037
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 4
A getTableName() 0 4 1
A findAll() 0 7 1
B findBy() 0 28 2
A insert() 0 17 1
B update() 0 25 2
A delete() 0 4 1
A createEntitiesFromStatement() 0 12 2
A createCommand() 0 10 1
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\Exception\EntityClassNonexistentException;
10
use Zortje\MVC\Model\Table\Entity\Exception\EntityClassNotDefinedException;
11
use Zortje\MVC\Model\Table\Entity\Exception\InvalidEntityPropertyException;
12
use Zortje\MVC\Model\Table\Exception\TableNameNotDefinedException;
13
14
/**
15
 * Class Table
16
 *
17
 * @package Zortje\MVC\Model\Table
18
 */
19
abstract class Table
20
{
21
22
    /**
23
     * @var \PDO Connection
24
     */
25
    protected $pdo;
26
27
    /**
28
     * @var string Table name
29
     */
30
    protected $tableName;
31
32
    /**
33
     * @var String Entity class
34
     */
35
    protected $entityClass;
36
37
    /**
38
     * @var SQLCommand SQL Command
39
     */
40
    protected $sqlCommand;
41
42
    /**
43
     * @param \PDO $pdo
44
     *
45
     * @throws TableNameNotDefinedException If table name is not defined in subclass
46
     * @throws EntityClassNotDefinedException If entity class is not defined in subclass
47
     * @throws EntityClassNonexistentException If entity class is nonexistent
48
     */
49 1
    public function __construct(\PDO $pdo)
50
    {
51 1
        if ($this->tableName === null) {
52
            throw new TableNameNotDefinedException([get_class($this)]);
53
        }
54
55 1
        if ($this->entityClass === null) {
56
            throw new EntityClassNotDefinedException([get_class($this)]);
57 1
        } elseif (!class_exists($this->entityClass)) {
58
            throw new EntityClassNonexistentException([get_class($this), $this->entityClass]);
59
        }
60
61
        // @todo should check if `$this->entityClass` is subclass of Entity class
62
63 1
        $this->pdo = $pdo;
64
65 1
        $this->sqlCommand = $this->createCommand();
66 1
    }
67
68
    /**
69
     * Get table name
70
     *
71
     * @return string Table name
72
     */
73 1
    public function getTableName(): string
74
    {
75 1
        return $this->tableName;
76
    }
77
78
    /**
79
     * Find all entities
80
     *
81
     * @return Entity[] Entities
82
     */
83 1
    public function findAll(): array
84
    {
85 1
        $stmt = $this->pdo->prepare($this->sqlCommand->selectFrom());
86 1
        $stmt->execute();
87
88 1
        return $this->createEntitiesFromStatement($stmt);
89
    }
90
91
    /**
92
     * Find all entities where key is equal to the given value
93
     *
94
     * @param string $key   Entity key
95
     * @param mixed  $value Value to search for
96
     *
97
     * @throws InvalidEntityPropertyException If entity does not have that property
98
     *
99
     * @return Entity[] Entities
100
     */
101 4
    public function findBy(string $key, $value): array
102
    {
103
        /**
104
         * Check if entity have the property
105
         *
106
         * @var Entity $entity
107
         */
108 4
        $reflector = new \ReflectionClass($this->entityClass);
109
110 4
        $entity = $reflector->newInstanceWithoutConstructor();
111
112 4
        if (!isset($entity::getColumns()[$key])) {
113 1
            throw new InvalidEntityPropertyException([$this->entityClass, $key]);
114
        }
115
116
        /**
117
         * Validate value
118
         */
119 3
        $value = $entity->validatePropertyValueType($key, $value);
120
121
        /**
122
         * Execute with key-value condition
123
         */
124 2
        $stmt = $this->pdo->prepare($this->sqlCommand->selectFromWhere([$key]));
125 2
        $stmt->execute([":$key" => $value]);
126
127 2
        return $this->createEntitiesFromStatement($stmt);
128
    }
129
130
    /**
131
     * Insert entity into database
132
     *
133
     * @param Entity $entity Entity object
134
     *
135
     * @return Entity Inserted entity with ID set
136
     */
137 1
    public function insert(Entity $entity): Entity
138
    {
139 1
        $stmt = $this->pdo->prepare($this->sqlCommand->insertInto());
140
141 1
        $now = (new \DateTime())->format('Y-m-d H:i:s');
142
143 1
        $array = array_merge($entity->toArray(false), [
144 1
            'modified' => $now,
145 1
            'created'  => $now
146
        ]);
147
148 1
        $stmt->execute($array);
149
150 1
        $entity->set('id', (int) $this->pdo->lastInsertId());
151
152 1
        return $entity;
153
    }
154
155
    /**
156
     * Update entity in the database
157
     *
158
     * @param Entity $entity Entity object
159
     *
160
     * @return bool True if row was affected, otherwise false
161
     */
162 1
    public function update(Entity $entity): bool
163
    {
164 1
        if ($entity->isAltered()) {
165
            /**
166
             * Set modified to now
167
             */
168 1
            $entity->set('modified', new \DateTime());
169
170
            /**
171
             * Get altered columns for SQL command
172
             */
173 1
            $alteredColumns = array_keys($entity->getAlteredColumns());
174
175 1
            $stmt = $this->pdo->prepare($this->sqlCommand->updateSetWhere($alteredColumns));
176
177
            /**
178
             * Execute with altered array
179
             */
180 1
            $stmt->execute($entity->alteredToArray(true));
181
182
            // @todo return true if row is altered
183
        }
184
185 1
        return false;
186
    }
187
188
    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...
189
    {
190
        // @todo Implement
191
    }
192
193
    /**
194
     * Creates an array of Entity objects from statement
195
     *
196
     * @param \PDOStatement $statement
197
     *
198
     * @return Entity[] Entities from statement
199
     */
200
    protected function createEntitiesFromStatement(\PDOStatement $statement): array
201
    {
202
        $entities = [];
203
204
        $entityFactory = new EntityFactory($this->entityClass);
205
206
        foreach ($statement as $row) {
207
            $entities[] = $entityFactory->createFromArray($row);
208
        }
209
210
        return $entities;
211
    }
212
213
    /**
214
     * Create SQLCommand for this Table with provided Entity
215
     *
216
     * @return SQLCommand
217
     */
218
    protected function createCommand(): SQLCommand
219
    {
220
        $reflector = new \ReflectionClass($this->entityClass);
221
222
        $entity = $reflector->newInstanceWithoutConstructor();
223
224
        $columns = $entity::getColumns();
225
226
        return new SQLCommand($this->tableName, $columns);
227
    }
228
}
229