Data.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. <?php
  2. /**
  3. * @package Grav\Common\Data
  4. *
  5. * @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Common\Data;
  9. use RocketTheme\Toolbox\ArrayTraits\Countable;
  10. use RocketTheme\Toolbox\ArrayTraits\Export;
  11. use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
  12. use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
  13. use RocketTheme\Toolbox\File\File;
  14. use RocketTheme\Toolbox\File\FileInterface;
  15. class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable, ExportInterface
  16. {
  17. use NestedArrayAccessWithGetters, Countable, Export;
  18. /** @var string */
  19. protected $gettersVariable = 'items';
  20. /** @var array */
  21. protected $items;
  22. /** @var Blueprint */
  23. protected $blueprints;
  24. /** @var File */
  25. protected $storage;
  26. /**
  27. * @param array $items
  28. * @param Blueprint|callable $blueprints
  29. */
  30. public function __construct(array $items = [], $blueprints = null)
  31. {
  32. $this->items = $items;
  33. $this->blueprints = $blueprints;
  34. }
  35. /**
  36. * Get value by using dot notation for nested arrays/objects.
  37. *
  38. * @example $value = $data->value('this.is.my.nested.variable');
  39. *
  40. * @param string $name Dot separated path to the requested value.
  41. * @param mixed $default Default value (or null).
  42. * @param string $separator Separator, defaults to '.'
  43. * @return mixed Value.
  44. */
  45. public function value($name, $default = null, $separator = '.')
  46. {
  47. return $this->get($name, $default, $separator);
  48. }
  49. /**
  50. * Join nested values together by using blueprints.
  51. *
  52. * @param string $name Dot separated path to the requested value.
  53. * @param mixed $value Value to be joined.
  54. * @param string $separator Separator, defaults to '.'
  55. * @return $this
  56. * @throws \RuntimeException
  57. */
  58. public function join($name, $value, $separator = '.')
  59. {
  60. $old = $this->get($name, null, $separator);
  61. if ($old !== null) {
  62. if (!\is_array($old)) {
  63. throw new \RuntimeException('Value ' . $old);
  64. }
  65. if (\is_object($value)) {
  66. $value = (array) $value;
  67. } elseif (!\is_array($value)) {
  68. throw new \RuntimeException('Value ' . $value);
  69. }
  70. $value = $this->blueprints()->mergeData($old, $value, $name, $separator);
  71. }
  72. $this->set($name, $value, $separator);
  73. return $this;
  74. }
  75. /**
  76. * Get nested structure containing default values defined in the blueprints.
  77. *
  78. * Fields without default value are ignored in the list.
  79. * @return array
  80. */
  81. public function getDefaults()
  82. {
  83. return $this->blueprints()->getDefaults();
  84. }
  85. /**
  86. * Set default values by using blueprints.
  87. *
  88. * @param string $name Dot separated path to the requested value.
  89. * @param mixed $value Value to be joined.
  90. * @param string $separator Separator, defaults to '.'
  91. * @return $this
  92. */
  93. public function joinDefaults($name, $value, $separator = '.')
  94. {
  95. if (\is_object($value)) {
  96. $value = (array) $value;
  97. }
  98. $old = $this->get($name, null, $separator);
  99. if ($old !== null) {
  100. $value = $this->blueprints()->mergeData($value, $old, $name, $separator);
  101. }
  102. $this->set($name, $value, $separator);
  103. return $this;
  104. }
  105. /**
  106. * Get value from the configuration and join it with given data.
  107. *
  108. * @param string $name Dot separated path to the requested value.
  109. * @param array|object $value Value to be joined.
  110. * @param string $separator Separator, defaults to '.'
  111. * @return array
  112. * @throws \RuntimeException
  113. */
  114. public function getJoined($name, $value, $separator = '.')
  115. {
  116. if (\is_object($value)) {
  117. $value = (array) $value;
  118. } elseif (!\is_array($value)) {
  119. throw new \RuntimeException('Value ' . $value);
  120. }
  121. $old = $this->get($name, null, $separator);
  122. if ($old === null) {
  123. // No value set; no need to join data.
  124. return $value;
  125. }
  126. if (!\is_array($old)) {
  127. throw new \RuntimeException('Value ' . $old);
  128. }
  129. // Return joined data.
  130. return $this->blueprints()->mergeData($old, $value, $name, $separator);
  131. }
  132. /**
  133. * Merge two configurations together.
  134. *
  135. * @param array $data
  136. * @return $this
  137. */
  138. public function merge(array $data)
  139. {
  140. $this->items = $this->blueprints()->mergeData($this->items, $data);
  141. return $this;
  142. }
  143. /**
  144. * Set default values to the configuration if variables were not set.
  145. *
  146. * @param array $data
  147. * @return $this
  148. */
  149. public function setDefaults(array $data)
  150. {
  151. $this->items = $this->blueprints()->mergeData($data, $this->items);
  152. return $this;
  153. }
  154. /**
  155. * Validate by blueprints.
  156. *
  157. * @return $this
  158. * @throws \Exception
  159. */
  160. public function validate()
  161. {
  162. $this->blueprints()->validate($this->items);
  163. return $this;
  164. }
  165. /**
  166. * @return $this
  167. */
  168. public function filter()
  169. {
  170. $args = func_get_args();
  171. $missingValuesAsNull = (bool)(array_shift($args) ?: false);
  172. $keepEmptyValues = (bool)(array_shift($args) ?: false);
  173. $this->items = $this->blueprints()->filter($this->items, $missingValuesAsNull, $keepEmptyValues);
  174. return $this;
  175. }
  176. /**
  177. * Get extra items which haven't been defined in blueprints.
  178. *
  179. * @return array
  180. */
  181. public function extra()
  182. {
  183. return $this->blueprints()->extra($this->items);
  184. }
  185. /**
  186. * Return blueprints.
  187. *
  188. * @return Blueprint
  189. */
  190. public function blueprints()
  191. {
  192. if (!$this->blueprints){
  193. $this->blueprints = new Blueprint;
  194. } elseif (\is_callable($this->blueprints)) {
  195. // Lazy load blueprints.
  196. $blueprints = $this->blueprints;
  197. $this->blueprints = $blueprints();
  198. }
  199. return $this->blueprints;
  200. }
  201. /**
  202. * Save data if storage has been defined.
  203. * @throws \RuntimeException
  204. */
  205. public function save()
  206. {
  207. $file = $this->file();
  208. if ($file) {
  209. $file->save($this->items);
  210. }
  211. }
  212. /**
  213. * Returns whether the data already exists in the storage.
  214. *
  215. * NOTE: This method does not check if the data is current.
  216. *
  217. * @return bool
  218. */
  219. public function exists()
  220. {
  221. $file = $this->file();
  222. return $file && $file->exists();
  223. }
  224. /**
  225. * Return unmodified data as raw string.
  226. *
  227. * NOTE: This function only returns data which has been saved to the storage.
  228. *
  229. * @return string
  230. */
  231. public function raw()
  232. {
  233. $file = $this->file();
  234. return $file ? $file->raw() : '';
  235. }
  236. /**
  237. * Set or get the data storage.
  238. *
  239. * @param FileInterface $storage Optionally enter a new storage.
  240. * @return FileInterface
  241. */
  242. public function file(FileInterface $storage = null)
  243. {
  244. if ($storage) {
  245. $this->storage = $storage;
  246. }
  247. return $this->storage;
  248. }
  249. public function jsonSerialize()
  250. {
  251. return $this->items;
  252. }
  253. }