249337334d68e48deee130f672d02786e54e20a3
[m6w6/pq-gateway] / lib / pq / Gateway / Row.php
1 <?php
2
3 namespace pq\Gateway;
4
5 class Row implements \JsonSerializable
6 {
7 /**
8 * @var \pq\Gateway\Table
9 */
10 protected $table;
11
12 /**
13 * @var array
14 */
15 protected $data;
16
17 /**
18 * @var array
19 */
20 protected $cell = array();
21
22 /**
23 * @param \pq\Gateway\Table $table
24 * @param array $data
25 * @param bool $prime whether to mark all columns as modified
26 */
27 function __construct(Table $table, array $data = null, $prime = false) {
28 $this->table = $table;
29 $this->data = (array) $data;
30
31 if ($prime) {
32 $this->prime();
33 }
34 }
35
36 /**
37 * Copy constructor
38 * @param array $data
39 * @return \pq\Gateway\Row
40 */
41 function __invoke(array $data) {
42 $that = clone $this;
43 $that->data = $data;
44 return $that->prime();
45 }
46
47 /**
48 * Export current state as an array
49 * @return array
50 * @throws \UnexpectedValueException if a cell has been modified by an expression
51 */
52 function export() {
53 $export = array_merge($this->data, $this->cell);
54 foreach ($export as &$val) {
55 if ($val instanceof Cell) {
56 if ($val->isExpr()) {
57 throw new \UnexpectedValueException("Cannot export an SQL expression");
58 }
59 $val = $val->get();
60 }
61 }
62 return $export;
63 }
64
65 /**
66 * Export current state with security sensitive data removed. You should override that, just
67 * calls export() by default.
68 * @return array
69 */
70 function exportPublic() {
71 return $this->export();
72 }
73
74 /**
75 * @implements JsonSerializable
76 * @return array
77 */
78 function jsonSerialize() {
79 return $this->exportPublic();
80 }
81
82 /**
83 * @return \pq\Gateway\Table
84 */
85 function getTable() {
86 return $this->table;
87 }
88
89 /**
90 * @return array
91 */
92 function getData() {
93 return $this->data;
94 }
95
96 /**
97 * Check whether the row contains modifications
98 * @return boolean
99 */
100 function isDirty() {
101 foreach ($this->cell as $cell) {
102 if ($cell->isDirty()) {
103 return true;
104 }
105 }
106 return false;
107 }
108
109 function refresh() {
110 $this->data = $this->table->find($this->criteria(), null, 1, 0)->current()->data;
111 $this->cell = array();
112 return $this;
113 }
114
115 /**
116 * Fill modified cells
117 * @return \pq\Gateway\Row
118 */
119 protected function prime() {
120 $this->cell = array();
121 foreach ($this->data as $key => $val) {
122 $this->cell[$key] = new Cell($this, $key, $val, true);
123 }
124 return $this;
125 }
126
127 /**
128 * Transform data array to where criteria
129 * @return array
130 */
131 protected function criteria() {
132 $where = array();
133 foreach($this->data as $k => $v) {
134 $where["$k="] = $v;
135 }
136 return $where;
137 }
138
139 /**
140 * Get an array of changed properties
141 * @return array
142 */
143 protected function changes() {
144 $changes = array();
145 foreach ($this->cell as $name => $cell) {
146 if ($cell->isDirty()) {
147 $changes[$name] = $cell->get();
148 }
149 }
150 return $changes;
151 }
152
153 /**
154 * Get a cell or parent rows
155 * @param string $p
156 * @return \pq\Gateway\Cell|\pq\Gateway\Rowset
157 */
158 function __get($p) {
159 if ($this->table->hasRelation($p)) {
160 return $this->table->by($this, $p);
161 }
162 if (!isset($this->cell[$p])) {
163 $this->cell[$p] = new Cell($this, $p, isset($this->data[$p]) ? $this->data[$p] : null);
164 }
165 return $this->cell[$p];
166 }
167
168 /**
169 * Set a cell value
170 * @param string $p
171 * @param mixed $v
172 */
173 function __set($p, $v) {
174 $this->__get($p)->set($v);
175 }
176
177 /**
178 * Unset a cell value
179 * @param string $p
180 */
181 function __unset($p) {
182 unset($this->data[$p]);
183 unset($this->cell[$p]);
184 }
185
186 /**
187 * Check if a cell isset
188 * @param string $p
189 * @return bool
190 */
191 function __isset($p) {
192 return isset($this->data[$p]) || isset($this->cell[$p]);
193 }
194
195 /**
196 * Get child rows of this row by foreign key
197 * @see \pq\Gateway\Table::of()
198 * @param string $foreign
199 * @param array $args [order, limit, offset]
200 * @return \pq\Gateway\Rowset
201 */
202 function __call($foreign, array $args) {
203 array_unshift($args, $this);
204 $table = forward_static_call(array(get_class($this->getTable()), "resolve"), $foreign);
205 return call_user_func_array(array($table, "of"), $args);
206 }
207
208 /**
209 * Create this row in the database
210 * @return \pq\Gateway\Row
211 */
212 function create() {
213 $this->data = $this->table->create($this->changes())->current()->data;
214 $this->cell = array();
215 return $this;
216 }
217
218 /**
219 * Update this row in the database
220 * @return \pq\Gateway\Row
221 */
222 function update() {
223 $this->data = $this->table->update($this->criteria(), $this->changes())->current()->data;
224 $this->cell = array();
225 return $this;
226 }
227
228 /**
229 * Delete this row in the database
230 * @return \pq\Gateway\Row
231 */
232 function delete() {
233 $this->data = $this->table->delete($this->criteria(), "*")->current()->data;
234 return $this->prime();
235 }
236 }