ArangoDB-PHP API Documentation
  • Namespace
  • Class
  • Deprecated

Namespaces

  • triagens
    • ArangoDb

Classes

  • triagens\ArangoDb\AdminHandler
  • triagens\ArangoDb\AqlUserFunction
  • triagens\ArangoDb\Autoloader
  • triagens\ArangoDb\Batch
  • triagens\ArangoDb\BatchPart
  • triagens\ArangoDb\BindVars
  • triagens\ArangoDb\Collection
  • triagens\ArangoDb\CollectionHandler
  • triagens\ArangoDb\Connection
  • triagens\ArangoDb\ConnectionOptions
  • triagens\ArangoDb\Cursor
  • triagens\ArangoDb\Database
  • triagens\ArangoDb\DefaultValues
  • triagens\ArangoDb\Document
  • triagens\ArangoDb\DocumentHandler
  • triagens\ArangoDb\Edge
  • triagens\ArangoDb\EdgeDefinition
  • triagens\ArangoDb\EdgeHandler
  • triagens\ArangoDb\Endpoint
  • triagens\ArangoDb\Export
  • triagens\ArangoDb\ExportCursor
  • triagens\ArangoDb\Graph
  • triagens\ArangoDb\GraphHandler
  • triagens\ArangoDb\Handler
  • triagens\ArangoDb\HttpHelper
  • triagens\ArangoDb\HttpResponse
  • triagens\ArangoDb\QueryCacheHandler
  • triagens\ArangoDb\QueryHandler
  • triagens\ArangoDb\Statement
  • triagens\ArangoDb\TraceRequest
  • triagens\ArangoDb\TraceResponse
  • triagens\ArangoDb\Transaction
  • triagens\ArangoDb\Traversal
  • triagens\ArangoDb\UpdatePolicy
  • triagens\ArangoDb\UrlHelper
  • triagens\ArangoDb\Urls
  • triagens\ArangoDb\User
  • triagens\ArangoDb\UserHandler
  • triagens\ArangoDb\ValueValidator
  • triagens\ArangoDb\Vertex
  • triagens\ArangoDb\VertexHandler

Exceptions

  • triagens\ArangoDb\ClientException
  • triagens\ArangoDb\ConnectException
  • triagens\ArangoDb\Exception
  • triagens\ArangoDb\ServerException
  1 <?php
  2 
  3 /**
  4  * ArangoDB PHP client: single document
  5  *
  6  * @package   triagens\ArangoDb
  7  * @author    Jan Steemann
  8  * @author    Frank Mayer
  9  * @copyright Copyright 2012, triagens GmbH, Cologne, Germany
 10  */
 11 
 12 namespace triagens\ArangoDb;
 13 
 14 /**
 15  * Value object representing a single collection-based document
 16  *
 17  * <br>
 18  *
 19  * @package   triagens\ArangoDb
 20  * @since     0.2
 21  */
 22 class Document
 23 {
 24     /**
 25      * The document id (might be NULL for new documents)
 26      *
 27      * @var string - document id
 28      */
 29     protected $_id;
 30 
 31     /**
 32      * The document key (might be NULL for new documents)
 33      *
 34      * @var string - document id
 35      */
 36     protected $_key;
 37 
 38     /**
 39      * The document revision (might be NULL for new documents)
 40      *
 41      * @var mixed
 42      */
 43     protected $_rev;
 44 
 45     /**
 46      * The document attributes (names/values)
 47      *
 48      * @var array
 49      */
 50     protected $_values = [];
 51 
 52     /**
 53      * Flag to indicate whether document was changed locally
 54      *
 55      * @var bool
 56      */
 57     protected $_changed = false;
 58 
 59     /**
 60      * Flag to indicate whether document is a new document (never been saved to the server)
 61      *
 62      * @var bool
 63      */
 64     protected $_isNew = true;
 65 
 66     /**
 67      * Flag to indicate whether validation of document values should be performed
 68      * This can be turned on, but has a performance penalty
 69      *
 70      * @var bool
 71      */
 72     protected $_doValidate = false;
 73 
 74     /**
 75      * Flag to indicate whether document was changed locally
 76      *
 77      * @var bool
 78      */
 79     protected $_hiddenAttributes = [];
 80 
 81     /**
 82      * Flag to indicate whether document was changed locally
 83      *
 84      * @var bool
 85      */
 86     protected $_ignoreHiddenAttributes = false;
 87 
 88     /**
 89      * Document id index
 90      */
 91     const ENTRY_ID = '_id';
 92 
 93     /**
 94      * Document key index
 95      */
 96     const ENTRY_KEY = '_key';
 97 
 98     /**
 99      * Revision id index
100      */
101     const ENTRY_REV = '_rev';
102 
103     /**
104      * isNew id index
105      */
106     const ENTRY_ISNEW = '_isNew';
107 
108     /**
109      * hidden attribute index
110      */
111     const ENTRY_HIDDENATTRIBUTES = '_hiddenAttributes';
112 
113     /**
114      * hidden attribute index
115      */
116     const ENTRY_IGNOREHIDDENATTRIBUTES = '_ignoreHiddenAttributes';
117 
118     /**
119      * waitForSync option index
120      */
121     const OPTION_WAIT_FOR_SYNC = 'waitForSync';
122 
123     /**
124      * policy option index
125      */
126     const OPTION_POLICY = 'policy';
127 
128     /**
129      * keepNull option index
130      */
131     const OPTION_KEEPNULL = 'keepNull';
132 
133     /**
134      * Constructs an empty document
135      *
136      * @param array $options           - optional, initial $options for document
137      *                                 <p>Options are :<br>
138      *                                 <li>'_hiddenAttributes' - Set an array of hidden attributes for created documents.
139      *                                 <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
140      *                                 <p>
141      *
142      */
143     public function __construct(array $options = null)
144     {
145         if (is_array($options)) {
146             // keeping the non-underscored version for backwards-compatibility
147             if (isset($options['hiddenAttributes'])) {
148                 $this->setHiddenAttributes($options['hiddenAttributes']);
149             }
150             if (isset($options[self::ENTRY_HIDDENATTRIBUTES])) {
151                 $this->setHiddenAttributes($options[self::ENTRY_HIDDENATTRIBUTES]);
152             }
153             if (isset($options[self::ENTRY_IGNOREHIDDENATTRIBUTES])) {
154                 $this->setIgnoreHiddenAttributes($options[self::ENTRY_IGNOREHIDDENATTRIBUTES]);
155             }
156             if (isset($options[self::ENTRY_ISNEW])) {
157                 $this->setIsNew($options[self::ENTRY_ISNEW]);
158             }
159             if (isset($options['_validate'])) {
160                 $this->_doValidate = $options['_validate'];
161             }
162         }
163     }
164 
165     /**
166      * Factory method to construct a new document using the values passed to populate it
167      *
168      * @throws ClientException
169      *
170      * @param array $values  - initial values for document
171      * @param array $options - optional, initial options for document
172      *
173      * @return Document|Edge|Graph
174      */
175     public static function createFromArray($values, array $options = [])
176     {
177         $document = new static($options);
178         foreach ($values as $key => $value) {
179             $document->set($key, $value);
180         }
181 
182         $document->setChanged(true);
183 
184         return $document;
185     }
186 
187     /**
188      * Clone a document
189      *
190      * Returns the clone
191      *
192      * @magic
193      *
194      * @return void
195      */
196     public function __clone()
197     {
198         $this->_id  = null;
199         $this->_key = null;
200         $this->_rev = null;
201         // do not change the _changed flag here
202     }
203 
204     /**
205      * Get a string representation of the document.
206      *
207      * It will not output hidden attributes.
208      *
209      * Returns the document as JSON-encoded string
210      *
211      * @magic
212      *
213      * @return string - JSON-encoded document
214      */
215     public function __toString()
216     {
217         return $this->toJson();
218     }
219 
220     /**
221      * Returns the document as JSON-encoded string
222      *
223      * @param array $options - optional, array of options that will be passed to the getAll function
224      *                       <p>Options are :
225      *                       <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
226      *                       <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
227      *                       </p>
228      *
229      * @return string - JSON-encoded document
230      */
231     public function toJson(array $options = [])
232     {
233         return json_encode($this->getAll($options));
234     }
235 
236     /**
237      * Returns the document as a serialized string
238      *
239      * @param array $options - optional, array of options that will be passed to the getAll function
240      *                       <p>Options are :
241      *                       <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
242      *                       <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
243      *                       </p>
244      *
245      * @return string - PHP serialized document
246      */
247     public function toSerialized(array $options = [])
248     {
249         return serialize($this->getAll($options));
250     }
251 
252     /**
253      * Returns the attributes with the hidden ones removed
254      *
255      * @param array $attributes - attributes array
256      *
257      * @param array $_hiddenAttributes
258      *
259      * @return array - attributes array
260      */
261     public function filterHiddenAttributes($attributes, array $_hiddenAttributes = [])
262     {
263         $hiddenAttributes = $_hiddenAttributes !== null ? $_hiddenAttributes : $this->getHiddenAttributes();
264 
265         if (count($hiddenAttributes) > 0) {
266             foreach ($hiddenAttributes as $hiddenAttributeName) {
267                 if (isset($attributes[$hiddenAttributeName])) {
268                     unset($attributes[$hiddenAttributeName]);
269                 }
270             }
271         }
272 
273         unset ($attributes[self::ENTRY_HIDDENATTRIBUTES]);
274 
275         return $attributes;
276     }
277 
278     /**
279      * Set a document attribute
280      *
281      * The key (attribute name) must be a string.
282      * This will validate the value of the attribute and might throw an
283      * exception if the value is invalid.
284      *
285      * @throws ClientException
286      *
287      * @param string $key   - attribute name
288      * @param mixed  $value - value for attribute
289      *
290      * @return void
291      */
292     public function set($key, $value)
293     {
294         if ($this->_doValidate) {
295             // validate the value passed
296             ValueValidator::validate($value);
297         }
298 
299         if ($key[0] === '_') {
300             if ($key === self::ENTRY_ID) {
301                 $this->setInternalId($value);
302 
303                 return;
304             }
305 
306             if ($key === self::ENTRY_KEY) {
307                 $this->setInternalKey($value);
308 
309                 return;
310             }
311 
312             if ($key === self::ENTRY_REV) {
313                 $this->setRevision($value);
314 
315                 return;
316             }
317 
318             if ($key === self::ENTRY_ISNEW) {
319                 $this->setIsNew($value);
320 
321                 return;
322             }
323         }
324 
325         if (!$this->_changed) {
326             if (!isset($this->_values[$key]) || $this->_values[$key] !== $value) {
327                 // set changed flag
328                 $this->_changed = true;
329             }
330         }
331 
332         // and store the value
333         $this->_values[$key] = $value;
334     }
335 
336     /**
337      * Set a document attribute, magic method
338      *
339      * This is a magic method that allows the object to be used without
340      * declaring all document attributes first.
341      * This function is mapped to set() internally.
342      *
343      * @throws ClientException
344      *
345      * @magic
346      *
347      * @param string $key   - attribute name
348      * @param mixed  $value - value for attribute
349      *
350      * @return void
351      */
352     public function __set($key, $value)
353     {
354         $this->set($key, $value);
355     }
356 
357     /**
358      * Get a document attribute
359      *
360      * @param string $key - name of attribute
361      *
362      * @return mixed - value of attribute, NULL if attribute is not set
363      */
364     public function get($key)
365     {
366         if (isset($this->_values[$key])) {
367             return $this->_values[$key];
368         }
369 
370         return null;
371     }
372 
373     /**
374      * Get a document attribute, magic method
375      *
376      * This function is mapped to get() internally.
377      *
378      * @magic
379      *
380      * @param string $key - name of attribute
381      *
382      * @return mixed - value of attribute, NULL if attribute is not set
383      */
384     public function __get($key)
385     {
386         return $this->get($key);
387     }
388 
389 
390     /**
391      * Is triggered by calling isset() or empty() on inaccessible properties.
392      *
393      * @param string $key - name of attribute
394      *
395      * @return boolean returns true or false (set or not set)
396      */
397     public function __isset($key)
398     {
399         if (isset($this->_values[$key])) {
400             return true;
401         }
402 
403         return false;
404     }
405 
406     /**
407      * Magic method to unset an attribute.
408      * Caution!!! This works only on the first array level.
409      * The preferred method to unset attributes in the database, is to set those to null and do an update() with the option: 'keepNull' => false.
410      *
411      * @magic
412      *
413      * @param $key
414      */
415     public function __unset($key)
416     {
417         unset($this->_values[$key]);
418     }
419 
420     /**
421      * Get all document attributes
422      *
423      * @param mixed $options - optional, array of options for the getAll function, or the boolean value for $includeInternals
424      *                       <p>Options are :
425      *                       <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
426      *                       <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
427      *                       </p>
428      *
429      * @return array - array of all document attributes/values
430      */
431     public function getAll(array $options = [])
432     {
433         // This preserves compatibility for the old includeInternals parameter.
434         $includeInternals       = false;
435         $ignoreHiddenAttributes = $this->{self::ENTRY_IGNOREHIDDENATTRIBUTES};
436         $_hiddenAttributes      = $this->{self::ENTRY_HIDDENATTRIBUTES};
437 
438         if (!is_array($options)) {
439             $includeInternals = $options;
440         } else {
441             // keeping the non-underscored version for backwards-compatibility
442             $includeInternals = array_key_exists(
443                 'includeInternals',
444                 $options
445             ) ? $options['includeInternals'] : $includeInternals;
446 
447             $includeInternals = array_key_exists(
448                 '_includeInternals',
449                 $options
450             ) ? $options['_includeInternals'] : $includeInternals;
451 
452             // keeping the non-underscored version for backwards-compatibility
453             $ignoreHiddenAttributes = array_key_exists(
454                 'ignoreHiddenAttributes',
455                 $options
456             ) ? $options['ignoreHiddenAttributes'] : $ignoreHiddenAttributes;
457 
458             $ignoreHiddenAttributes = array_key_exists(
459                 self::ENTRY_IGNOREHIDDENATTRIBUTES,
460                 $options
461             ) ? $options[self::ENTRY_IGNOREHIDDENATTRIBUTES] : $ignoreHiddenAttributes;
462 
463             $_hiddenAttributes = array_key_exists(
464                 self::ENTRY_HIDDENATTRIBUTES,
465                 $options
466             ) ? $options[self::ENTRY_HIDDENATTRIBUTES] : $_hiddenAttributes;
467         }
468 
469         $data         = $this->_values;
470         $nonInternals = ['_changed', '_values', self::ENTRY_HIDDENATTRIBUTES];
471 
472         if ($includeInternals === true) {
473             foreach ($this as $key => $value) {
474                 if ($key[0] === '_' && 0 !== strpos($key, '__') && !in_array($key, $nonInternals, true)) {
475                     $data[$key] = $value;
476                 }
477             }
478         }
479 
480         if ($ignoreHiddenAttributes === false) {
481             $data = $this->filterHiddenAttributes($data, $_hiddenAttributes);
482         }
483 
484         if (null !== $this->_key) {
485             $data['_key'] = $this->_key;
486         }
487 
488         return $data;
489     }
490 
491     /**
492      * Get all document attributes for insertion/update
493      *
494      * @return mixed - associative array of all document attributes/values
495      */
496     public function getAllForInsertUpdate()
497     {
498         $data = [];
499         foreach ($this->_values as $key => $value) {
500             if ($key === '_id' || $key === '_rev') {
501                 continue;
502             } else if ($key === '_key' && $value === null) {
503                 // key value not yet set
504                 continue;
505             }
506             $data[$key] = $value;
507         }
508         if ($this->_key !== null) {
509             $data['_key'] = $this->_key;
510         }
511 
512         return $data;
513     }
514 
515 
516     /**
517      * Get all document attributes, and return an empty object if the documentapped into a DocumentWrapper class
518      *
519      * @param mixed $options - optional, array of options for the getAll function, or the boolean value for $includeInternals
520      *                       <p>Options are :
521      *                       <li>'_includeInternals' - true to include the internal attributes. Defaults to false</li>
522      *                       <li>'_ignoreHiddenAttributes' - true to show hidden attributes. Defaults to false</li>
523      *                       </p>
524      *
525      * @return mixed - associative array of all document attributes/values, or an empty StdClass if the document
526      *                 does not have any
527      */
528     public function getAllAsObject(array $options = [])
529     {
530         $result = $this->getAll($options);
531         if (count($result) === 0) {
532             return new \stdClass();
533         }
534 
535         return $result;
536     }
537 
538     /**
539      * Set the hidden attributes
540      * $cursor
541      *
542      * @param array $attributes - array of attributes
543      *
544      * @return void
545      */
546     public function setHiddenAttributes(array $attributes)
547     {
548         $this->{self::ENTRY_HIDDENATTRIBUTES} = $attributes;
549     }
550 
551     /**
552      * Get the hidden attributes
553      *
554      * @return array $attributes - array of attributes
555      */
556     public function getHiddenAttributes()
557     {
558         return $this->{self::ENTRY_HIDDENATTRIBUTES};
559     }
560 
561     /**
562      * @return boolean
563      */
564     public function isIgnoreHiddenAttributes()
565     {
566         return $this->{self::ENTRY_IGNOREHIDDENATTRIBUTES};
567     }
568 
569     /**
570      * @param boolean $ignoreHiddenAttributes
571      */
572     public function setIgnoreHiddenAttributes($ignoreHiddenAttributes)
573     {
574         $this->{self::ENTRY_IGNOREHIDDENATTRIBUTES} = (bool) $ignoreHiddenAttributes;
575     }
576 
577     /**
578      * Set the changed flag
579      *
580      * @param bool $value - change flag
581      *
582      * @return bool
583      */
584     public function setChanged($value)
585     {
586         return $this->_changed = (bool) $value;
587     }
588 
589     /**
590      * Get the changed flag
591      *
592      * @return bool - true if document was changed, false otherwise
593      */
594     public function getChanged()
595     {
596         return $this->_changed;
597     }
598 
599     /**
600      * Set the isNew flag
601      *
602      * @param bool $isNew - flags if new or existing doc
603      *
604      * @return void
605      */
606     public function setIsNew($isNew)
607     {
608         $this->_isNew = (bool) $isNew;
609     }
610 
611     /**
612      * Get the isNew flag
613      *
614      * @return bool $isNew - flags if new or existing doc
615      */
616     public function getIsNew()
617     {
618         return $this->_isNew;
619     }
620 
621     /**
622      * Set the internal document id
623      *
624      * This will throw if the id of an existing document gets updated to some other id
625      *
626      * @throws ClientException
627      *
628      * @param string $id - internal document id
629      *
630      * @return void
631      */
632     public function setInternalId($id)
633     {
634         if ($this->_id !== null && $this->_id !== $id) {
635             throw new ClientException('Should not update the id of an existing document');
636         }
637 
638 
639         if (!preg_match('/^[a-zA-Z0-9_-]{1,64}\/[a-zA-Z0-9_:\.@\-()+,=;$!*\'%]{1,254}$/', $id)) {
640             throw new ClientException('Invalid format for document id');
641         }
642 
643         $this->_id = (string) $id;
644     }
645 
646     /**
647      * Set the internal document key
648      *
649      * This will throw if the key of an existing document gets updated to some other key
650      *
651      * @throws ClientException
652      *
653      * @param string $key - internal document key
654      *
655      * @return void
656      */
657     public function setInternalKey($key)
658     {
659         if ($this->_key !== null && $this->_key !== $key) {
660             throw new ClientException('Should not update the key of an existing document');
661         }
662 
663         if (!preg_match('/^[a-zA-Z0-9_:\.@\-()+,=;$!*\'%]{1,254}$/', $key)) {
664             throw new ClientException('Invalid format for document key');
665         }
666 
667         $this->_key = (string) $key;
668     }
669 
670     /**
671      * Get the internal document id (if already known)
672      *
673      * Document ids are generated on the server only. Document ids consist of collection id and
674      * document id, in the format collectionId/documentId
675      *
676      * @return string - internal document id, might be NULL if document does not yet have an id
677      */
678     public function getInternalId()
679     {
680         return $this->_id;
681     }
682 
683     /**
684      * Get the internal document key (if already known)
685      *
686      * @return string - internal document key, might be NULL if document does not yet have a key
687      */
688     public function getInternalKey()
689     {
690         return $this->_key;
691     }
692 
693     /**
694      * Convenience function to get the document handle (if already known) - is an alias to getInternalId()
695      *
696      * Document handles are generated on the server only. Document handles consist of collection id and
697      * document id, in the format collectionId/documentId
698      *
699      * @return string - internal document id, might be NULL if document does not yet have an id
700      */
701     public function getHandle()
702     {
703         return $this->getInternalId();
704     }
705 
706     /**
707      * Get the document id (if already known)
708      *
709      * Document ids are generated on the server only. Document ids are numeric but might be
710      * bigger than PHP_INT_MAX. To reliably store a document id elsewhere, a PHP string should be used
711      *
712      * @return mixed - document id, might be NULL if document does not yet have an id
713      */
714     public function getId()
715     {
716         @list(, $documentId) = explode('/', $this->_id, 2);
717 
718         return $documentId;
719     }
720 
721     /**
722      * Get the document key (if already known).
723      * Alias function for getInternalKey()
724      *
725      * @return mixed - document key, might be NULL if document does not yet have a key
726      */
727     public function getKey()
728     {
729 
730         return $this->getInternalKey();
731     }
732 
733     /**
734      * Get the collection id (if already known)
735      *
736      * Collection ids are generated on the server only. Collection ids are numeric but might be
737      * bigger than PHP_INT_MAX. To reliably store a collection id elsewhere, a PHP string should be used
738      *
739      * @return mixed - collection id, might be NULL if document does not yet have an id
740      */
741     public function getCollectionId()
742     {
743         @list($collectionId) = explode('/', $this->_id, 2);
744 
745         return $collectionId;
746     }
747 
748     /**
749      * Set the document revision
750      *
751      * Revision ids are generated on the server only.
752      *
753      * Document ids are strings, even if they look "numeric"
754      * To reliably store a document id elsewhere, a PHP string must be used
755      *
756      * @param mixed $rev - revision id
757      *
758      * @return void
759      */
760     public function setRevision($rev)
761     {
762         $this->_rev = (string) $rev;
763     }
764 
765     /**
766      * Get the document revision (if already known)
767      *
768      * @return mixed - revision id, might be NULL if document does not yet have an id
769      */
770     public function getRevision()
771     {
772         return $this->_rev;
773     }
774 }
775 
ArangoDB-PHP API Documentation API documentation generated by ApiGen