Passed
Push — master ( 6df7c9...74bc2a )
by y
02:15
created

Table   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 193
Duplicated Lines 0 %

Importance

Changes 6
Bugs 0 Features 1
Metric Value
wmc 22
eloc 50
c 6
b 0
f 1
dl 0
loc 193
rs 10

14 Methods

Rating   Name   Duplication   Size   Complexity  
A cache() 0 2 1
A setName() 0 7 2
A count() 0 6 2
A insert() 0 4 1
A apply() 0 11 2
A offsetExists() 0 2 1
A __construct() 0 6 2
A update() 0 10 3
A getColumns() 0 2 1
A select() 0 5 2
A getName() 0 2 1
A delete() 0 6 2
A __toString() 0 2 1
A offsetGet() 0 2 1
1
<?php
2
3
namespace Helix\DB;
4
5
use Closure;
6
use Helix\DB;
7
use Helix\DB\SQL\Predicate;
8
9
/**
10
 * Table manipulation using arrays.
11
 *
12
 * Accessing the table as an array produces {@link Column} instances.
13
 *
14
 * @immutable Mutations operate on and return clones.
15
 */
16
class Table extends AbstractTable {
17
18
    /**
19
     * Prepared query cache.
20
     *
21
     * @var Statement[]
22
     */
23
    protected $_cache = [];
24
25
    /**
26
     * `[name => Column]`
27
     *
28
     * @var Column[]
29
     */
30
    protected $columns = [];
31
32
    /**
33
     * @var string
34
     */
35
    protected $name;
36
37
    /**
38
     * @param DB $db
39
     * @param string $name
40
     * @param string[] $columns
41
     */
42
    public function __construct (DB $db, $name, array $columns) {
43
        parent::__construct($db);
44
        $this->name = $name;
45
        asort($columns);
46
        foreach ($columns as $column) {
47
            $this->columns[$column] = new Column($db, $column, $this);
48
        }
49
    }
50
51
    /**
52
     * Returns the table name.
53
     *
54
     * @return string
55
     */
56
    final public function __toString () {
57
        return $this->name;
58
    }
59
60
    /**
61
     * `INSERT IGNORE`
62
     *
63
     * @param array $values
64
     * @return int Rows affected.
65
     */
66
    public function apply (array $values): int {
67
        $columns = implode(',', array_keys($values));
68
        $values = $this->db->quoteList($values);
69
        switch ($this->db) {
70
            case 'sqlite':
71
                return $this->db->exec(
72
                    "INSERT OR IGNORE INTO {$this} ({$columns}) VALUES ({$values})"
73
                );
74
            default:
75
                return $this->db->exec(
76
                    "INSERT IGNORE INTO {$this} ({$columns}) VALUES ({$values})"
77
                );
78
        }
79
    }
80
81
    /**
82
     * Caches a prepared query.
83
     *
84
     * @param string $key
85
     * @param Closure $prepare `():Statement`
86
     * @return Statement
87
     */
88
    protected function cache (string $key, Closure $prepare) {
89
        return $this->_cache[$key] ?? $this->_cache[$key] = $prepare->__invoke();
90
    }
91
92
    /**
93
     * @param array $match `[a => b]`
94
     * @return int
95
     */
96
    public function count (array $match = []) {
97
        $select = $this->select(['COUNT(*)']);
98
        foreach ($match as $a => $b) {
99
            $select->where($this->db->match($this[$a] ?? $a, $b));
100
        }
101
        return (int)$select->execute()->fetchColumn();
102
    }
103
104
    /**
105
     * Executes a deletion using arbitrary columns.
106
     *
107
     * @see DB::match()
108
     *
109
     * @param array $match
110
     * @return int Rows affected.
111
     */
112
    public function delete (array $match): int {
113
        foreach ($match as $a => $b) {
114
            $match[$a] = $this->db->match($this[$a] ?? $a, $b);
115
        }
116
        $match = Predicate::all($match);
117
        return $this->db->exec("DELETE FROM {$this} WHERE {$match}");
118
    }
119
120
    /**
121
     * @return Column[]
122
     */
123
    final public function getColumns (): array {
124
        return $this->columns;
125
    }
126
127
    /**
128
     * @return string
129
     */
130
    final public function getName (): string {
131
        return $this->name;
132
    }
133
134
    /**
135
     * Executes an insertion using arbitrary columns.
136
     *
137
     * @param array $values
138
     * @return Statement
139
     */
140
    public function insert (array $values) {
141
        $columns = implode(',', array_keys($values));
142
        $values = $this->db->quoteList($values);
143
        return $this->db->query("INSERT INTO {$this} ($columns) VALUES ($values)");
144
    }
145
146
    /**
147
     * @param string $name
148
     * @return bool
149
     */
150
    public function offsetExists ($name): bool {
151
        return isset($this->columns[$name]);
152
    }
153
154
    /**
155
     * @param string $name
156
     * @return Column
157
     */
158
    public function offsetGet ($name) {
159
        return $this->columns[$name];
160
    }
161
162
    /**
163
     * Returns a selection object for columns in the table.
164
     *
165
     * @param string[] $columns Defaults to all columns.
166
     * @return Select
167
     */
168
    public function select (array $columns = []) {
169
        if (empty($columns)) {
170
            $columns = $this->columns;
171
        }
172
        return new Select($this->db, $this, $columns);
173
    }
174
175
    /**
176
     * Returns a clone with a different name. Columns are also re-qualified.
177
     *
178
     * @param string $name
179
     * @return Table
180
     */
181
    public function setName (string $name) {
182
        $clone = clone $this;
183
        $clone->name = $name;
184
        foreach ($this->columns as $name => $column) {
185
            $clone->columns[$name] = $column->setQualifier($clone);
186
        }
187
        return $clone;
188
    }
189
190
    /**
191
     * Executes an update using arbitrary columns.
192
     *
193
     * @see DB::match()
194
     *
195
     * @param array $values
196
     * @param array $match
197
     * @return int Rows affected.
198
     */
199
    public function update (array $values, array $match): int {
200
        foreach ($this->db->quoteArray($values) as $key => $value) {
201
            $values[$key] = "{$key} = {$value}";
202
        }
203
        $values = implode(', ', $values);
204
        foreach ($match as $a => $b) {
205
            $match[$a] = $this->db->match($this[$a] ?? $a, $b);
206
        }
207
        $match = Predicate::all($match);
208
        return $this->db->exec("UPDATE {$this} SET {$values} WHERE {$match}");
209
    }
210
}