Passed
Push — master ( 6c4562...bd418e )
by y
01:33
created

Table::offsetExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 2
rs 10
cc 1
nc 1
nop 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
        foreach ($columns as $column) {
46
            $this->columns[$column] = $db->factory(Column::class, $db, $column, $this);
47
        }
48
    }
49
50
    /**
51
     * Returns the table name.
52
     *
53
     * @return string
54
     */
55
    final public function __toString () {
56
        return $this->name;
57
    }
58
59
    /**
60
     * `INSERT IGNORE`
61
     *
62
     * @param array $values
63
     * @return int Rows affected.
64
     */
65
    public function apply (array $values): int {
66
        $columns = implode(',', array_keys($values));
67
        $values = $this->db->quoteList($values);
68
        switch ($this->db) {
69
            case 'sqlite':
70
                return $this->db->exec(
71
                    "INSERT OR IGNORE INTO {$this} ({$columns}) VALUES ({$values})"
72
                );
73
            default:
74
                return $this->db->exec(
75
                    "INSERT IGNORE INTO {$this} ({$columns}) VALUES ({$values})"
76
                );
77
        }
78
    }
79
80
    /**
81
     * Caches a prepared query.
82
     *
83
     * @param string $key
84
     * @param Closure $prepare `():Statement`
85
     * @return Statement
86
     */
87
    protected function cache (string $key, Closure $prepare) {
88
        return $this->_cache[$key] ?? $this->_cache[$key] = $prepare->__invoke();
89
    }
90
91
    /**
92
     * @param array $match `[a => b]`
93
     * @return int
94
     */
95
    public function count (array $match = []) {
96
        $select = $this->select(['COUNT(*)']);
97
        foreach ($match as $a => $b) {
98
            $select->where($this->db->match($this[$a] ?? $a, $b));
99
        }
100
        return (int)$select->execute()->fetchColumn();
101
    }
102
103
    /**
104
     * Executes a deletion using arbitrary columns.
105
     *
106
     * @see DB::match()
107
     *
108
     * @param array $match
109
     * @return int Rows affected.
110
     */
111
    public function delete (array $match): int {
112
        foreach ($match as $a => $b) {
113
            $match[$a] = $this->db->match($this[$a] ?? $a, $b);
114
        }
115
        $match = Predicate::all($match);
116
        return $this->db->exec("DELETE FROM {$this} WHERE {$match}");
117
    }
118
119
    /**
120
     * @return Column[]
121
     */
122
    final public function getColumns (): array {
123
        return $this->columns;
124
    }
125
126
    /**
127
     * @return string
128
     */
129
    final public function getName (): string {
130
        return $this->name;
131
    }
132
133
    /**
134
     * Executes an insertion using arbitrary columns.
135
     *
136
     * @param array $values
137
     * @return Statement
138
     */
139
    public function insert (array $values) {
140
        $columns = implode(',', array_keys($values));
141
        $values = $this->db->quoteList($values);
142
        return $this->db->query("INSERT INTO {$this} ($columns) VALUES ($values)");
143
    }
144
145
    /**
146
     * @param int|string $column
147
     * @return Column
148
     */
149
    public function offsetGet ($column) {
150
        if (is_int($column)) {
151
            return current(array_slice($this->columns, $column, 1)) ?: null;
152
        }
153
        return $this->columns[$column] ?? null;
154
    }
155
156
    /**
157
     * Returns a selection object for columns in the table.
158
     *
159
     * @param string[] $columns Defaults to all columns.
160
     * @return Select
161
     */
162
    public function select (array $columns = []) {
163
        if (empty($columns)) {
164
            $columns = $this->columns;
165
        }
166
        return $this->db->factory(Select::class, $this->db, $this, $columns);
167
    }
168
169
    /**
170
     * Returns a clone with a different name. Columns are also re-qualified.
171
     *
172
     * @param string $name
173
     * @return Table
174
     */
175
    public function setName (string $name) {
176
        $clone = clone $this;
177
        $clone->name = $name;
178
        foreach ($this->columns as $name => $column) {
179
            $clone->columns[$name] = $column->setQualifier($clone);
180
        }
181
        return $clone;
182
    }
183
184
    /**
185
     * Executes an update using arbitrary columns.
186
     *
187
     * @see DB::match()
188
     *
189
     * @param array $values
190
     * @param array $match
191
     * @return int Rows affected.
192
     */
193
    public function update (array $values, array $match): int {
194
        foreach ($this->db->quoteArray($values) as $key => $value) {
195
            $values[$key] = "{$key} = {$value}";
196
        }
197
        $values = implode(', ', $values);
198
        foreach ($match as $a => $b) {
199
            $match[$a] = $this->db->match($this[$a] ?? $a, $b);
200
        }
201
        $match = Predicate::all($match);
202
        return $this->db->exec("UPDATE {$this} SET {$values} WHERE {$match}");
203
    }
204
}