"Fossies" - the Fresh Open Source Software Archive 
Member "icingaweb2-2.11.4/library/vendor/Zend/Db/Table/Row/Abstract.php" (26 Jan 2023, 37639 Bytes) of package /linux/www/icingaweb2-2.11.4.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code 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_Db
17 * @subpackage Table
18 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
19 * @license http://framework.zend.com/license/new-bsd New BSD License
20 * @version $Id$
21 */
22
23 /**
24 * @see Zend_Db
25 */
26
27 /**
28 * @category Zend
29 * @package Zend_Db
30 * @subpackage Table
31 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
32 * @license http://framework.zend.com/license/new-bsd New BSD License
33 */
34 abstract class Zend_Db_Table_Row_Abstract implements ArrayAccess, IteratorAggregate
35 {
36
37 /**
38 * The data for each column in the row (column_name => value).
39 * The keys must match the physical names of columns in the
40 * table for which this row is defined.
41 *
42 * @var array
43 */
44 protected $_data = array();
45
46 /**
47 * This is set to a copy of $_data when the data is fetched from
48 * a database, specified as a new tuple in the constructor, or
49 * when dirty data is posted to the database with save().
50 *
51 * @var array
52 */
53 protected $_cleanData = array();
54
55 /**
56 * Tracks columns where data has been updated. Allows more specific insert and
57 * update operations.
58 *
59 * @var array
60 */
61 protected $_modifiedFields = array();
62
63 /**
64 * Zend_Db_Table_Abstract parent class or instance.
65 *
66 * @var Zend_Db_Table_Abstract
67 */
68 protected $_table = null;
69
70 /**
71 * Connected is true if we have a reference to a live
72 * Zend_Db_Table_Abstract object.
73 * This is false after the Rowset has been deserialized.
74 *
75 * @var boolean
76 */
77 protected $_connected = true;
78
79 /**
80 * A row is marked read only if it contains columns that are not physically represented within
81 * the database schema (e.g. evaluated columns/Zend_Db_Expr columns). This can also be passed
82 * as a run-time config options as a means of protecting row data.
83 *
84 * @var boolean
85 */
86 protected $_readOnly = false;
87
88 /**
89 * Name of the class of the Zend_Db_Table_Abstract object.
90 *
91 * @var string
92 */
93 protected $_tableClass = null;
94
95 /**
96 * Primary row key(s).
97 *
98 * @var array
99 */
100 protected $_primary;
101
102 /**
103 * Constructor.
104 *
105 * Supported params for $config are:-
106 * - table = class name or object of type Zend_Db_Table_Abstract
107 * - data = values of columns in this row.
108 *
109 * @param array $config OPTIONAL Array of user-specified config options.
110 * @return void
111 * @throws Zend_Db_Table_Row_Exception
112 */
113 public function __construct(array $config = array())
114 {
115 if (isset($config['table']) && $config['table'] instanceof Zend_Db_Table_Abstract) {
116 $this->_table = $config['table'];
117 $this->_tableClass = get_class($this->_table);
118 } elseif ($this->_tableClass !== null) {
119 $this->_table = $this->_getTableFromString($this->_tableClass);
120 }
121
122 if (isset($config['data'])) {
123 if (!is_array($config['data'])) {
124 throw new Zend_Db_Table_Row_Exception('Data must be an array');
125 }
126 $this->_data = $config['data'];
127 }
128 if (isset($config['stored']) && $config['stored'] === true) {
129 $this->_cleanData = $this->_data;
130 }
131
132 if (isset($config['readOnly']) && $config['readOnly'] === true) {
133 $this->setReadOnly(true);
134 }
135
136 // Retrieve primary keys from table schema
137 if (($table = $this->_getTable())) {
138 $info = $table->info();
139 $this->_primary = (array) $info['primary'];
140 }
141
142 $this->init();
143 }
144
145 /**
146 * Transform a column name from the user-specified form
147 * to the physical form used in the database.
148 * You can override this method in a custom Row class
149 * to implement column name mappings, for example inflection.
150 *
151 * @param string $columnName Column name given.
152 * @return string The column name after transformation applied (none by default).
153 * @throws Zend_Db_Table_Row_Exception if the $columnName is not a string.
154 */
155 protected function _transformColumn($columnName)
156 {
157 if (!is_string($columnName)) {
158 throw new Zend_Db_Table_Row_Exception('Specified column is not a string');
159 }
160 // Perform no transformation by default
161 return $columnName;
162 }
163
164 /**
165 * Retrieve row field value
166 *
167 * @param string $columnName The user-specified column name.
168 * @return string The corresponding column value.
169 * @throws Zend_Db_Table_Row_Exception if the $columnName is not a column in the row.
170 */
171 public function __get($columnName)
172 {
173 $columnName = $this->_transformColumn($columnName);
174 if (!array_key_exists($columnName, $this->_data)) {
175 throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is not in the row");
176 }
177 return $this->_data[$columnName];
178 }
179
180 /**
181 * Set row field value
182 *
183 * @param string $columnName The column key.
184 * @param mixed $value The value for the property.
185 * @return void
186 * @throws Zend_Db_Table_Row_Exception
187 */
188 public function __set($columnName, $value)
189 {
190 $columnName = $this->_transformColumn($columnName);
191 if (!array_key_exists($columnName, $this->_data)) {
192 throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is not in the row");
193 }
194 $this->_data[$columnName] = $value;
195 $this->_modifiedFields[$columnName] = true;
196 }
197
198 /**
199 * Unset row field value
200 *
201 * @param string $columnName The column key.
202 * @return Zend_Db_Table_Row_Abstract
203 * @throws Zend_Db_Table_Row_Exception
204 */
205 public function __unset($columnName)
206 {
207 $columnName = $this->_transformColumn($columnName);
208 if (!array_key_exists($columnName, $this->_data)) {
209 throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is not in the row");
210 }
211 if ($this->isConnected() && in_array($columnName, $this->_table->info('primary'))) {
212 throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is a primary key and should not be unset");
213 }
214 unset($this->_data[$columnName]);
215 return $this;
216 }
217
218 /**
219 * Test existence of row field
220 *
221 * @param string $columnName The column key.
222 * @return boolean
223 */
224 public function __isset($columnName)
225 {
226 $columnName = $this->_transformColumn($columnName);
227 return array_key_exists($columnName, $this->_data);
228 }
229
230 /**
231 * Store table, primary key and data in serialized object
232 *
233 * @return array
234 */
235 public function __sleep()
236 {
237 return array('_tableClass', '_primary', '_data', '_cleanData', '_readOnly' ,'_modifiedFields');
238 }
239
240 /**
241 * Setup to do on wakeup.
242 * A de-serialized Row should not be assumed to have access to a live
243 * database connection, so set _connected = false.
244 *
245 * @return void
246 */
247 public function __wakeup()
248 {
249 $this->_connected = false;
250 }
251
252 /**
253 * Proxy to __isset
254 * Required by the ArrayAccess implementation
255 *
256 * @param string $offset
257 * @return boolean
258 */
259 public function offsetExists($offset)
260 {
261 return $this->__isset($offset);
262 }
263
264 /**
265 * Proxy to __get
266 * Required by the ArrayAccess implementation
267 *
268 * @param string $offset
269 * @return string
270 */
271 public function offsetGet($offset)
272 {
273 return $this->__get($offset);
274 }
275
276 /**
277 * Proxy to __set
278 * Required by the ArrayAccess implementation
279 *
280 * @param string $offset
281 * @param mixed $value
282 */
283 public function offsetSet($offset, $value)
284 {
285 $this->__set($offset, $value);
286 }
287
288 /**
289 * Proxy to __unset
290 * Required by the ArrayAccess implementation
291 *
292 * @param string $offset
293 */
294 public function offsetUnset($offset)
295 {
296 return $this->__unset($offset);
297 }
298
299 /**
300 * Initialize object
301 *
302 * Called from {@link __construct()} as final step of object instantiation.
303 *
304 * @return void
305 */
306 public function init()
307 {
308 }
309
310 /**
311 * Returns the table object, or null if this is disconnected row
312 *
313 * @return Zend_Db_Table_Abstract|null
314 */
315 public function getTable()
316 {
317 return $this->_table;
318 }
319
320 /**
321 * Set the table object, to re-establish a live connection
322 * to the database for a Row that has been de-serialized.
323 *
324 * @param Zend_Db_Table_Abstract $table
325 * @return boolean
326 * @throws Zend_Db_Table_Row_Exception
327 */
328 public function setTable(Zend_Db_Table_Abstract $table = null)
329 {
330 if ($table == null) {
331 $this->_table = null;
332 $this->_connected = false;
333 return false;
334 }
335
336 $tableClass = get_class($table);
337 if (! $table instanceof $this->_tableClass) {
338 throw new Zend_Db_Table_Row_Exception("The specified Table is of class $tableClass, expecting class to be instance of $this->_tableClass");
339 }
340
341 $this->_table = $table;
342 $this->_tableClass = $tableClass;
343
344 $info = $this->_table->info();
345
346 if ($info['cols'] != array_keys($this->_data)) {
347 throw new Zend_Db_Table_Row_Exception('The specified Table does not have the same columns as the Row');
348 }
349
350 if (! array_intersect((array) $this->_primary, $info['primary']) == (array) $this->_primary) {
351
352 throw new Zend_Db_Table_Row_Exception("The specified Table '$tableClass' does not have the same primary key as the Row");
353 }
354
355 $this->_connected = true;
356 return true;
357 }
358
359 /**
360 * Query the class name of the Table object for which this
361 * Row was created.
362 *
363 * @return string
364 */
365 public function getTableClass()
366 {
367 return $this->_tableClass;
368 }
369
370 /**
371 * Test the connected status of the row.
372 *
373 * @return boolean
374 */
375 public function isConnected()
376 {
377 return $this->_connected;
378 }
379
380 /**
381 * Test the read-only status of the row.
382 *
383 * @return boolean
384 */
385 public function isReadOnly()
386 {
387 return $this->_readOnly;
388 }
389
390 /**
391 * Set the read-only status of the row.
392 *
393 * @param boolean $flag
394 * @return boolean
395 */
396 public function setReadOnly($flag)
397 {
398 $this->_readOnly = (bool) $flag;
399 }
400
401 /**
402 * Returns an instance of the parent table's Zend_Db_Table_Select object.
403 *
404 * @return Zend_Db_Table_Select
405 */
406 public function select()
407 {
408 return $this->getTable()->select();
409 }
410
411 /**
412 * Saves the properties to the database.
413 *
414 * This performs an intelligent insert/update, and reloads the
415 * properties with fresh data from the table on success.
416 *
417 * @return mixed The primary key value(s), as an associative array if the
418 * key is compound, or a scalar if the key is single-column.
419 */
420 public function save()
421 {
422 /**
423 * If the _cleanData array is empty,
424 * this is an INSERT of a new row.
425 * Otherwise it is an UPDATE.
426 */
427 if (empty($this->_cleanData)) {
428 return $this->_doInsert();
429 } else {
430 return $this->_doUpdate();
431 }
432 }
433
434 /**
435 * @return mixed The primary key value(s), as an associative array if the
436 * key is compound, or a scalar if the key is single-column.
437 */
438 protected function _doInsert()
439 {
440 /**
441 * A read-only row cannot be saved.
442 */
443 if ($this->_readOnly === true) {
444 throw new Zend_Db_Table_Row_Exception('This row has been marked read-only');
445 }
446
447 /**
448 * Run pre-INSERT logic
449 */
450 $this->_insert();
451
452 /**
453 * Execute the INSERT (this may throw an exception)
454 */
455 $data = array_intersect_key($this->_data, $this->_modifiedFields);
456 $primaryKey = $this->_getTable()->insert($data);
457
458 /**
459 * Normalize the result to an array indexed by primary key column(s).
460 * The table insert() method may return a scalar.
461 */
462 if (is_array($primaryKey)) {
463 $newPrimaryKey = $primaryKey;
464 } else {
465 //ZF-6167 Use tempPrimaryKey temporary to avoid that zend encoding fails.
466 $tempPrimaryKey = (array) $this->_primary;
467 $newPrimaryKey = array(current($tempPrimaryKey) => $primaryKey);
468 }
469
470 /**
471 * Save the new primary key value in _data. The primary key may have
472 * been generated by a sequence or auto-increment mechanism, and this
473 * merge should be done before the _postInsert() method is run, so the
474 * new values are available for logging, etc.
475 */
476 $this->_data = array_merge($this->_data, $newPrimaryKey);
477
478 /**
479 * Run post-INSERT logic
480 */
481 $this->_postInsert();
482
483 /**
484 * Update the _cleanData to reflect that the data has been inserted.
485 */
486 $this->_refresh();
487
488 return $primaryKey;
489 }
490
491 /**
492 * @return mixed The primary key value(s), as an associative array if the
493 * key is compound, or a scalar if the key is single-column.
494 */
495 protected function _doUpdate()
496 {
497 /**
498 * A read-only row cannot be saved.
499 */
500 if ($this->_readOnly === true) {
501 throw new Zend_Db_Table_Row_Exception('This row has been marked read-only');
502 }
503
504 /**
505 * Get expressions for a WHERE clause
506 * based on the primary key value(s).
507 */
508 $where = $this->_getWhereQuery(false);
509
510 /**
511 * Run pre-UPDATE logic
512 */
513 $this->_update();
514
515 /**
516 * Compare the data to the modified fields array to discover
517 * which columns have been changed.
518 */
519 $diffData = array_intersect_key($this->_data, $this->_modifiedFields);
520
521 /**
522 * Were any of the changed columns part of the primary key?
523 */
524 $pkDiffData = array_intersect_key($diffData, array_flip((array)$this->_primary));
525
526 /**
527 * Execute cascading updates against dependent tables.
528 * Do this only if primary key value(s) were changed.
529 */
530 if (count($pkDiffData) > 0) {
531 $depTables = $this->_getTable()->getDependentTables();
532 if (!empty($depTables)) {
533 $pkNew = $this->_getPrimaryKey(true);
534 $pkOld = $this->_getPrimaryKey(false);
535 foreach ($depTables as $tableClass) {
536 $t = $this->_getTableFromString($tableClass);
537 $t->_cascadeUpdate($this->getTableClass(), $pkOld, $pkNew);
538 }
539 }
540 }
541
542 /**
543 * Execute the UPDATE (this may throw an exception)
544 * Do this only if data values were changed.
545 * Use the $diffData variable, so the UPDATE statement
546 * includes SET terms only for data values that changed.
547 */
548 if (count($diffData) > 0) {
549 $this->_getTable()->update($diffData, $where);
550 }
551
552 /**
553 * Run post-UPDATE logic. Do this before the _refresh()
554 * so the _postUpdate() function can tell the difference
555 * between changed data and clean (pre-changed) data.
556 */
557 $this->_postUpdate();
558
559 /**
560 * Refresh the data just in case triggers in the RDBMS changed
561 * any columns. Also this resets the _cleanData.
562 */
563 $this->_refresh();
564
565 /**
566 * Return the primary key value(s) as an array
567 * if the key is compound or a scalar if the key
568 * is a scalar.
569 */
570 $primaryKey = $this->_getPrimaryKey(true);
571 if (count($primaryKey) == 1) {
572 return current($primaryKey);
573 }
574
575 return $primaryKey;
576 }
577
578 /**
579 * Deletes existing rows.
580 *
581 * @return int The number of rows deleted.
582 */
583 public function delete()
584 {
585 /**
586 * A read-only row cannot be deleted.
587 */
588 if ($this->_readOnly === true) {
589 throw new Zend_Db_Table_Row_Exception('This row has been marked read-only');
590 }
591
592 $where = $this->_getWhereQuery();
593
594 /**
595 * Execute pre-DELETE logic
596 */
597 $this->_delete();
598
599 /**
600 * Execute cascading deletes against dependent tables
601 */
602 $depTables = $this->_getTable()->getDependentTables();
603 if (!empty($depTables)) {
604 $pk = $this->_getPrimaryKey();
605 foreach ($depTables as $tableClass) {
606 $t = $this->_getTableFromString($tableClass);
607 $t->_cascadeDelete($this->getTableClass(), $pk);
608 }
609 }
610
611 /**
612 * Execute the DELETE (this may throw an exception)
613 */
614 $result = $this->_getTable()->delete($where);
615
616 /**
617 * Execute post-DELETE logic
618 */
619 $this->_postDelete();
620
621 /**
622 * Reset all fields to null to indicate that the row is not there
623 */
624 $this->_data = array_combine(
625 array_keys($this->_data),
626 array_fill(0, count($this->_data), null)
627 );
628
629 return $result;
630 }
631
632 public function getIterator()
633 {
634 return new ArrayIterator((array) $this->_data);
635 }
636
637 /**
638 * Returns the column/value data as an array.
639 *
640 * @return array
641 */
642 public function toArray()
643 {
644 return (array)$this->_data;
645 }
646
647 /**
648 * Sets all data in the row from an array.
649 *
650 * @param array $data
651 * @return Zend_Db_Table_Row_Abstract Provides a fluent interface
652 */
653 public function setFromArray(array $data)
654 {
655 $data = array_intersect_key($data, $this->_data);
656
657 foreach ($data as $columnName => $value) {
658 $this->__set($columnName, $value);
659 }
660
661 return $this;
662 }
663
664 /**
665 * Refreshes properties from the database.
666 *
667 * @return void
668 */
669 public function refresh()
670 {
671 return $this->_refresh();
672 }
673
674 /**
675 * Retrieves an instance of the parent table.
676 *
677 * @return Zend_Db_Table_Abstract
678 */
679 protected function _getTable()
680 {
681 if (!$this->_connected) {
682 throw new Zend_Db_Table_Row_Exception('Cannot save a Row unless it is connected');
683 }
684 return $this->_table;
685 }
686
687 /**
688 * Retrieves an associative array of primary keys.
689 *
690 * @param bool $useDirty
691 * @return array
692 */
693 protected function _getPrimaryKey($useDirty = true)
694 {
695 if (!is_array($this->_primary)) {
696 throw new Zend_Db_Table_Row_Exception("The primary key must be set as an array");
697 }
698
699 $primary = array_flip($this->_primary);
700 if ($useDirty) {
701 $array = array_intersect_key($this->_data, $primary);
702 } else {
703 $array = array_intersect_key($this->_cleanData, $primary);
704 }
705 if (count($primary) != count($array)) {
706 throw new Zend_Db_Table_Row_Exception("The specified Table '$this->_tableClass' does not have the same primary key as the Row");
707 }
708 return $array;
709 }
710
711 /**
712 * Retrieves an associative array of primary keys.
713 *
714 * @param bool $useDirty
715 * @return array
716 */
717 public function getPrimaryKey($useDirty = true)
718 {
719 return $this->_getPrimaryKey($useDirty);
720 }
721
722 /**
723 * Constructs where statement for retrieving row(s).
724 *
725 * @param bool $useDirty
726 * @return array
727 */
728 protected function _getWhereQuery($useDirty = true)
729 {
730 $where = array();
731 $db = $this->_getTable()->getAdapter();
732 $primaryKey = $this->_getPrimaryKey($useDirty);
733 $info = $this->_getTable()->info();
734 $metadata = $info[Zend_Db_Table_Abstract::METADATA];
735
736 // retrieve recently updated row using primary keys
737 $where = array();
738 foreach ($primaryKey as $column => $value) {
739 $tableName = $db->quoteIdentifier($info[Zend_Db_Table_Abstract::NAME], true);
740 $type = $metadata[$column]['DATA_TYPE'];
741 $columnName = $db->quoteIdentifier($column, true);
742 $where[] = $db->quoteInto("{$tableName}.{$columnName} = ?", $value, $type);
743 }
744 return $where;
745 }
746
747 /**
748 * Refreshes properties from the database.
749 *
750 * @return void
751 */
752 protected function _refresh()
753 {
754 $where = $this->_getWhereQuery();
755 $row = $this->_getTable()->fetchRow($where);
756
757 if (null === $row) {
758 throw new Zend_Db_Table_Row_Exception('Cannot refresh row as parent is missing');
759 }
760
761 $this->_data = $row->toArray();
762 $this->_cleanData = $this->_data;
763 $this->_modifiedFields = array();
764 }
765
766 /**
767 * Allows pre-insert logic to be applied to row.
768 * Subclasses may override this method.
769 *
770 * @return void
771 */
772 protected function _insert()
773 {
774 }
775
776 /**
777 * Allows post-insert logic to be applied to row.
778 * Subclasses may override this method.
779 *
780 * @return void
781 */
782 protected function _postInsert()
783 {
784 }
785
786 /**
787 * Allows pre-update logic to be applied to row.
788 * Subclasses may override this method.
789 *
790 * @return void
791 */
792 protected function _update()
793 {
794 }
795
796 /**
797 * Allows post-update logic to be applied to row.
798 * Subclasses may override this method.
799 *
800 * @return void
801 */
802 protected function _postUpdate()
803 {
804 }
805
806 /**
807 * Allows pre-delete logic to be applied to row.
808 * Subclasses may override this method.
809 *
810 * @return void
811 */
812 protected function _delete()
813 {
814 }
815
816 /**
817 * Allows post-delete logic to be applied to row.
818 * Subclasses may override this method.
819 *
820 * @return void
821 */
822 protected function _postDelete()
823 {
824 }
825
826 /**
827 * Prepares a table reference for lookup.
828 *
829 * Ensures all reference keys are set and properly formatted.
830 *
831 * @param Zend_Db_Table_Abstract $dependentTable
832 * @param Zend_Db_Table_Abstract $parentTable
833 * @param string $ruleKey
834 * @return array
835 */
836 protected function _prepareReference(Zend_Db_Table_Abstract $dependentTable, Zend_Db_Table_Abstract $parentTable, $ruleKey)
837 {
838 $parentTableName = (get_class($parentTable) === 'Zend_Db_Table') ? $parentTable->getDefinitionConfigName() : get_class($parentTable);
839 $map = $dependentTable->getReference($parentTableName, $ruleKey);
840
841 if (!isset($map[Zend_Db_Table_Abstract::REF_COLUMNS])) {
842 $parentInfo = $parentTable->info();
843 $map[Zend_Db_Table_Abstract::REF_COLUMNS] = array_values((array) $parentInfo['primary']);
844 }
845
846 $map[Zend_Db_Table_Abstract::COLUMNS] = (array) $map[Zend_Db_Table_Abstract::COLUMNS];
847 $map[Zend_Db_Table_Abstract::REF_COLUMNS] = (array) $map[Zend_Db_Table_Abstract::REF_COLUMNS];
848
849 return $map;
850 }
851
852 /**
853 * Query a dependent table to retrieve rows matching the current row.
854 *
855 * @param string|Zend_Db_Table_Abstract $dependentTable
856 * @param string OPTIONAL $ruleKey
857 * @param Zend_Db_Table_Select OPTIONAL $select
858 * @return Zend_Db_Table_Rowset_Abstract Query result from $dependentTable
859 * @throws Zend_Db_Table_Row_Exception If $dependentTable is not a table or is not loadable.
860 */
861 public function findDependentRowset($dependentTable, $ruleKey = null, Zend_Db_Table_Select $select = null)
862 {
863 $db = $this->_getTable()->getAdapter();
864
865 if (is_string($dependentTable)) {
866 $dependentTable = $this->_getTableFromString($dependentTable);
867 }
868
869 if (!$dependentTable instanceof Zend_Db_Table_Abstract) {
870 $type = gettype($dependentTable);
871 if ($type == 'object') {
872 $type = get_class($dependentTable);
873 }
874 throw new Zend_Db_Table_Row_Exception("Dependent table must be a Zend_Db_Table_Abstract, but it is $type");
875 }
876
877 // even if we are interacting between a table defined in a class and a
878 // table via extension, ensure to persist the definition
879 if (($tableDefinition = $this->_table->getDefinition()) !== null
880 && ($dependentTable->getDefinition() == null)) {
881 $dependentTable->setOptions(array(Zend_Db_Table_Abstract::DEFINITION => $tableDefinition));
882 }
883
884 if ($select === null) {
885 $select = $dependentTable->select();
886 } else {
887 $select->setTable($dependentTable);
888 }
889
890 $map = $this->_prepareReference($dependentTable, $this->_getTable(), $ruleKey);
891
892 for ($i = 0; $i < count($map[Zend_Db_Table_Abstract::COLUMNS]); ++$i) {
893 $parentColumnName = $db->foldCase($map[Zend_Db_Table_Abstract::REF_COLUMNS][$i]);
894 $value = $this->_data[$parentColumnName];
895 // Use adapter from dependent table to ensure correct query construction
896 $dependentDb = $dependentTable->getAdapter();
897 $dependentColumnName = $dependentDb->foldCase($map[Zend_Db_Table_Abstract::COLUMNS][$i]);
898 $dependentColumn = $dependentDb->quoteIdentifier($dependentColumnName, true);
899 $dependentInfo = $dependentTable->info();
900 $type = $dependentInfo[Zend_Db_Table_Abstract::METADATA][$dependentColumnName]['DATA_TYPE'];
901 $select->where("$dependentColumn = ?", $value, $type);
902 }
903
904 return $dependentTable->fetchAll($select);
905 }
906
907 /**
908 * Query a parent table to retrieve the single row matching the current row.
909 *
910 * @param string|Zend_Db_Table_Abstract $parentTable
911 * @param string OPTIONAL $ruleKey
912 * @param Zend_Db_Table_Select OPTIONAL $select
913 * @return Zend_Db_Table_Row_Abstract Query result from $parentTable
914 * @throws Zend_Db_Table_Row_Exception If $parentTable is not a table or is not loadable.
915 */
916 public function findParentRow($parentTable, $ruleKey = null, Zend_Db_Table_Select $select = null)
917 {
918 $db = $this->_getTable()->getAdapter();
919
920 if (is_string($parentTable)) {
921 $parentTable = $this->_getTableFromString($parentTable);
922 }
923
924 if (!$parentTable instanceof Zend_Db_Table_Abstract) {
925 $type = gettype($parentTable);
926 if ($type == 'object') {
927 $type = get_class($parentTable);
928 }
929 throw new Zend_Db_Table_Row_Exception("Parent table must be a Zend_Db_Table_Abstract, but it is $type");
930 }
931
932 // even if we are interacting between a table defined in a class and a
933 // table via extension, ensure to persist the definition
934 if (($tableDefinition = $this->_table->getDefinition()) !== null
935 && ($parentTable->getDefinition() == null)) {
936 $parentTable->setOptions(array(Zend_Db_Table_Abstract::DEFINITION => $tableDefinition));
937 }
938
939 if ($select === null) {
940 $select = $parentTable->select();
941 } else {
942 $select->setTable($parentTable);
943 }
944
945 $map = $this->_prepareReference($this->_getTable(), $parentTable, $ruleKey);
946
947 // iterate the map, creating the proper wheres
948 for ($i = 0; $i < count($map[Zend_Db_Table_Abstract::COLUMNS]); ++$i) {
949 $dependentColumnName = $db->foldCase($map[Zend_Db_Table_Abstract::COLUMNS][$i]);
950 $value = $this->_data[$dependentColumnName];
951 // Use adapter from parent table to ensure correct query construction
952 $parentDb = $parentTable->getAdapter();
953 $parentColumnName = $parentDb->foldCase($map[Zend_Db_Table_Abstract::REF_COLUMNS][$i]);
954 $parentColumn = $parentDb->quoteIdentifier($parentColumnName, true);
955 $parentInfo = $parentTable->info();
956
957 // determine where part
958 $type = $parentInfo[Zend_Db_Table_Abstract::METADATA][$parentColumnName]['DATA_TYPE'];
959 $nullable = $parentInfo[Zend_Db_Table_Abstract::METADATA][$parentColumnName]['NULLABLE'];
960 if ($value === null && $nullable == true) {
961 $select->where("$parentColumn IS NULL");
962 } elseif ($value === null && $nullable == false) {
963 return null;
964 } else {
965 $select->where("$parentColumn = ?", $value, $type);
966 }
967
968 }
969
970 return $parentTable->fetchRow($select);
971 }
972
973 /**
974 * @param string|Zend_Db_Table_Abstract $matchTable
975 * @param string|Zend_Db_Table_Abstract $intersectionTable
976 * @param string OPTIONAL $callerRefRule
977 * @param string OPTIONAL $matchRefRule
978 * @param Zend_Db_Table_Select OPTIONAL $select
979 * @return Zend_Db_Table_Rowset_Abstract Query result from $matchTable
980 * @throws Zend_Db_Table_Row_Exception If $matchTable or $intersectionTable is not a table class or is not loadable.
981 */
982 public function findManyToManyRowset($matchTable, $intersectionTable, $callerRefRule = null,
983 $matchRefRule = null, Zend_Db_Table_Select $select = null)
984 {
985 $db = $this->_getTable()->getAdapter();
986
987 if (is_string($intersectionTable)) {
988 $intersectionTable = $this->_getTableFromString($intersectionTable);
989 }
990
991 if (!$intersectionTable instanceof Zend_Db_Table_Abstract) {
992 $type = gettype($intersectionTable);
993 if ($type == 'object') {
994 $type = get_class($intersectionTable);
995 }
996 throw new Zend_Db_Table_Row_Exception("Intersection table must be a Zend_Db_Table_Abstract, but it is $type");
997 }
998
999 // even if we are interacting between a table defined in a class and a
1000 // table via extension, ensure to persist the definition
1001 if (($tableDefinition = $this->_table->getDefinition()) !== null
1002 && ($intersectionTable->getDefinition() == null)) {
1003 $intersectionTable->setOptions(array(Zend_Db_Table_Abstract::DEFINITION => $tableDefinition));
1004 }
1005
1006 if (is_string($matchTable)) {
1007 $matchTable = $this->_getTableFromString($matchTable);
1008 }
1009
1010 if (! $matchTable instanceof Zend_Db_Table_Abstract) {
1011 $type = gettype($matchTable);
1012 if ($type == 'object') {
1013 $type = get_class($matchTable);
1014 }
1015 throw new Zend_Db_Table_Row_Exception("Match table must be a Zend_Db_Table_Abstract, but it is $type");
1016 }
1017
1018 // even if we are interacting between a table defined in a class and a
1019 // table via extension, ensure to persist the definition
1020 if (($tableDefinition = $this->_table->getDefinition()) !== null
1021 && ($matchTable->getDefinition() == null)) {
1022 $matchTable->setOptions(array(Zend_Db_Table_Abstract::DEFINITION => $tableDefinition));
1023 }
1024
1025 if ($select === null) {
1026 $select = $matchTable->select();
1027 } else {
1028 $select->setTable($matchTable);
1029 }
1030
1031 // Use adapter from intersection table to ensure correct query construction
1032 $interInfo = $intersectionTable->info();
1033 $interDb = $intersectionTable->getAdapter();
1034 $interName = $interInfo['name'];
1035 $interSchema = isset($interInfo['schema']) ? $interInfo['schema'] : null;
1036 $matchInfo = $matchTable->info();
1037 $matchName = $matchInfo['name'];
1038 $matchSchema = isset($matchInfo['schema']) ? $matchInfo['schema'] : null;
1039
1040 $matchMap = $this->_prepareReference($intersectionTable, $matchTable, $matchRefRule);
1041
1042 for ($i = 0; $i < count($matchMap[Zend_Db_Table_Abstract::COLUMNS]); ++$i) {
1043 $interCol = $interDb->quoteIdentifier('i' . '.' . $matchMap[Zend_Db_Table_Abstract::COLUMNS][$i], true);
1044 $matchCol = $interDb->quoteIdentifier('m' . '.' . $matchMap[Zend_Db_Table_Abstract::REF_COLUMNS][$i], true);
1045 $joinCond[] = "$interCol = $matchCol";
1046 }
1047 $joinCond = implode(' AND ', $joinCond);
1048
1049 $select->from(array('i' => $interName), array(), $interSchema)
1050 ->joinInner(array('m' => $matchName), $joinCond, Zend_Db_Select::SQL_WILDCARD, $matchSchema)
1051 ->setIntegrityCheck(false);
1052
1053 $callerMap = $this->_prepareReference($intersectionTable, $this->_getTable(), $callerRefRule);
1054
1055 for ($i = 0; $i < count($callerMap[Zend_Db_Table_Abstract::COLUMNS]); ++$i) {
1056 $callerColumnName = $db->foldCase($callerMap[Zend_Db_Table_Abstract::REF_COLUMNS][$i]);
1057 $value = $this->_data[$callerColumnName];
1058 $interColumnName = $interDb->foldCase($callerMap[Zend_Db_Table_Abstract::COLUMNS][$i]);
1059 $interCol = $interDb->quoteIdentifier("i.$interColumnName", true);
1060 $interInfo = $intersectionTable->info();
1061 $type = $interInfo[Zend_Db_Table_Abstract::METADATA][$interColumnName]['DATA_TYPE'];
1062 $select->where($interDb->quoteInto("$interCol = ?", $value, $type));
1063 }
1064
1065 $stmt = $select->query();
1066
1067 $config = array(
1068 'table' => $matchTable,
1069 'data' => $stmt->fetchAll(Zend_Db::FETCH_ASSOC),
1070 'rowClass' => $matchTable->getRowClass(),
1071 'readOnly' => false,
1072 'stored' => true
1073 );
1074
1075 $rowsetClass = $matchTable->getRowsetClass();
1076 if (!class_exists($rowsetClass)) {
1077 try {
1078 Zend_Loader::loadClass($rowsetClass);
1079 } catch (Zend_Exception $e) {
1080 throw new Zend_Db_Table_Row_Exception($e->getMessage(), $e->getCode(), $e);
1081 }
1082 }
1083 $rowset = new $rowsetClass($config);
1084 return $rowset;
1085 }
1086
1087 /**
1088 * Turn magic function calls into non-magic function calls
1089 * to the above methods.
1090 *
1091 * @param string $method
1092 * @param array $args OPTIONAL Zend_Db_Table_Select query modifier
1093 * @return Zend_Db_Table_Row_Abstract|Zend_Db_Table_Rowset_Abstract
1094 * @throws Zend_Db_Table_Row_Exception If an invalid method is called.
1095 */
1096 public function __call($method, array $args)
1097 {
1098 $matches = array();
1099
1100 if (count($args) && $args[0] instanceof Zend_Db_Table_Select) {
1101 $select = $args[0];
1102 } else {
1103 $select = null;
1104 }
1105
1106 /**
1107 * Recognize methods for Has-Many cases:
1108 * findParent<Class>()
1109 * findParent<Class>By<Rule>()
1110 * Use the non-greedy pattern repeat modifier e.g. \w+?
1111 */
1112 if (preg_match('/^findParent(\w+?)(?:By(\w+))?$/', $method, $matches)) {
1113 $class = $matches[1];
1114 $ruleKey1 = isset($matches[2]) ? $matches[2] : null;
1115 return $this->findParentRow($class, $ruleKey1, $select);
1116 }
1117
1118 /**
1119 * Recognize methods for Many-to-Many cases:
1120 * find<Class1>Via<Class2>()
1121 * find<Class1>Via<Class2>By<Rule>()
1122 * find<Class1>Via<Class2>By<Rule1>And<Rule2>()
1123 * Use the non-greedy pattern repeat modifier e.g. \w+?
1124 */
1125 if (preg_match('/^find(\w+?)Via(\w+?)(?:By(\w+?)(?:And(\w+))?)?$/', $method, $matches)) {
1126 $class = $matches[1];
1127 $viaClass = $matches[2];
1128 $ruleKey1 = isset($matches[3]) ? $matches[3] : null;
1129 $ruleKey2 = isset($matches[4]) ? $matches[4] : null;
1130 return $this->findManyToManyRowset($class, $viaClass, $ruleKey1, $ruleKey2, $select);
1131 }
1132
1133 /**
1134 * Recognize methods for Belongs-To cases:
1135 * find<Class>()
1136 * find<Class>By<Rule>()
1137 * Use the non-greedy pattern repeat modifier e.g. \w+?
1138 */
1139 if (preg_match('/^find(\w+?)(?:By(\w+))?$/', $method, $matches)) {
1140 $class = $matches[1];
1141 $ruleKey1 = isset($matches[2]) ? $matches[2] : null;
1142 return $this->findDependentRowset($class, $ruleKey1, $select);
1143 }
1144
1145 throw new Zend_Db_Table_Row_Exception("Unrecognized method '$method()'");
1146 }
1147
1148
1149 /**
1150 * _getTableFromString
1151 *
1152 * @param string $tableName
1153 * @return Zend_Db_Table_Abstract
1154 */
1155 protected function _getTableFromString($tableName)
1156 {
1157 return Zend_Db_Table_Abstract::getTableFromString($tableName, $this->_table);
1158 }
1159
1160 }