PageRenderTime 27ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Dojo/Data.php

https://github.com/mfairchild365/zf2
PHP | 520 lines | 241 code | 54 blank | 225 comment | 36 complexity | 5253630895bc7f4cdffa1f70da3319ce MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Dojo
  17. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. */
  20. /**
  21. * @namespace
  22. */
  23. namespace Zend\Dojo;
  24. use Zend\Json\Json;
  25. /**
  26. * dojo.data support for Zend Framework
  27. *
  28. * @uses ArrayAccess
  29. * @uses IteratorAggregate
  30. * @uses Countable
  31. * @uses \Zend\Dojo\Exception
  32. * @uses \Zend\Json\Json
  33. * @package Zend_Dojo
  34. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  35. * @license http://framework.zend.com/license/new-bsd New BSD License
  36. */
  37. class Data implements \ArrayAccess,\IteratorAggregate,\Countable
  38. {
  39. /**
  40. * Identifier field of item
  41. * @var string|int
  42. */
  43. protected $_identifier;
  44. /**
  45. * Collected items
  46. * @var array
  47. */
  48. protected $_items = array();
  49. /**
  50. * Label field of item
  51. * @var string
  52. */
  53. protected $_label;
  54. /**
  55. * Data container metadata
  56. * @var array
  57. */
  58. protected $_metadata = array();
  59. /**
  60. * Constructor
  61. *
  62. * @param string|null $identifier
  63. * @param array|Traversable|null $items
  64. * @param string|null $label
  65. * @return void
  66. */
  67. public function __construct($identifier = null, $items = null, $label = null)
  68. {
  69. if (null !== $identifier) {
  70. $this->setIdentifier($identifier);
  71. }
  72. if (null !== $items) {
  73. $this->setItems($items);
  74. }
  75. if (null !== $label) {
  76. $this->setLabel($label);
  77. }
  78. }
  79. /**
  80. * Set the items to collect
  81. *
  82. * @param array|Traversable $items
  83. * @return \Zend\Dojo\Data
  84. */
  85. public function setItems($items)
  86. {
  87. $this->clearItems();
  88. return $this->addItems($items);
  89. }
  90. /**
  91. * Set an individual item, optionally by identifier (overwrites)
  92. *
  93. * @param array|object $item
  94. * @param string|null $identifier
  95. * @return \Zend\Dojo\Data
  96. */
  97. public function setItem($item, $id = null)
  98. {
  99. $item = $this->_normalizeItem($item, $id);
  100. $this->_items[$item['id']] = $item['data'];
  101. return $this;
  102. }
  103. /**
  104. * Add an individual item, optionally by identifier
  105. *
  106. * @param array|object $item
  107. * @param string|null $id
  108. * @return \Zend\Dojo\Data
  109. */
  110. public function addItem($item, $id = null)
  111. {
  112. $item = $this->_normalizeItem($item, $id);
  113. if ($this->hasItem($item['id'])) {
  114. throw new Exception\InvalidArgumentException('Overwriting items using addItem() is not allowed');
  115. }
  116. $this->_items[$item['id']] = $item['data'];
  117. return $this;
  118. }
  119. /**
  120. * Add multiple items at once
  121. *
  122. * @param array|Traversable $items
  123. * @return \Zend\Dojo\Data
  124. */
  125. public function addItems($items)
  126. {
  127. if (!is_array($items) && (!is_object($items) || !($items instanceof \Traversable))) {
  128. throw new Exception\InvalidArgumentException('Only arrays and Traversable objects may be added to ' . __CLASS__);
  129. }
  130. foreach ($items as $item) {
  131. $this->addItem($item);
  132. }
  133. return $this;
  134. }
  135. /**
  136. * Get all items as an array
  137. *
  138. * Serializes items to arrays.
  139. *
  140. * @return array
  141. */
  142. public function getItems()
  143. {
  144. return $this->_items;
  145. }
  146. /**
  147. * Does an item with the given identifier exist?
  148. *
  149. * @param string|int $id
  150. * @return bool
  151. */
  152. public function hasItem($id)
  153. {
  154. return array_key_exists($id, $this->_items);
  155. }
  156. /**
  157. * Retrieve an item by identifier
  158. *
  159. * Item retrieved will be flattened to an array.
  160. *
  161. * @param string $id
  162. * @return array
  163. */
  164. public function getItem($id)
  165. {
  166. if (!$this->hasItem($id)) {
  167. return null;
  168. }
  169. return $this->_items[$id];
  170. }
  171. /**
  172. * Remove item by identifier
  173. *
  174. * @param string $id
  175. * @return \Zend\Dojo\Data
  176. */
  177. public function removeItem($id)
  178. {
  179. if ($this->hasItem($id)) {
  180. unset($this->_items[$id]);
  181. }
  182. return $this;
  183. }
  184. /**
  185. * Remove all items at once
  186. *
  187. * @return \Zend\Dojo\Data
  188. */
  189. public function clearItems()
  190. {
  191. $this->_items = array();
  192. return $this;
  193. }
  194. /**
  195. * Set identifier for item lookups
  196. *
  197. * @param string|int|null $identifier
  198. * @return \Zend\Dojo\Data
  199. */
  200. public function setIdentifier($identifier)
  201. {
  202. if (null === $identifier) {
  203. $this->_identifier = null;
  204. } elseif (is_string($identifier)) {
  205. $this->_identifier = $identifier;
  206. } elseif (is_numeric($identifier)) {
  207. $this->_identifier = (int) $identifier;
  208. } else {
  209. throw new Exception\InvalidArgumentException('Invalid identifier; please use a string or integer');
  210. }
  211. return $this;
  212. }
  213. /**
  214. * Retrieve current item identifier
  215. *
  216. * @return string|int|null
  217. */
  218. public function getIdentifier()
  219. {
  220. return $this->_identifier;
  221. }
  222. /**
  223. * Set label to use for displaying item associations
  224. *
  225. * @param string|null $label
  226. * @return \Zend\Dojo\Data
  227. */
  228. public function setLabel($label)
  229. {
  230. if (null === $label) {
  231. $this->_label = null;
  232. } else {
  233. $this->_label = (string) $label;
  234. }
  235. return $this;
  236. }
  237. /**
  238. * Retrieve item association label
  239. *
  240. * @return string|null
  241. */
  242. public function getLabel()
  243. {
  244. return $this->_label;
  245. }
  246. /**
  247. * Set metadata by key or en masse
  248. *
  249. * @param string|array $spec
  250. * @param mixed $value
  251. * @return \Zend\Dojo\Data
  252. */
  253. public function setMetadata($spec, $value = null)
  254. {
  255. if (is_string($spec) && (null !== $value)) {
  256. $this->_metadata[$spec] = $value;
  257. } elseif (is_array($spec)) {
  258. foreach ($spec as $key => $value) {
  259. $this->setMetadata($key, $value);
  260. }
  261. }
  262. return $this;
  263. }
  264. /**
  265. * Get metadata item or all metadata
  266. *
  267. * @param null|string $key Metadata key when pulling single metadata item
  268. * @return mixed
  269. */
  270. public function getMetadata($key = null)
  271. {
  272. if (null === $key) {
  273. return $this->_metadata;
  274. }
  275. if (array_key_exists($key, $this->_metadata)) {
  276. return $this->_metadata[$key];
  277. }
  278. return null;
  279. }
  280. /**
  281. * Clear individual or all metadata item(s)
  282. *
  283. * @param null|string $key
  284. * @return \Zend\Dojo\Data
  285. */
  286. public function clearMetadata($key = null)
  287. {
  288. if (null === $key) {
  289. $this->_metadata = array();
  290. } elseif (array_key_exists($key, $this->_metadata)) {
  291. unset($this->_metadata[$key]);
  292. }
  293. return $this;
  294. }
  295. /**
  296. * Load object from array
  297. *
  298. * @param array $data
  299. * @return \Zend\Dojo\Data
  300. */
  301. public function fromArray(array $data)
  302. {
  303. if (array_key_exists('identifier', $data)) {
  304. $this->setIdentifier($data['identifier']);
  305. }
  306. if (array_key_exists('label', $data)) {
  307. $this->setLabel($data['label']);
  308. }
  309. if (array_key_exists('items', $data) && is_array($data['items'])) {
  310. $this->setItems($data['items']);
  311. } else {
  312. $this->clearItems();
  313. }
  314. return $this;
  315. }
  316. /**
  317. * Load object from JSON
  318. *
  319. * @param string $json
  320. * @return \Zend\Dojo\Data
  321. */
  322. public function fromJson($json)
  323. {
  324. if (!is_string($json)) {
  325. throw new Exception\InvalidArgumentException('fromJson() expects JSON input');
  326. }
  327. $data = Json::decode($json, Json::TYPE_ARRAY);
  328. return $this->fromArray($data);
  329. }
  330. /**
  331. * Seralize entire data structure, including identifier and label, to array
  332. *
  333. * @return array
  334. */
  335. public function toArray()
  336. {
  337. if (null === ($identifier = $this->getIdentifier())) {
  338. throw new Exception\RuntimeException('Serialization requires that an identifier be present in the object; first call setIdentifier()');
  339. }
  340. $array = array(
  341. 'identifier' => $identifier,
  342. 'items' => array_values($this->getItems()),
  343. );
  344. $metadata = $this->getMetadata();
  345. if (!empty($metadata)) {
  346. foreach ($metadata as $key => $value) {
  347. $array[$key] = $value;
  348. }
  349. }
  350. if (null !== ($label = $this->getLabel())) {
  351. $array['label'] = $label;
  352. }
  353. return $array;
  354. }
  355. /**
  356. * Serialize to JSON (dojo.data format)
  357. *
  358. * @return string
  359. */
  360. public function toJson()
  361. {
  362. return Json::encode($this->toArray());
  363. }
  364. /**
  365. * Serialize to string (proxy to {@link toJson()})
  366. *
  367. * @return string
  368. */
  369. public function __toString()
  370. {
  371. return $this->toJson();
  372. }
  373. /**
  374. * ArrayAccess: does offset exist?
  375. *
  376. * @param string|int $offset
  377. * @return bool
  378. */
  379. public function offsetExists($offset)
  380. {
  381. return (null !== $this->getItem($offset));
  382. }
  383. /**
  384. * ArrayAccess: retrieve by offset
  385. *
  386. * @param string|int $offset
  387. * @return array
  388. */
  389. public function offsetGet($offset)
  390. {
  391. return $this->getItem($offset);
  392. }
  393. /**
  394. * ArrayAccess: set value by offset
  395. *
  396. * @param string $offset
  397. * @param array|object|null $value
  398. * @return void
  399. */
  400. public function offsetSet($offset, $value)
  401. {
  402. $this->setItem($value, $offset);
  403. }
  404. /**
  405. * ArrayAccess: unset value by offset
  406. *
  407. * @param string $offset
  408. * @return void
  409. */
  410. public function offsetUnset($offset)
  411. {
  412. $this->removeItem($offset);
  413. }
  414. /**
  415. * IteratorAggregate: return an iterator
  416. *
  417. * @return Traversable
  418. */
  419. public function getIterator()
  420. {
  421. return new \ArrayIterator($this->_items);
  422. }
  423. /**
  424. * Countable: how many items are present
  425. *
  426. * @return int
  427. */
  428. public function count()
  429. {
  430. return count($this->_items);
  431. }
  432. /**
  433. * Normalize an item to attach to the collection
  434. *
  435. * @param array|object $item
  436. * @param string|int|null $id
  437. * @return array
  438. */
  439. protected function _normalizeItem($item, $id)
  440. {
  441. if (!is_object($item) && !is_array($item)) {
  442. throw new Exception\InvalidArgumentException('Only arrays and objects may be attached');
  443. }
  444. if (null === ($identifier = $this->getIdentifier())) {
  445. throw new Exception\RuntimeException('You must set an identifier prior to adding items');
  446. }
  447. if (is_object($item)) {
  448. if (method_exists($item, 'toArray')) {
  449. $item = $item->toArray();
  450. } else {
  451. $item = get_object_vars($item);
  452. }
  453. }
  454. if ((null === $id) && !array_key_exists($identifier, $item)) {
  455. throw new Exception\InvalidArgumentException('Item must contain a column matching the currently set identifier');
  456. } elseif (null === $id) {
  457. $id = $item[$identifier];
  458. } else {
  459. $item[$identifier] = $id;
  460. }
  461. return array(
  462. 'id' => $id,
  463. 'data' => $item,
  464. );
  465. }
  466. }