File indexing completed on 2024-12-22 05:36:37

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Dojo
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 /**
0023  * dojo.data support for Zend Framework
0024  *
0025  * @uses       ArrayAccess
0026  * @uses       Iterator
0027  * @uses       Countable
0028  * @package    Zend_Dojo
0029  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0030  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0031  */
0032 class Zend_Dojo_Data implements ArrayAccess,Iterator,Countable
0033 {
0034     /**
0035      * Identifier field of item
0036      * @var string|int
0037      */
0038     protected $_identifier;
0039 
0040     /**
0041      * Collected items
0042      * @var array
0043      */
0044     protected $_items = array();
0045 
0046     /**
0047      * Label field of item
0048      * @var string
0049      */
0050     protected $_label;
0051 
0052     /**
0053      * Data container metadata
0054      * @var array
0055      */
0056     protected $_metadata = array();
0057 
0058     /**
0059      * Constructor
0060      *
0061      * @param  string|null $identifier
0062      * @param  array|Traversable|null $items
0063      * @param  string|null $label
0064      * @return void
0065      */
0066     public function __construct($identifier = null, $items = null, $label = null)
0067     {
0068         if (null !== $identifier) {
0069             $this->setIdentifier($identifier);
0070         }
0071         if (null !== $items) {
0072             $this->setItems($items);
0073         }
0074         if (null !== $label) {
0075             $this->setLabel($label);
0076         }
0077     }
0078 
0079     /**
0080      * Set the items to collect
0081      *
0082      * @param array|Traversable $items
0083      * @return Zend_Dojo_Data
0084      */
0085     public function setItems($items)
0086     {
0087         $this->clearItems();
0088         return $this->addItems($items);
0089     }
0090 
0091     /**
0092      * Set an individual item, optionally by identifier (overwrites)
0093      *
0094      * @param  array|object $item
0095      * @param  string|null $identifier
0096      * @return Zend_Dojo_Data
0097      */
0098     public function setItem($item, $id = null)
0099     {
0100         $item = $this->_normalizeItem($item, $id);
0101         $this->_items[$item['id']] = $item['data'];
0102         return $this;
0103     }
0104 
0105     /**
0106      * Add an individual item, optionally by identifier
0107      *
0108      * @param  array|object $item
0109      * @param  string|null $id
0110      * @return Zend_Dojo_Data
0111      */
0112     public function addItem($item, $id = null)
0113     {
0114         $item = $this->_normalizeItem($item, $id);
0115 
0116         if ($this->hasItem($item['id'])) {
0117             // require_once 'Zend/Dojo/Exception.php';
0118             throw new Zend_Dojo_Exception('Overwriting items using addItem() is not allowed');
0119         }
0120 
0121         $this->_items[$item['id']] = $item['data'];
0122 
0123         return $this;
0124     }
0125 
0126     /**
0127      * Add multiple items at once
0128      *
0129      * @param  array|Traversable $items
0130      * @return Zend_Dojo_Data
0131      */
0132     public function addItems($items)
0133     {
0134         if (!is_array($items) && (!is_object($items) || !($items instanceof Traversable))) {
0135             // require_once 'Zend/Dojo/Exception.php';
0136             throw new Zend_Dojo_Exception('Only arrays and Traversable objects may be added to ' . __CLASS__);
0137         }
0138 
0139         foreach ($items as $item) {
0140             $this->addItem($item);
0141         }
0142 
0143         return $this;
0144     }
0145 
0146     /**
0147      * Get all items as an array
0148      *
0149      * Serializes items to arrays.
0150      *
0151      * @return array
0152      */
0153     public function getItems()
0154     {
0155         return $this->_items;
0156     }
0157 
0158     /**
0159      * Does an item with the given identifier exist?
0160      *
0161      * @param  string|int $id
0162      * @return bool
0163      */
0164     public function hasItem($id)
0165     {
0166         return array_key_exists($id, $this->_items);
0167     }
0168 
0169     /**
0170      * Retrieve an item by identifier
0171      *
0172      * Item retrieved will be flattened to an array.
0173      *
0174      * @param  string $id
0175      * @return array
0176      */
0177     public function getItem($id)
0178     {
0179         if (!$this->hasItem($id)) {
0180             return null;
0181         }
0182 
0183         return $this->_items[$id];
0184     }
0185 
0186     /**
0187      * Remove item by identifier
0188      *
0189      * @param  string $id
0190      * @return Zend_Dojo_Data
0191      */
0192     public function removeItem($id)
0193     {
0194         if ($this->hasItem($id)) {
0195             unset($this->_items[$id]);
0196         }
0197 
0198         return $this;
0199     }
0200 
0201     /**
0202      * Remove all items at once
0203      *
0204      * @return Zend_Dojo_Data
0205      */
0206     public function clearItems()
0207     {
0208         $this->_items = array();
0209         return $this;
0210     }
0211 
0212 
0213     /**
0214      * Set identifier for item lookups
0215      *
0216      * @param  string|int|null $identifier
0217      * @return Zend_Dojo_Data
0218      */
0219     public function setIdentifier($identifier)
0220     {
0221         if (null === $identifier) {
0222             $this->_identifier = null;
0223         } elseif (is_string($identifier)) {
0224             $this->_identifier = $identifier;
0225         } elseif (is_numeric($identifier)) {
0226             $this->_identifier = (int) $identifier;
0227         } else {
0228             // require_once 'Zend/Dojo/Exception.php';
0229             throw new Zend_Dojo_Exception('Invalid identifier; please use a string or integer');
0230         }
0231 
0232         return $this;
0233     }
0234 
0235     /**
0236      * Retrieve current item identifier
0237      *
0238      * @return string|int|null
0239      */
0240     public function getIdentifier()
0241     {
0242         return $this->_identifier;
0243     }
0244 
0245 
0246     /**
0247      * Set label to use for displaying item associations
0248      *
0249      * @param  string|null $label
0250      * @return Zend_Dojo_Data
0251      */
0252     public function setLabel($label)
0253     {
0254         if (null === $label) {
0255             $this->_label = null;
0256         } else {
0257             $this->_label = (string) $label;
0258         }
0259         return $this;
0260     }
0261 
0262     /**
0263      * Retrieve item association label
0264      *
0265      * @return string|null
0266      */
0267     public function getLabel()
0268     {
0269         return $this->_label;
0270     }
0271 
0272     /**
0273      * Set metadata by key or en masse
0274      *
0275      * @param  string|array $spec
0276      * @param  mixed $value
0277      * @return Zend_Dojo_Data
0278      */
0279     public function setMetadata($spec, $value = null)
0280     {
0281         if (is_string($spec) && (null !== $value)) {
0282             $this->_metadata[$spec] = $value;
0283         } elseif (is_array($spec)) {
0284             foreach ($spec as $key => $value) {
0285                 $this->setMetadata($key, $value);
0286             }
0287         }
0288         return $this;
0289     }
0290 
0291     /**
0292      * Get metadata item or all metadata
0293      *
0294      * @param  null|string $key Metadata key when pulling single metadata item
0295      * @return mixed
0296      */
0297     public function getMetadata($key = null)
0298     {
0299         if (null === $key) {
0300             return $this->_metadata;
0301         }
0302 
0303         if (array_key_exists($key, $this->_metadata)) {
0304             return $this->_metadata[$key];
0305         }
0306 
0307         return null;
0308     }
0309 
0310     /**
0311      * Clear individual or all metadata item(s)
0312      *
0313      * @param  null|string $key
0314      * @return Zend_Dojo_Data
0315      */
0316     public function clearMetadata($key = null)
0317     {
0318         if (null === $key) {
0319             $this->_metadata = array();
0320         } elseif (array_key_exists($key, $this->_metadata)) {
0321             unset($this->_metadata[$key]);
0322         }
0323         return $this;
0324     }
0325 
0326     /**
0327      * Load object from array
0328      *
0329      * @param  array $data
0330      * @return Zend_Dojo_Data
0331      */
0332     public function fromArray(array $data)
0333     {
0334         if (array_key_exists('identifier', $data)) {
0335             $this->setIdentifier($data['identifier']);
0336         }
0337         if (array_key_exists('label', $data)) {
0338             $this->setLabel($data['label']);
0339         }
0340         if (array_key_exists('items', $data) && is_array($data['items'])) {
0341             $this->setItems($data['items']);
0342         } else {
0343             $this->clearItems();
0344         }
0345         return $this;
0346     }
0347 
0348     /**
0349      * Load object from JSON
0350      *
0351      * @param  string $json
0352      * @return Zend_Dojo_Data
0353      */
0354     public function fromJson($json)
0355     {
0356         if (!is_string($json)) {
0357             // require_once 'Zend/Dojo/Exception.php';
0358             throw new Zend_Dojo_Exception('fromJson() expects JSON input');
0359         }
0360         // require_once 'Zend/Json.php';
0361         $data = Zend_Json::decode($json);
0362         return $this->fromArray($data);
0363     }
0364 
0365     /**
0366      * Seralize entire data structure, including identifier and label, to array
0367      *
0368      * @return array
0369      */
0370     public function toArray()
0371     {
0372         if (null === ($identifier = $this->getIdentifier())) {
0373             // require_once 'Zend/Dojo/Exception.php';
0374             throw new Zend_Dojo_Exception('Serialization requires that an identifier be present in the object; first call setIdentifier()');
0375         }
0376 
0377         $array = array(
0378             'identifier' => $identifier,
0379             'items'      => array_values($this->getItems()),
0380         );
0381 
0382         $metadata = $this->getMetadata();
0383         if (!empty($metadata)) {
0384             foreach ($metadata as $key => $value) {
0385                 $array[$key] = $value;
0386             }
0387         }
0388 
0389         if (null !== ($label = $this->getLabel())) {
0390             $array['label'] = $label;
0391         }
0392 
0393         return $array;
0394     }
0395 
0396     /**
0397      * Serialize to JSON (dojo.data format)
0398      *
0399      * @return string
0400      */
0401     public function toJson()
0402     {
0403         // require_once 'Zend/Json.php';
0404         return Zend_Json::encode($this->toArray());
0405     }
0406 
0407     /**
0408      * Serialize to string (proxy to {@link toJson()})
0409      *
0410      * @return string
0411      */
0412     public function __toString()
0413     {
0414         return $this->toJson();
0415     }
0416 
0417     /**
0418      * ArrayAccess: does offset exist?
0419      *
0420      * @param  string|int $offset
0421      * @return bool
0422      */
0423     public function offsetExists($offset)
0424     {
0425         return (null !== $this->getItem($offset));
0426     }
0427 
0428     /**
0429      * ArrayAccess: retrieve by offset
0430      *
0431      * @param  string|int $offset
0432      * @return array
0433      */
0434     public function offsetGet($offset)
0435     {
0436         return $this->getItem($offset);
0437     }
0438 
0439     /**
0440      * ArrayAccess: set value by offset
0441      *
0442      * @param  string $offset
0443      * @param  array|object|null $value
0444      * @return void
0445      */
0446     public function offsetSet($offset, $value)
0447     {
0448         $this->setItem($value, $offset);
0449     }
0450 
0451     /**
0452      * ArrayAccess: unset value by offset
0453      *
0454      * @param  string $offset
0455      * @return void
0456      */
0457     public function offsetUnset($offset)
0458     {
0459         $this->removeItem($offset);
0460     }
0461 
0462     /**
0463      * Iterator: get current value
0464      *
0465      * @return array
0466      */
0467     public function current()
0468     {
0469         return current($this->_items);
0470     }
0471 
0472     /**
0473      * Iterator: get current key
0474      *
0475      * @return string|int
0476      */
0477     public function key()
0478     {
0479         return key($this->_items);
0480     }
0481 
0482     /**
0483      * Iterator: get next item
0484      *
0485      * @return void
0486      */
0487     public function next()
0488     {
0489         return next($this->_items);
0490     }
0491 
0492     /**
0493      * Iterator: rewind to first value in collection
0494      *
0495      * @return void
0496      */
0497     public function rewind()
0498     {
0499         return reset($this->_items);
0500     }
0501 
0502     /**
0503      * Iterator: is item valid?
0504      *
0505      * @return bool
0506      */
0507     public function valid()
0508     {
0509         return (bool) $this->current();
0510     }
0511 
0512     /**
0513      * Countable: how many items are present
0514      *
0515      * @return int
0516      */
0517     public function count()
0518     {
0519         return count($this->_items);
0520     }
0521 
0522     /**
0523      * Normalize an item to attach to the collection
0524      *
0525      * @param  array|object $item
0526      * @param  string|int|null $id
0527      * @return array
0528      */
0529     protected function _normalizeItem($item, $id)
0530     {
0531         if (null === ($identifier = $this->getIdentifier())) {
0532             // require_once 'Zend/Dojo/Exception.php';
0533             throw new Zend_Dojo_Exception('You must set an identifier prior to adding items');
0534         }
0535 
0536         if (!is_object($item) && !is_array($item)) {
0537             // require_once 'Zend/Dojo/Exception.php';
0538             throw new Zend_Dojo_Exception('Only arrays and objects may be attached');
0539         }
0540 
0541         if (is_object($item)) {
0542             if (method_exists($item, 'toArray')) {
0543                 $item = $item->toArray();
0544             } else {
0545                 $item = get_object_vars($item);
0546             }
0547         }
0548 
0549         if ((null === $id) && !array_key_exists($identifier, $item)) {
0550             // require_once 'Zend/Dojo/Exception.php';
0551             throw new Zend_Dojo_Exception('Item must contain a column matching the currently set identifier');
0552         } elseif (null === $id) {
0553             $id = $item[$identifier];
0554         } else {
0555             $item[$identifier] = $id;
0556         }
0557 
0558         return array(
0559             'id'   => $id,
0560             'data' => $item,
0561         );
0562     }
0563 }