You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Entity.php 7.0KB

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