Passed
Push — master ( 6a14f8...ed2d6e )
by Morris
14:56 queued 12s
created

Entity::setter()   B

Complexity

Conditions 8
Paths 9

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 17
c 0
b 0
f 0
nc 9
nop 2
dl 0
loc 28
rs 8.4444
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Bernhard Posselt <[email protected]>
6
 * @author Christoph Wurst <[email protected]>
7
 * @author Daniel Kesselberg <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 *
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program. If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
26
namespace OCP\AppFramework\Db;
27
28
use function lcfirst;
29
use function substr;
30
31
/**
32
 * @method integer getId()
33
 * @method void setId(integer $id)
34
 * @since 7.0.0
35
 */
36
abstract class Entity {
37
	public $id;
38
39
	private $_updatedFields = [];
40
	private $_fieldTypes = ['id' => 'integer'];
41
42
43
	/**
44
	 * Simple alternative constructor for building entities from a request
45
	 * @param array $params the array which was obtained via $this->params('key')
46
	 * in the controller
47
	 * @return Entity
48
	 * @since 7.0.0
49
	 */
50
	public static function fromParams(array $params) {
51
		$instance = new static();
52
53
		foreach ($params as $key => $value) {
54
			$method = 'set' . ucfirst($key);
55
			$instance->$method($value);
56
		}
57
58
		return $instance;
59
	}
60
61
62
	/**
63
	 * Maps the keys of the row array to the attributes
64
	 * @param array $row the row to map onto the entity
65
	 * @since 7.0.0
66
	 */
67
	public static function fromRow(array $row) {
68
		$instance = new static();
69
70
		foreach ($row as $key => $value) {
71
			$prop = ucfirst($instance->columnToProperty($key));
72
			$setter = 'set' . $prop;
73
			$instance->$setter($value);
74
		}
75
76
		$instance->resetUpdatedFields();
77
78
		return $instance;
79
	}
80
81
82
	/**
83
	 * @return array with attribute and type
84
	 * @since 7.0.0
85
	 */
86
	public function getFieldTypes() {
87
		return $this->_fieldTypes;
88
	}
89
90
91
	/**
92
	 * Marks the entity as clean needed for setting the id after the insertion
93
	 * @since 7.0.0
94
	 */
95
	public function resetUpdatedFields() {
96
		$this->_updatedFields = [];
97
	}
98
99
	/**
100
	 * Generic setter for properties
101
	 * @since 7.0.0
102
	 */
103
	protected function setter($name, $args) {
104
		// setters should only work for existing attributes
105
		if (property_exists($this, $name)) {
106
			if ($this->$name === $args[0]) {
107
				return;
108
			}
109
			$this->markFieldUpdated($name);
110
111
			// if type definition exists, cast to correct type
112
			if ($args[0] !== null && array_key_exists($name, $this->_fieldTypes)) {
113
				$type = $this->_fieldTypes[$name];
114
				if ($type === 'blob') {
115
					// (B)LOB is treated as string when we read from the DB
116
					$type = 'string';
117
				}
118
119
				if ($type === 'datetime') {
120
					if (!$args[0] instanceof \DateTime) {
121
						$args[0] = new \DateTime($args[0]);
122
					}
123
				} else {
124
					settype($args[0], $type);
125
				}
126
			}
127
			$this->$name = $args[0];
128
		} else {
129
			throw new \BadFunctionCallException($name .
130
				' is not a valid attribute');
131
		}
132
	}
133
134
	/**
135
	 * Generic getter for properties
136
	 * @since 7.0.0
137
	 */
138
	protected function getter($name) {
139
		// getters should only work for existing attributes
140
		if (property_exists($this, $name)) {
141
			return $this->$name;
142
		} else {
143
			throw new \BadFunctionCallException($name .
144
				' is not a valid attribute');
145
		}
146
	}
147
148
149
	/**
150
	 * Each time a setter is called, push the part after set
151
	 * into an array: for instance setId will save Id in the
152
	 * updated fields array so it can be easily used to create the
153
	 * getter method
154
	 * @since 7.0.0
155
	 */
156
	public function __call($methodName, $args) {
157
		if (strpos($methodName, 'set') === 0) {
158
			$this->setter(lcfirst(substr($methodName, 3)), $args);
159
		} elseif (strpos($methodName, 'get') === 0) {
160
			return $this->getter(lcfirst(substr($methodName, 3)));
161
		} elseif ($this->isGetterForBoolProperty($methodName)) {
162
			return $this->getter(lcfirst(substr($methodName, 2)));
163
		} else {
164
			throw new \BadFunctionCallException($methodName .
165
				' does not exist');
166
		}
167
	}
168
169
	/**
170
	 * @param string $methodName
171
	 * @return bool
172
	 * @since 18.0.0
173
	 */
174
	protected function isGetterForBoolProperty(string $methodName): bool {
175
		if (strpos($methodName, 'is') === 0) {
176
			$fieldName = lcfirst(substr($methodName, 2));
177
			return isset($this->_fieldTypes[$fieldName]) && strpos($this->_fieldTypes[$fieldName], 'bool') === 0;
178
		}
179
		return false;
180
	}
181
182
	/**
183
	 * Mark am attribute as updated
184
	 * @param string $attribute the name of the attribute
185
	 * @since 7.0.0
186
	 */
187
	protected function markFieldUpdated($attribute) {
188
		$this->_updatedFields[$attribute] = true;
189
	}
190
191
192
	/**
193
	 * Transform a database columnname to a property
194
	 * @param string $columnName the name of the column
195
	 * @return string the property name
196
	 * @since 7.0.0
197
	 */
198
	public function columnToProperty($columnName) {
199
		$parts = explode('_', $columnName);
200
		$property = null;
201
202
		foreach ($parts as $part) {
203
			if ($property === null) {
204
				$property = $part;
205
			} else {
206
				$property .= ucfirst($part);
207
			}
208
		}
209
210
		return $property;
211
	}
212
213
214
	/**
215
	 * Transform a property to a database column name
216
	 * @param string $property the name of the property
217
	 * @return string the column name
218
	 * @since 7.0.0
219
	 */
220
	public function propertyToColumn($property) {
221
		$parts = preg_split('/(?=[A-Z])/', $property);
222
		$column = null;
223
224
		foreach ($parts as $part) {
225
			if ($column === null) {
226
				$column = $part;
227
			} else {
228
				$column .= '_' . lcfirst($part);
229
			}
230
		}
231
232
		return $column;
233
	}
234
235
236
	/**
237
	 * @return array array of updated fields for update query
238
	 * @since 7.0.0
239
	 */
240
	public function getUpdatedFields() {
241
		return $this->_updatedFields;
242
	}
243
244
245
	/**
246
	 * Adds type information for a field so that its automatically casted to
247
	 * that value once its being returned from the database
248
	 * @param string $fieldName the name of the attribute
249
	 * @param string $type the type which will be used to call settype()
250
	 * @since 7.0.0
251
	 */
252
	protected function addType($fieldName, $type) {
253
		$this->_fieldTypes[$fieldName] = $type;
254
	}
255
256
257
	/**
258
	 * Slugify the value of a given attribute
259
	 * Warning: This doesn't result in a unique value
260
	 * @param string $attributeName the name of the attribute, which value should be slugified
261
	 * @return string slugified value
262
	 * @since 7.0.0
263
	 */
264
	public function slugify($attributeName) {
265
		// toSlug should only work for existing attributes
266
		if (property_exists($this, $attributeName)) {
267
			$value = $this->$attributeName;
268
			// replace everything except alphanumeric with a single '-'
269
			$value = preg_replace('/[^A-Za-z0-9]+/', '-', $value);
270
			$value = strtolower($value);
271
			// trim '-'
272
			return trim($value, '-');
273
		} else {
274
			throw new \BadFunctionCallException($attributeName .
275
				' is not a valid attribute');
276
		}
277
	}
278
}
279