1 <?php
2
3 /**
4 * ArangoDB PHP client: transaction
5 *
6 * @package triagens\ArangoDb
7 * @author Frank Mayer
8 * @copyright Copyright 2013, triagens GmbH, Cologne, Germany
9 */
10
11 namespace triagens\ArangoDb;
12
13 /**
14 * Transaction object
15 *
16 * A transaction is an object that is used to prepare and send a transaction
17 * to the server.
18 *
19 * The object encapsulates:<br />
20 * <ul>
21 * <li> the collections definitions for locking
22 * <li> the actual javascript function
23 * <li> additional options like waitForSync, lockTimeout and params
24 * </ul>
25 *
26 * The transaction object requires the connection object and can be initialized
27 * with or without initial transaction configuration.
28 * Any configuration can be set and retrieved by the object's methods like this:<br />
29 *
30 * <pre>
31 * $this->setAction('function (){your code};');
32 * $this->setCollections(array('read' => 'my_read_collection, 'write' => array('col_1', 'col2')));
33 * </pre>
34 * <br />
35 * or like this:
36 * <pre>
37 * $this->action('function (){your code};');
38 * $this->collections(array('read' => 'my_read_collection, 'write' => array('col_1', 'col2')));
39 * </pre>
40 * <br />
41 * There are also helper functions to set collections directly, based on their locking:
42 * <pre>
43 * $this->setWriteCollections($array or $string if single collection)
44 * $this->setReadCollections($array or $string if single collection)
45 * </pre>
46 * <br />
47 *
48 * @property array $collection - The collections array that includes both read and write collection definitions
49 * @property mixed $readCollection - The read-collections array or string (if only one)
50 * @property mixed $writeCollection - The write-collections array or string (if only one)
51 * @property string $action - The action to pass to the server
52 * @property bool $waitForSync - WaitForSync on the transaction
53 * @property int $lockTimeout - LockTimeout on the transaction
54 *
55 * @package triagens\ArangoDb
56 * @since 1.3
57 */
58 class Transaction
59 {
60 /**
61 * The connection object
62 *
63 * @var Connection
64 */
65 private $_connection;
66
67 /**
68 * The transaction's attributes.
69 *
70 * @var array
71 */
72 protected $attributes = [];
73
74 /**
75 * Collections index
76 */
77 const ENTRY_COLLECTIONS = 'collections';
78
79 /**
80 * Action index
81 */
82 const ENTRY_ACTION = 'action';
83
84 /**
85 * WaitForSync index
86 */
87 const ENTRY_WAIT_FOR_SYNC = 'waitForSync';
88
89 /**
90 * Lock timeout index
91 */
92 const ENTRY_LOCK_TIMEOUT = 'lockTimeout';
93
94 /**
95 * Params index
96 */
97 const ENTRY_PARAMS = 'params';
98
99 /**
100 * Read index
101 */
102 const ENTRY_READ = 'read';
103
104 /**
105 * WRITE index
106 */
107 const ENTRY_WRITE = 'write';
108
109 /**
110 * @var $_action string The action property of the transaction.
111 */
112 protected $_action;
113
114 /**
115 * Initialise the transaction object
116 *
117 * The $transaction array can be used to specify the collections, action and further
118 * options for the transaction in form of an array.
119 *
120 * Example:
121 * array(
122 * 'collections' => array(
123 * 'write' => array(
124 * 'my_collection'
125 * )
126 * ),
127 * 'action' => 'function (){}',
128 * 'waitForSync' => true
129 * )
130 *
131 *
132 * @param Connection $connection - the connection to be used
133 * @param array $transactionArray - transaction initialization data
134 *
135 * @throws \triagens\ArangoDb\ClientException
136 */
137 public function __construct(Connection $connection, array $transactionArray = null)
138 {
139 $this->_connection = $connection;
140 if (is_array($transactionArray)) {
141 $this->buildTransactionAttributesFromArray($transactionArray);
142 }
143 }
144
145
146 /**
147 * Execute the transaction
148 *
149 * This will post the query to the server and return the results as
150 * a Cursor. The cursor can then be used to iterate the results.
151 *
152 * @throws Exception throw exception if transaction failed
153 * @return mixed true if successful without a return value or the return value if one was set in the action
154 */
155 public function execute()
156 {
157 $response = $this->_connection->post(
158 Urls::URL_TRANSACTION,
159 $this->getConnection()->json_encode_wrapper($this->attributes)
160 );
161 $responseArray = $response->getJson();
162 if (isset($responseArray['result'])) {
163 return $responseArray['result'];
164 }
165
166 return true;
167 }
168
169
170 /**
171 * Return the connection object
172 *
173 * @return Connection - the connection object
174 */
175 protected function getConnection()
176 {
177 return $this->_connection;
178 }
179
180
181 /**
182 * Set the collections array.
183 *
184 * The array should have 2 sub-arrays, namely 'read' and 'write' which should hold the respective collections
185 * for the transaction
186 *
187 * @param array $value
188 */
189 public function setCollections(array $value)
190 {
191 if (array_key_exists('read', $value)) {
192 $this->setReadCollections($value['read']);
193 }
194 if (array_key_exists('write', $value)) {
195 $this->setWriteCollections($value['write']);
196 }
197 }
198
199
200 /**
201 * Get collections array
202 *
203 * This holds the read and write collections of the transaction
204 *
205 * @return array $value
206 */
207 public function getCollections()
208 {
209 return $this->get(self::ENTRY_COLLECTIONS);
210 }
211
212
213 /**
214 * set action value
215 *
216 * @param string $value
217 *
218 * @throws \triagens\ArangoDb\ClientException
219 */
220 public function setAction($value)
221 {
222 $this->set(self::ENTRY_ACTION, (string) $value);
223 }
224
225
226 /**
227 * get action value
228 *
229 * @return string action
230 */
231 public function getAction()
232 {
233 return $this->get(self::ENTRY_ACTION);
234 }
235
236
237 /**
238 * set waitForSync value
239 *
240 * @param bool $value
241 *
242 * @throws \triagens\ArangoDb\ClientException
243 */
244 public function setWaitForSync($value)
245 {
246 $this->set(self::ENTRY_WAIT_FOR_SYNC, (bool) $value);
247 }
248
249
250 /**
251 * get waitForSync value
252 *
253 * @return bool waitForSync
254 */
255 public function getWaitForSync()
256 {
257 return $this->get(self::ENTRY_WAIT_FOR_SYNC);
258 }
259
260
261 /**
262 * Set lockTimeout value
263 *
264 * @param int $value
265 *
266 * @throws \triagens\ArangoDb\ClientException
267 */
268 public function setLockTimeout($value)
269 {
270 $this->set(self::ENTRY_LOCK_TIMEOUT, (int) $value);
271 }
272
273
274 /**
275 * Get lockTimeout value
276 *
277 * @return int lockTimeout
278 */
279 public function getLockTimeout()
280 {
281 return $this->get(self::ENTRY_LOCK_TIMEOUT);
282 }
283
284
285 /**
286 * Set params value
287 *
288 * @param array $value
289 *
290 * @throws \triagens\ArangoDb\ClientException
291 */
292 public function setParams(array $value)
293 {
294 $this->set(self::ENTRY_PARAMS, (array) $value);
295 }
296
297
298 /**
299 * Get params value
300 *
301 * @return array params
302 */
303 public function getParams()
304 {
305 return $this->get(self::ENTRY_PARAMS);
306 }
307
308
309 /**
310 * Convenience function to directly set write-collections without having to access
311 * them from the collections attribute.
312 *
313 * @param array $value
314 */
315 public function setWriteCollections($value)
316 {
317
318 $this->attributes[self::ENTRY_COLLECTIONS][self::ENTRY_WRITE] = $value;
319 }
320
321
322 /**
323 * Convenience function to directly get write-collections without having to access
324 * them from the collections attribute.
325 *
326 * @return array params
327 */
328 public function getWriteCollections()
329 {
330 return $this->attributes[self::ENTRY_COLLECTIONS][self::ENTRY_WRITE];
331 }
332
333
334 /**
335 * Convenience function to directly set read-collections without having to access
336 * them from the collections attribute.
337 *
338 * @param array $value
339 */
340 public function setReadCollections($value)
341 {
342
343 $this->attributes[self::ENTRY_COLLECTIONS][self::ENTRY_READ] = $value;
344 }
345
346
347 /**
348 * Convenience function to directly get read-collections without having to access
349 * them from the collections attribute.
350 *
351 * @return array params
352 */
353 public function getReadCollections()
354 {
355 return $this->attributes[self::ENTRY_COLLECTIONS][self::ENTRY_READ];
356 }
357
358
359 /**
360 * Sets an attribute
361 *
362 * @param $key
363 * @param $value
364 *
365 * @throws ClientException
366 */
367 public function set($key, $value)
368 {
369 if (!is_string($key)) {
370 throw new ClientException('Invalid document attribute key');
371 }
372
373 $this->attributes[$key] = $value;
374 }
375
376
377 /**
378 * Set an attribute, magic method
379 *
380 * This is a magic method that allows the object to be used without
381 * declaring all document attributes first.
382 *
383 * @throws ClientException
384 *
385 * @magic
386 *
387 * @param string $key - attribute name
388 * @param mixed $value - value for attribute
389 *
390 * @return void
391 */
392 public function __set($key, $value)
393 {
394 switch ($key) {
395 case self::ENTRY_COLLECTIONS :
396 $this->setCollections($value);
397 break;
398 case 'writeCollections' :
399 $this->setWriteCollections($value);
400 break;
401 case 'readCollections' :
402 $this->setReadCollections($value);
403 break;
404 case self::ENTRY_ACTION :
405 $this->setAction($value);
406 break;
407 case self::ENTRY_WAIT_FOR_SYNC :
408 $this->setWaitForSync($value);
409 break;
410 case self::ENTRY_LOCK_TIMEOUT :
411 $this->setLockTimeout($value);
412 break;
413 case self::ENTRY_PARAMS :
414 $this->setParams($value);
415 break;
416 default:
417 $this->set($key, $value);
418 break;
419 }
420 }
421
422
423 /**
424 * Get an attribute
425 *
426 * @param string $key - name of attribute
427 *
428 * @return mixed - value of attribute, NULL if attribute is not set
429 */
430 public function get($key)
431 {
432 switch ($key) {
433 case 'writeCollections' :
434 return $this->getWriteCollections();
435 break;
436 case 'readCollections' :
437 return $this->getReadCollections();
438 break;
439 }
440
441 if (isset($this->attributes[$key])) {
442 return $this->attributes[$key];
443 }
444
445 return null;
446 }
447
448
449 /**
450 * Get an attribute, magic method
451 *
452 * This function is mapped to get() internally.
453 *
454 * @magic
455 *
456 * @param string $key - name of attribute
457 *
458 * @return mixed - value of attribute, NULL if attribute is not set
459 */
460 public function __get($key)
461 {
462 return $this->get($key);
463 }
464
465
466 /**
467 * Is triggered by calling isset() or empty() on inaccessible properties.
468 *
469 * @param string $key - name of attribute
470 *
471 * @return boolean returns true or false (set or not set)
472 */
473 public function __isset($key)
474 {
475 if (isset($this->attributes[$key])) {
476 return true;
477 }
478
479 return false;
480 }
481
482
483 /**
484 * Returns the action string
485 *
486 * @magic
487 *
488 * @return string - the current action string
489 */
490 public function __toString()
491 {
492 return $this->_action;
493 }
494
495
496 /**
497 * Build the object's attributes from a given array
498 *
499 * @param $options
500 *
501 * @throws \triagens\ArangoDb\ClientException
502 */
503 public function buildTransactionAttributesFromArray($options)
504 {
505 if (isset($options[self::ENTRY_COLLECTIONS])) {
506 $this->setCollections($options[self::ENTRY_COLLECTIONS]);
507 }
508
509 if (isset($options[self::ENTRY_ACTION])) {
510 $this->setAction($options[self::ENTRY_ACTION]);
511 }
512
513 if (isset($options[self::ENTRY_WAIT_FOR_SYNC])) {
514 $this->setWaitForSync($options[self::ENTRY_WAIT_FOR_SYNC]);
515 }
516
517 if (isset($options[self::ENTRY_LOCK_TIMEOUT])) {
518 $this->setLockTimeout($options[self::ENTRY_LOCK_TIMEOUT]);
519 }
520
521 if (isset($options[self::ENTRY_PARAMS])) {
522 $this->setParams($options[self::ENTRY_PARAMS]);
523 }
524 }
525 }
526