Source for file Record.php

Documentation is available at Record.php

  1. <?php
  2. /*
  3.  *  $Id: Record.php 2294 2007-08-29 22:20:30Z zYne $
  4.  *
  5.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16.  *
  17.  * This software consists of voluntary contributions made by many individuals
  18.  * and is licensed under the LGPL. For more information, see
  19.  * <http://www.phpdoctrine.com>.
  20.  */
  21. Doctrine::autoload('Doctrine_Record_Abstract');
  22. /**
  23.  * Doctrine_Record
  24.  * All record classes should inherit this super class
  25.  *
  26.  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
  27.  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
  28.  * @package     Doctrine
  29.  * @category    Object Relational Mapping
  30.  * @link        www.phpdoctrine.com
  31.  * @since       1.0
  32.  * @version     $Revision: 2294 $
  33.  */
  34. abstract class Doctrine_Record extends Doctrine_Record_Abstract implements CountableIteratorAggregateSerializable
  35. {
  36.     /**
  37.      * STATE CONSTANTS
  38.      */
  39.  
  40.     /**
  41.      * DIRTY STATE
  42.      * a Doctrine_Record is in dirty state when its properties are changed
  43.      */
  44.     const STATE_DIRTY       = 1;
  45.     /**
  46.      * TDIRTY STATE
  47.      * a Doctrine_Record is in transient dirty state when it is created
  48.      * and some of its fields are modified but it is NOT yet persisted into database
  49.      */
  50.     const STATE_TDIRTY      = 2;
  51.     /**
  52.      * CLEAN STATE
  53.      * a Doctrine_Record is in clean state when all of its properties are loaded from the database
  54.      * and none of its properties are changed
  55.      */
  56.     const STATE_CLEAN       = 3;
  57.     /**
  58.      * PROXY STATE
  59.      * a Doctrine_Record is in proxy state when its properties are not fully loaded
  60.      */
  61.     const STATE_PROXY       = 4;
  62.     /**
  63.      * NEW TCLEAN
  64.      * a Doctrine_Record is in transient clean state when it is created and none of its fields are modified
  65.      */
  66.     const STATE_TCLEAN      = 5;
  67.     /**
  68.      * LOCKED STATE
  69.      * a Doctrine_Record is temporarily locked during deletes and saves
  70.      *
  71.      * This state is used internally to ensure that circular deletes
  72.      * and saves will not cause infinite loops
  73.      */
  74.     const STATE_LOCKED     = 6;
  75.  
  76.     /**
  77.      * @var Doctrine_Node_<TreeImpl>        node object
  78.      */
  79.     protected $_node;
  80.     /**
  81.      * @var integer $_id                    the primary keys of this object
  82.      */
  83.     protected $_id           = array();
  84.     /**
  85.      * @var array $_data                    the record data
  86.      */
  87.     protected $_data         = array();
  88.     /**
  89.      * @var array $_values                  the values array, aggregate values and such are mapped into this array
  90.      */
  91.     protected $_values       = array();
  92.     /**
  93.      * @var integer $_state                 the state of this record
  94.      * @see STATE_* constants
  95.      */
  96.     protected $_state;
  97.     /**
  98.      * @var array $_modified                an array containing properties that have been modified
  99.      */
  100.     protected $_modified     = array();
  101.     /**
  102.      * @var Doctrine_Validator_ErrorStack   error stack object
  103.      */
  104.     protected $_errorStack;
  105.     /**
  106.      * @var Doctrine_Record_Filter          the filter object
  107.      */
  108.     protected $_filter;
  109.     /**
  110.      * @var array $_references              an array containing all the references
  111.      */
  112.     protected $_references     = array();
  113.     /**
  114.      * @var integer $index                  this index is used for creating object identifiers
  115.      */
  116.     private static $_index 1;
  117.     /**
  118.      * @var integer $oid                    object identifier, each Record object has a unique object identifier
  119.      */
  120.     private $_oid;
  121.  
  122.     /**
  123.      * constructor
  124.      * @param Doctrine_Table|null$table       a Doctrine_Table object or null,
  125.      *                                          if null the table object is retrieved from current connection
  126.      *
  127.      * @param boolean $isNewEntry              whether or not this record is transient
  128.      *
  129.      * @throws Doctrine_Connection_Exception   if object is created using the new operator and there are no
  130.      *                                          open connections
  131.      * @throws Doctrine_Record_Exception       if the cleanData operation fails somehow
  132.      */
  133.     public function __construct($table null$isNewEntry false)
  134.     {
  135.         if (isset($table&& $table instanceof Doctrine_Table{
  136.             $this->_table = $table;
  137.             $exists $isNewEntry);
  138.         else {
  139.             $class  get_class($this);
  140.             // get the table of this class
  141.             $this->_table = Doctrine_Manager::getInstance()
  142.                             ->getTable(get_class($this));
  143.             $exists false;
  144.         }
  145.  
  146.         // initialize the filter object
  147.         $this->_filter = new Doctrine_Record_Filter($this);
  148.  
  149.         // Check if the current connection has the records table in its registry
  150.         // If not this record is only used for creating table definition and setting up
  151.         // relations.
  152.  
  153.         if ($this->_table->getConnection()->hasTable($this->_table->getComponentName())) {
  154.             $this->_oid = self::$_index;
  155.  
  156.             self::$_index++;
  157.  
  158.             $keys $this->_table->getPrimaryKeys();
  159.  
  160.             // get the data array
  161.             $this->_data = $this->_table->getData();
  162.  
  163.             // get the column count
  164.             $count count($this->_data);
  165.  
  166.             $this->_values = $this->cleanData($this->_data);
  167.  
  168.             $this->prepareIdentifiers($exists);
  169.  
  170.             if $exists{
  171.                 if ($count 0{
  172.                     $this->_state = Doctrine_Record::STATE_TDIRTY;
  173.                 else {
  174.                     $this->_state = Doctrine_Record::STATE_TCLEAN;
  175.                 }
  176.  
  177.                 // set the default values for this record
  178.                 $this->assignDefaultValues();
  179.             else {
  180.                 $this->_state      = Doctrine_Record::STATE_CLEAN;
  181.  
  182.                 if ($count $this->_table->getColumnCount()) {
  183.                     $this->_state  = Doctrine_Record::STATE_PROXY;
  184.                 }
  185.             }
  186.  
  187.             $this->_errorStack = new Doctrine_Validator_ErrorStack();
  188.  
  189.             $repository $this->_table->getRepository();
  190.             $repository->add($this);
  191.             
  192.             $this->construct();
  193.         }
  194.         
  195.     }
  196.     /**
  197.      * _index
  198.      *
  199.      * @return integer 
  200.      */
  201.     public static function _index()
  202.     {
  203.         return self::$_index;
  204.     }
  205.     /**
  206.      * setUp
  207.      * this method is used for setting up relations and attributes
  208.      * it should be implemented by child classes
  209.      *
  210.      * @return void 
  211.      */
  212.     public function setUp()
  213.     }
  214.     /**
  215.      * construct
  216.      * Empty tempalte method to provide concrete Record classes with the possibility
  217.      * to hook into the constructor procedure
  218.      *
  219.      * @return void 
  220.      */
  221.     public function construct()
  222.     }
  223.     /**
  224.      * getOid
  225.      * returns the object identifier
  226.      *
  227.      * @return integer 
  228.      */
  229.     public function getOid()
  230.     {
  231.         return $this->_oid;
  232.     }
  233.     /**
  234.      * isValid
  235.      *
  236.      * @return boolean                          whether or not this record passes all column validations
  237.      */
  238.     public function isValid()
  239.     {
  240.         if $this->_table->getAttribute(Doctrine::ATTR_VLD)) {
  241.             return true;
  242.         }
  243.         // Clear the stack from any previous errors.
  244.         $this->_errorStack->clear();
  245.  
  246.         // Run validation process
  247.         $validator new Doctrine_Validator();
  248.         $validator->validateRecord($this);
  249.         $this->validate();
  250.         if ($this->_state == self::STATE_TDIRTY || $this->_state == self::STATE_TCLEAN{
  251.             $this->validateOnInsert();
  252.         else {
  253.             $this->validateOnUpdate();
  254.         }
  255.  
  256.         return $this->_errorStack->count(== true false;
  257.     }
  258.     /**
  259.      * Empty template method to provide concrete Record classes with the possibility
  260.      * to hook into the validation procedure, doing any custom / specialized
  261.      * validations that are neccessary.
  262.      */
  263.     protected function validate()
  264.     }
  265.     /**
  266.      * Empty template method to provide concrete Record classes with the possibility
  267.      * to hook into the validation procedure only when the record is going to be
  268.      * updated.
  269.      */
  270.     protected function validateOnUpdate()
  271.     }
  272.     /**
  273.      * Empty template method to provide concrete Record classes with the possibility
  274.      * to hook into the validation procedure only when the record is going to be
  275.      * inserted into the data store the first time.
  276.      */
  277.     protected function validateOnInsert()
  278.     }
  279.     /**
  280.      * Empty template method to provide concrete Record classes with the possibility
  281.      * to hook into the serializing procedure.
  282.      */
  283.     public function preSerialize($event)
  284.     }
  285.     /**
  286.      * Empty template method to provide concrete Record classes with the possibility
  287.      * to hook into the serializing procedure.
  288.      */
  289.     public function postSerialize($event)
  290.     }
  291.     /**
  292.      * Empty template method to provide concrete Record classes with the possibility
  293.      * to hook into the serializing procedure.
  294.      */
  295.     public function preUnserialize($event)
  296.     }
  297.     /**
  298.      * Empty template method to provide concrete Record classes with the possibility
  299.      * to hook into the serializing procedure.
  300.      */
  301.     public function postUnserialize($event)
  302.     }
  303.     /**
  304.      * Empty template method to provide concrete Record classes with the possibility
  305.      * to hook into the saving procedure.
  306.      */
  307.     public function preSave($event)
  308.     }
  309.     /**
  310.      * Empty template method to provide concrete Record classes with the possibility
  311.      * to hook into the saving procedure.
  312.      */
  313.     public function postSave($event)
  314.     }
  315.     /**
  316.      * Empty template method to provide concrete Record classes with the possibility
  317.      * to hook into the deletion procedure.
  318.      */
  319.     public function preDelete($event)
  320.     }
  321.     /**
  322.      * Empty template method to provide concrete Record classes with the possibility
  323.      * to hook into the deletion procedure.
  324.      */
  325.     public function postDelete($event)
  326.     }
  327.     /**
  328.      * Empty template method to provide concrete Record classes with the possibility
  329.      * to hook into the saving procedure only when the record is going to be
  330.      * updated.
  331.      */
  332.     public function preUpdate($event)
  333.     }
  334.     /**
  335.      * Empty template method to provide concrete Record classes with the possibility
  336.      * to hook into the saving procedure only when the record is going to be
  337.      * updated.
  338.      */
  339.     public function postUpdate($event)
  340.     }
  341.     /**
  342.      * Empty template method to provide concrete Record classes with the possibility
  343.      * to hook into the saving procedure only when the record is going to be
  344.      * inserted into the data store the first time.
  345.      */
  346.     public function preInsert($event)
  347.     }
  348.     /**
  349.      * Empty template method to provide concrete Record classes with the possibility
  350.      * to hook into the saving procedure only when the record is going to be
  351.      * inserted into the data store the first time.
  352.      */
  353.     public function postInsert($event)
  354.     }
  355.     /**
  356.      * getErrorStack
  357.      *
  358.      * @return Doctrine_Validator_ErrorStack    returns the errorStack associated with this record
  359.      */
  360.     public function getErrorStack()
  361.     {
  362.         return $this->_errorStack;
  363.     }
  364.     /**
  365.      * errorStack
  366.      * assigns / returns record errorStack
  367.      *
  368.      * @param Doctrine_Validator_ErrorStack          errorStack to be assigned for this record
  369.      * @return void|Doctrine_Validator_ErrorStack   returns the errorStack associated with this record
  370.      */
  371.     public function errorStack($stack null)
  372.     {
  373.         if($stack !== null{
  374.             if($stack instanceof Doctrine_Validator_ErrorStack)) {
  375.                throw new Doctrine_Record_Exception('Argument should be an instance of Doctrine_Validator_ErrorStack.');
  376.             }
  377.             $this->_errorStack = $stack;
  378.         else {
  379.             return $this->_errorStack;
  380.         }
  381.     }
  382.     /**
  383.      * setDefaultValues
  384.      * sets the default values for records internal data
  385.      *
  386.      * @param boolean $overwrite                whether or not to overwrite the already set values
  387.      * @return boolean 
  388.      */
  389.     public function assignDefaultValues($overwrite false)
  390.     {
  391.         if $this->_table->hasDefaultValues()) {
  392.             return false;
  393.         }
  394.         foreach ($this->_data as $column => $value{
  395.             $default $this->_table->getDefaultValueOf($column);
  396.  
  397.             if ($default === null{
  398.                 $default self::$_null;
  399.             }
  400.  
  401.             if ($value === self::$_null || $overwrite{
  402.                 $this->_data[$column$default;
  403.                 $this->_modified[]    $column;
  404.                 $this->_state = Doctrine_Record::STATE_TDIRTY;
  405.             }
  406.         }
  407.     }
  408.     /**
  409.      * cleanData
  410.      *
  411.      * @param array $data       data array to be cleaned
  412.      * @return integer 
  413.      */
  414.     public function cleanData(&$data)
  415.     {
  416.         $tmp $data;
  417.         $data array();
  418.  
  419.         foreach ($this->getTable()->getColumnNames(as $name{
  420.             if isset($tmp[$name])) {
  421.                 $data[$nameself::$_null;
  422.             else {
  423.                 $data[$name$tmp[$name];
  424.             }
  425.             unset($tmp[$name]);
  426.         }
  427.  
  428.         return $tmp;
  429.     }
  430.     /**
  431.      * hydrate
  432.      * hydrates this object from given array
  433.      *
  434.      * @param array $data 
  435.      * @return boolean 
  436.      */
  437.     public function hydrate(array $data)
  438.     {
  439.         $this->_values $this->cleanData($data);
  440.         $this->_data   = array_merge($this->_data$data);
  441.  
  442.         $this->prepareIdentifiers(true);
  443.     }
  444.     /**
  445.      * prepareIdentifiers
  446.      * prepares identifiers for later use
  447.      *
  448.      * @param boolean $exists               whether or not this record exists in persistent data store
  449.      * @return void 
  450.      */
  451.     private function prepareIdentifiers($exists true)
  452.     {
  453.         switch ($this->_table->getIdentifierType()) {
  454.             case Doctrine::IDENTIFIER_AUTOINC:
  455.             case Doctrine::IDENTIFIER_SEQUENCE:
  456.             case Doctrine::IDENTIFIER_NATURAL:
  457.                 $name $this->_table->getIdentifier();
  458.  
  459.                 if ($exists{
  460.                     if (isset($this->_data[$name]&& $this->_data[$name!== self::$_null{
  461.                         $this->_id[$name$this->_data[$name];
  462.                     }
  463.                 }
  464.                 break;
  465.             case Doctrine::IDENTIFIER_COMPOSITE:
  466.                 $names $this->_table->getIdentifier();
  467.  
  468.                 foreach ($names as $name{
  469.                     if ($this->_data[$name=== self::$_null{
  470.                         $this->_id[$namenull;
  471.                     else {
  472.                         $this->_id[$name$this->_data[$name];
  473.                     }
  474.                 }
  475.                 break;
  476.         }
  477.     }
  478.     /**
  479.      * serialize
  480.      * this method is automatically called when this Doctrine_Record is serialized
  481.      *
  482.      * @return array 
  483.      */
  484.     public function serialize()
  485.     {
  486.         $event new Doctrine_Event($thisDoctrine_Event::RECORD_SERIALIZE);
  487.  
  488.         $this->preSerialize($event);
  489.  
  490.         $vars get_object_vars($this);
  491.  
  492.         unset($vars['_references']);
  493.         unset($vars['_table']);
  494.         unset($vars['_errorStack']);
  495.         unset($vars['_filter']);
  496.         unset($vars['_modified']);
  497.         unset($vars['_node']);
  498.  
  499.         $name $this->_table->getIdentifier();
  500.         $this->_data array_merge($this->_data$this->_id);
  501.  
  502.         foreach ($this->_data as $k => $v{
  503.             if ($v instanceof Doctrine_Record && $this->_table->getTypeOf($k!= 'object'{
  504.                 unset($vars['_data'][$k]);
  505.             elseif ($v === self::$_null{
  506.                 unset($vars['_data'][$k]);
  507.             else {
  508.                 switch ($this->_table->getTypeOf($k)) {
  509.                     case 'array':
  510.                     case 'object':
  511.                         $vars['_data'][$kserialize($vars['_data'][$k]);
  512.                         break;
  513.                     case 'gzip':
  514.                         $vars['_data'][$kgzcompress($vars['_data'][$k]);
  515.                         break;
  516.                     case 'enum':
  517.                         $vars['_data'][$k$this->_table->enumIndex($k$vars['_data'][$k]);
  518.                         break;
  519.                 }
  520.             }
  521.         }
  522.  
  523.         $str serialize($vars);
  524.         
  525.         $this->postSerialize($event);
  526.  
  527.         return $str;
  528.     }
  529.     /**
  530.      * unseralize
  531.      * this method is automatically called everytime a Doctrine_Record object is unserialized
  532.      *
  533.      * @param string $serialized                Doctrine_Record as serialized string
  534.      * @throws Doctrine_Record_Exception        if the cleanData operation fails somehow
  535.      * @return void 
  536.      */
  537.     public function unserialize($serialized)
  538.     {
  539.         $event new Doctrine_Event($thisDoctrine_Event::RECORD_UNSERIALIZE);
  540.  
  541.         $this->preUnserialize($event);
  542.  
  543.         $manager    Doctrine_Manager::getInstance();
  544.         $connection $manager->getConnectionForComponent(get_class($this));
  545.  
  546.         $this->_oid self::$_index;
  547.         self::$_index++;
  548.  
  549.         $this->_table $connection->getTable(get_class($this));
  550.  
  551.         $array unserialize($serialized);
  552.  
  553.         foreach($array as $k => $v{
  554.             $this->$k $v;
  555.         }
  556.  
  557.         foreach ($this->_data as $k => $v{
  558.  
  559.             switch ($this->_table->getTypeOf($k)) {
  560.                 case 'array':
  561.                 case 'object':
  562.                     $this->_data[$kunserialize($this->_data[$k]);
  563.                     break;
  564.                 case 'gzip':
  565.                    $this->_data[$kgzuncompress($this->_data[$k]);
  566.                     break;
  567.                 case 'enum':
  568.                     $this->_data[$k$this->_table->enumValue($k$this->_data[$k]);
  569.                     break;
  570.                 
  571.             }
  572.         }
  573.         
  574.         $this->_table->getRepository()->add($this);
  575.         $this->_filter new Doctrine_Record_Filter($this);
  576.  
  577.         $this->cleanData($this->_data);
  578.  
  579.         $this->prepareIdentifiers($this->exists());
  580.         
  581.         $this->postUnserialize($event);
  582.     }
  583.     /**
  584.      * state
  585.      * returns / assigns the state of this record
  586.      *
  587.      * @param integer|string$state                 if set, this method tries to set the record state to $state
  588.      * @see Doctrine_Record::STATE_* constants
  589.      *
  590.      * @throws Doctrine_Record_State_Exception      if trying to set an unknown state
  591.      * @return null|integer
  592.      */
  593.     public function state($state null)
  594.     {
  595.         if ($state == null{
  596.             return $this->_state;
  597.         }
  598.         $err false;
  599.         if (is_integer($state)) {
  600.             if ($state >= && $state <= 6{
  601.                 $this->_state $state;
  602.             else {
  603.                 $err true;
  604.             }
  605.         elseif (is_string($state)) {
  606.             $upper strtoupper($state);
  607.             
  608.             $const 'Doctrine_Record::STATE_' $upper;
  609.             if (defined($const)) {
  610.                 $this->_state constant($const);  
  611.             else {
  612.                 $err true;
  613.             }
  614.         }
  615.  
  616.         if ($this->_state === Doctrine_Record::STATE_TCLEAN ||
  617.             $this->_state === Doctrine_Record::STATE_CLEAN{
  618.  
  619.             $this->_modified array();
  620.         }
  621.  
  622.         if ($err{
  623.             throw new Doctrine_Record_State_Exception('Unknown record state ' $state);
  624.         }
  625.     }
  626.     /**
  627.      * refresh
  628.      * refresh internal data from the database
  629.      *
  630.      * @throws Doctrine_Record_Exception        When the refresh operation fails (when the database row
  631.      *                                           this record represents does not exist anymore)
  632.      * @return boolean 
  633.      */
  634.     public function refresh()
  635.     {
  636.         $id $this->identifier();
  637.         if is_array($id)) {
  638.             $id array($id);
  639.         }
  640.         if (empty($id)) {
  641.             return false;
  642.         }
  643.         $id array_values($id);
  644.  
  645.         $records Doctrine_Query::create()
  646.                    ->from($this->_table->getComponentName())
  647.                    ->where(implode(' = ? AND '$this->_table->getPrimaryKeys()) ' = ?')
  648.                    ->execute($id);
  649.  
  650.  
  651.         if (count($records=== 0{
  652.             throw new Doctrine_Record_Exception('Failed to refresh. Record does not exist.');
  653.         }
  654.  
  655.         $this->_modified array();
  656.  
  657.         $this->prepareIdentifiers();
  658.  
  659.         $this->_state    Doctrine_Record::STATE_CLEAN;
  660.  
  661.         return $this;
  662.     }
  663.     
  664.     /**
  665.      * refresh
  666.      * refres data of related objects from the database
  667.      *
  668.      * @param string $name              name of a related component.
  669.      *                                   if set, this method only refreshes the specified related component
  670.      *
  671.      * @return Doctrine_Record          this object
  672.      */
  673.     public function refreshRelated($name null)
  674.     {
  675.         if (is_null($name)) {
  676.             foreach ($this->_table->getRelations(as $rel{
  677.                 $this->_references[$rel->getAlias()$rel->fetchRelatedFor($this);
  678.             }
  679.         else {
  680.             $rel $this->_table->getRelation($name);
  681.             $this->_references[$name$rel->fetchRelatedFor($this);
  682.         }
  683.     }
  684.     
  685.     /**
  686.      * getTable
  687.      * returns the table object for this record
  688.      *
  689.      * @return object Doctrine_Table        a Doctrine_Table object
  690.      */
  691.     public function getTable()
  692.     {
  693.         return $this->_table;
  694.     }
  695.     /**
  696.      * getData
  697.      * return all the internal data
  698.      *
  699.      * @return array                        an array containing all the properties
  700.      */
  701.     public function getData()
  702.     {
  703.         return $this->_data;
  704.     }
  705.     /**
  706.      * rawGet
  707.      * returns the value of a property, if the property is not yet loaded
  708.      * this method does NOT load it
  709.      *
  710.      * @param $name                         name of the property
  711.      * @throws Doctrine_Record_Exception    if trying to get an unknown property
  712.      * @return mixed 
  713.      */
  714.  
  715.     public function rawGet($name)
  716.     {
  717.         if isset($this->_data[$name])) {
  718.             throw new Doctrine_Record_Exception('Unknown property '$name);
  719.         }
  720.         if ($this->_data[$name=== self::$_null)
  721.             return null;
  722.  
  723.         return $this->_data[$name];
  724.     }
  725.  
  726.     /**
  727.      * load
  728.      * loads all the unitialized properties from the database
  729.      *
  730.      * @return boolean 
  731.      */
  732.     public function load()
  733.     {
  734.         // only load the data from database if the Doctrine_Record is in proxy state
  735.         if ($this->_state == Doctrine_Record::STATE_PROXY{
  736.             $this->refresh();
  737.  
  738.             $this->_state Doctrine_Record::STATE_CLEAN;
  739.  
  740.             return true;
  741.         }
  742.         return false;
  743.     }
  744.     /**
  745.      * get
  746.      * returns a value of a property or a related component
  747.      *
  748.      * @param mixed $name                       name of the property or related component
  749.      * @param boolean $load                     whether or not to invoke the loading procedure
  750.      * @throws Doctrine_Record_Exception        if trying to get a value of unknown property / related component
  751.      * @return mixed 
  752.      */
  753.     public function get($name$load true)
  754.     {
  755.         $value self::$_null;
  756.         $lower strtolower($name);
  757.  
  758.         $lower $this->_table->getColumnName($lower);
  759.  
  760.         if (isset($this->_data[$lower])) {
  761.             // check if the property is null (= it is the Doctrine_Null object located in self::$_null)
  762.             if ($this->_data[$lower=== self::$_null && $load{
  763.                 $this->load();
  764.             }
  765.  
  766.             if ($this->_data[$lower=== self::$_null{
  767.                 $value null;
  768.             else {
  769.                 $value $this->_data[$lower];
  770.             }
  771.             return $value;
  772.         }
  773.  
  774.         if (isset($this->_id[$lower])) {
  775.             return $this->_id[$lower];
  776.         }
  777.         if ($name === $this->_table->getIdentifier()) {
  778.             return null;
  779.         }
  780.         if (isset($this->_values[$lower])) {
  781.             return $this->_values[$lower];
  782.         }
  783.  
  784.         try {
  785.  
  786.             if isset($this->_references[$name]&& $load{
  787.  
  788.                 $rel $this->_table->getRelation($name);
  789.  
  790.                 $this->_references[$name$rel->fetchRelatedFor($this);
  791.             }
  792.  
  793.         catch(Doctrine_Table_Exception $e
  794.             throw new Doctrine_Record_Exception("Unknown property / related component '$name'.");
  795.         }
  796.  
  797.         return $this->_references[$name];
  798.     }
  799.     /**
  800.      * mapValue
  801.      * This simple method is used for mapping values to $values property.
  802.      * Usually this method is used internally by Doctrine for the mapping of
  803.      * aggregate values.
  804.      *
  805.      * @param string $name                  the name of the mapped value
  806.      * @param mixed $value                  mixed value to be mapped
  807.      * @return void 
  808.      */
  809.     public function mapValue($name$value)
  810.     {
  811.         $name strtolower($name);
  812.         $this->_values[$name$value;
  813.     }
  814.     /**
  815.      * set
  816.      * method for altering properties and Doctrine_Record references
  817.      * if the load parameter is set to false this method will not try to load uninitialized record data
  818.      *
  819.      * @param mixed $name                   name of the property or reference
  820.      * @param mixed $value                  value of the property or reference
  821.      * @param boolean $load                 whether or not to refresh / load the uninitialized record data
  822.      *
  823.      * @throws Doctrine_Record_Exception    if trying to set a value for unknown property / related component
  824.      * @throws Doctrine_Record_Exception    if trying to set a value of wrong type for related component
  825.      *
  826.      * @return Doctrine_Record 
  827.      */
  828.     public function set($name$value$load true)
  829.     {
  830.         $lower strtolower($name);
  831.  
  832.         $lower $this->_table->getColumnName($lower);
  833.  
  834.         if (isset($this->_data[$lower])) {
  835.             if ($value instanceof Doctrine_Record{
  836.                 $type $this->_table->getTypeOf($name);
  837.  
  838.                 $id $value->getIncremented();
  839.  
  840.                 if ($id !== null && $type !== 'object'{
  841.                     $value $id;
  842.                 }
  843.             }
  844.  
  845.             if ($load{
  846.                 $old $this->get($lower$load);
  847.             else {
  848.                 $old $this->_data[$lower];
  849.             }
  850.  
  851.             if ($old !== $value{
  852.                 if ($value === null{
  853.                     $value self::$_null;
  854.                 }
  855.  
  856.                 $this->_data[$lower$value;
  857.                 $this->_modified[]   $lower;
  858.                 switch ($this->_state{
  859.                     case Doctrine_Record::STATE_CLEAN:
  860.                         $this->_state Doctrine_Record::STATE_DIRTY;
  861.                         break;
  862.                     case Doctrine_Record::STATE_TCLEAN:
  863.                         $this->_state Doctrine_Record::STATE_TDIRTY;
  864.                         break;
  865.                 }
  866.             }
  867.         else {
  868.             try {
  869.                 $this->coreSetRelated($name$value);
  870.             catch(Doctrine_Table_Exception $e{
  871.                 throw new Doctrine_Record_Exception("Unknown property / related component '$name'.");
  872.             }
  873.         }
  874.     }
  875.  
  876.     public function coreSetRelated($name$value)
  877.     {
  878.         $rel $this->_table->getRelation($name);
  879.  
  880.         // one-to-many or one-to-one relation
  881.         if ($rel instanceof Doctrine_Relation_ForeignKey ||
  882.             $rel instanceof Doctrine_Relation_LocalKey{
  883.             if $rel->isOneToOne()) {
  884.                 // one-to-many relation found
  885.                 if ($value instanceof Doctrine_Collection)) {
  886.                     throw new Doctrine_Record_Exception("Couldn't call Doctrine::set(), second argument should be an instance of Doctrine_Collection when setting one-to-many references.");
  887.                 }
  888.                 if (isset($this->_references[$name])) {
  889.                     $this->_references[$name]->setData($value->getData());
  890.                     return $this;
  891.                 }
  892.             else {
  893.                 if ($value !== self::$_null{
  894.                     // one-to-one relation found
  895.                     if ($value instanceof Doctrine_Record)) {
  896.                         throw new Doctrine_Record_Exception("Couldn't call Doctrine::set(), second argument should be an instance of Doctrine_Record or Doctrine_Null when setting one-to-one references.");
  897.                     }
  898.                     if ($rel instanceof Doctrine_Relation_LocalKey{
  899.                         $foreign $rel->getForeign();
  900.                         if (!empty($foreign&& $foreign != $value->getTable()->getIdentifier())
  901.                           $this->set($rel->getLocal()$value->rawGet($foreign)false);
  902.                         else
  903.                           $this->set($rel->getLocal()$valuefalse);                          
  904.                     else {
  905.                         $value->set($rel->getForeign()$thisfalse);
  906.                     }                            
  907.                 }
  908.             }
  909.  
  910.         elseif ($rel instanceof Doctrine_Relation_Association{
  911.             // join table relation found
  912.             if ($value instanceof Doctrine_Collection)) {
  913.                 throw new Doctrine_Record_Exception("Couldn't call Doctrine::set(), second argument should be an instance of Doctrine_Collection when setting many-to-many references.");
  914.             }
  915.         }
  916.  
  917.         $this->_references[$name$value;
  918.     }
  919.     /**
  920.      * contains
  921.      *
  922.      * @param string $name 
  923.      * @return boolean 
  924.      */
  925.     public function contains($name)
  926.     {
  927.         $lower strtolower($name);
  928.  
  929.         if (isset($this->_data[$lower])) {
  930.             return true;
  931.         }
  932.         if (isset($this->_id[$lower])) {
  933.             return true;
  934.         }
  935.         if (isset($this->_values[$lower])) {
  936.             return true;                                      
  937.         }
  938.         if (isset($this->_references[$name]&& 
  939.             $this->_references[$name!== self::$_null{
  940.  
  941.             return true;
  942.         }
  943.         return false;
  944.     }
  945.     /**
  946.      * @param string $name 
  947.      * @return void 
  948.      */
  949.     public function __unset($name)
  950.     {
  951.         if (isset($this->_data[$name])) {
  952.             $this->_data[$namearray();
  953.         }
  954.         // todo: what to do with references ?
  955.     }
  956.     /**
  957.      * applies the changes made to this object into database
  958.      * this method is smart enough to know if any changes are made
  959.      * and whether to use INSERT or UPDATE statement
  960.      *
  961.      * this method also saves the related components
  962.      *
  963.      * @param Doctrine_Connection $conn                 optional connection parameter
  964.      * @return void 
  965.      */
  966.     public function save(Doctrine_Connection $conn null)
  967.     {
  968.         if ($conn === null{
  969.             $conn $this->_table->getConnection();
  970.         }
  971.         $conn->unitOfWork->saveGraph($this);
  972.     }
  973.     /**
  974.      * Tries to save the object and all its related components.
  975.      * In contrast to Doctrine_Record::save(), this method does not
  976.      * throw an exception when validation fails but returns TRUE on
  977.      * success or FALSE on failure.
  978.      * 
  979.      * @param Doctrine_Connection $conn                 optional connection parameter
  980.      * @return TRUE if the record was saved sucessfully without errors, FALSE otherwise.
  981.      */
  982.     public function trySave(Doctrine_Connection $conn null{
  983.         try {
  984.             $this->save($conn);
  985.             return true;
  986.         catch (Doctrine_Validator_Exception $ignored{
  987.             return false;
  988.         }
  989.     }
  990.     /**
  991.      * replace
  992.      * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
  993.      * query, except that if there is already a row in the table with the same
  994.      * key field values, the REPLACE query just updates its values instead of
  995.      * inserting a new row.
  996.      *
  997.      * The REPLACE type of query does not make part of the SQL standards. Since
  998.      * practically only MySQL and SQLIte implement it natively, this type of
  999.      * query isemulated through this method for other DBMS using standard types
  1000.      * of queries inside a transaction to assure the atomicity of the operation.
  1001.      *
  1002.      * @param Doctrine_Connection $conn             optional connection parameter
  1003.      * @throws Doctrine_Connection_Exception        if some of the key values was null
  1004.      * @throws Doctrine_Connection_Exception        if there were no key fields
  1005.      * @throws PDOException                         if something fails at PDO level
  1006.      * @return integer                              number of rows affected
  1007.      */
  1008.     public function replace(Doctrine_Connection $conn null)
  1009.     {
  1010.         if ($conn === null{
  1011.             $conn $this->_table->getConnection();
  1012.         }
  1013.  
  1014.         return $conn->replace($this->_table->getTableName()$this->getPrepared()$this->id);
  1015.     }
  1016.     /**
  1017.      * returns an array of modified fields and associated values
  1018.      * @return array 
  1019.      */
  1020.     public function getModified()
  1021.     {
  1022.         $a array();
  1023.  
  1024.         foreach ($this->_modified as $k => $v{
  1025.             $a[$v$this->_data[$v];
  1026.         }
  1027.         return $a;
  1028.     }
  1029.     /**
  1030.      * getPrepared
  1031.      *
  1032.      * returns an array of modified fields and values with data preparation
  1033.      * adds column aggregation inheritance and converts Records into primary key values
  1034.      *
  1035.      * @param array $array 
  1036.      * @return array 
  1037.      */
  1038.     public function getPrepared(array $array array()) 
  1039.     {
  1040.         $a array();
  1041.  
  1042.         if (empty($array)) {
  1043.             $array $this->_modified;
  1044.         }
  1045.  
  1046.         foreach ($array as $k => $v{
  1047.             $type $this->_table->getTypeOf($v);
  1048.  
  1049.             if ($this->_data[$v=== self::$_null{
  1050.                 $a[$vnull;
  1051.                 continue;
  1052.             }
  1053.  
  1054.             switch ($type{
  1055.                 case 'array':
  1056.                 case 'object':
  1057.                     $a[$vserialize($this->_data[$v]);
  1058.                     break;
  1059.                 case 'gzip':
  1060.                     $a[$vgzcompress($this->_data[$v],5);
  1061.                     break;
  1062.                 case 'boolean':
  1063.                     $a[$v$this->getTable()->getConnection()->convertBooleans($this->_data[$v]);
  1064.                 break;
  1065.                 case 'enum':
  1066.                     $a[$v$this->_table->enumIndex($v$this->_data[$v]);
  1067.                     break;
  1068.                 default:
  1069.                     if ($this->_data[$vinstanceof Doctrine_Record{
  1070.                         $this->_data[$v$this->_data[$v]->getIncremented();
  1071.                     }
  1072.                     /** TODO:
  1073.                     if ($this->_data[$v] === null) {
  1074.                         throw new Doctrine_Record_Exception('Unexpected null value.');
  1075.                     }
  1076.                     */
  1077.  
  1078.                     $a[$v$this->_data[$v];
  1079.             }
  1080.         }
  1081.         $map $this->_table->inheritanceMap;
  1082.         foreach ($map as $k => $v{
  1083.             $old $this->get($kfalse);
  1084.  
  1085.             if ((string) $old !== (string) $v || $old === null{
  1086.                 $a[$k$v;
  1087.                 $this->_data[$k$v;
  1088.             }
  1089.         }
  1090.  
  1091.         return $a;
  1092.     }
  1093.     /**
  1094.      * count
  1095.      * this class implements countable interface
  1096.      *
  1097.      * @return integer          the number of columns in this record
  1098.      */
  1099.     public function count()
  1100.     {
  1101.         return count($this->_data);
  1102.     }
  1103.     /**
  1104.      * alias for count()
  1105.      *
  1106.      * @return integer          the number of columns in this record
  1107.      */
  1108.     public function columnCount()
  1109.     {
  1110.         return $this->count();
  1111.     }
  1112.     /**
  1113.      * toArray
  1114.      * returns the record as an array
  1115.      *
  1116.      * @param boolean $deep - Return also the relations
  1117.      * @return array 
  1118.      */
  1119.     public function toArray($deep false)
  1120.     {
  1121.         $a array();
  1122.  
  1123.         foreach ($this as $column => $value{
  1124.             if ($value === self::$_null{
  1125.                 $value null;
  1126.             }
  1127.             $a[$column$value;
  1128.         }
  1129.         if ($this->_table->getIdentifierType(==  Doctrine::IDENTIFIER_AUTOINC{
  1130.             $i      $this->_table->getIdentifier();
  1131.             $a[$i]  $this->getIncremented();
  1132.         }
  1133.         if ($deep{
  1134.             foreach ($this->_references as $key => $relation{
  1135.                 $a[$key$relation->toArray($deep);
  1136.             }
  1137.         }
  1138.         return array_merge($a$this->_values);
  1139.     }
  1140.     /**
  1141.      * exists
  1142.      * returns true if this record is persistent, otherwise false
  1143.      *
  1144.      * @return boolean 
  1145.      */
  1146.     public function exists()
  1147.     {
  1148.         return ($this->_state !== Doctrine_Record::STATE_TCLEAN &&
  1149.                 $this->_state !== Doctrine_Record::STATE_TDIRTY);
  1150.     }
  1151.     /**
  1152.      * isModified
  1153.      * returns true if this record was modified, otherwise false
  1154.      *
  1155.      * @return boolean 
  1156.      */
  1157.     public function isModified()
  1158.     {
  1159.         return ($this->_state === Doctrine_Record::STATE_DIRTY ||
  1160.                 $this->_state === Doctrine_Record::STATE_TDIRTY);
  1161.     }
  1162.     /**
  1163.      * method for checking existence of properties and Doctrine_Record references
  1164.      * @param mixed $name               name of the property or reference
  1165.      * @return boolean 
  1166.      */
  1167.     public function hasRelation($name)
  1168.     {
  1169.         if (isset($this->_data[$name]|| isset($this->_id[$name])) {
  1170.             return true;
  1171.         }
  1172.         return $this->_table->hasRelation($name);
  1173.     }
  1174.     /**
  1175.      * getIterator
  1176.      * @return Doctrine_Record_Iterator     a Doctrine_Record_Iterator that iterates through the data
  1177.      */
  1178.     public function getIterator()
  1179.     {
  1180.         return new Doctrine_Record_Iterator($this);
  1181.     }
  1182.     /**
  1183.      * deletes this data access object and all the related composites
  1184.      * this operation is isolated by a transaction
  1185.      *
  1186.      * this event can be listened by the onPreDelete and onDelete listeners
  1187.      *
  1188.      * @return boolean      true on success, false on failure
  1189.      */
  1190.     public function delete(Doctrine_Connection $conn null)
  1191.     {
  1192.         if ($conn == null{
  1193.             $conn $this->_table->getConnection();
  1194.         }
  1195.         return $conn->unitOfWork->delete($this);
  1196.     }
  1197.     /**
  1198.      * copy
  1199.      * returns a copy of this object
  1200.      *
  1201.      * @return Doctrine_Record 
  1202.      */
  1203.     public function copy()
  1204.     {
  1205.         $data $this->_data;
  1206.  
  1207.         if ($this->_table->getIdentifierType(=== Doctrine::IDENTIFIER_AUTOINC{
  1208.             $id $this->_table->getIdentifier();
  1209.  
  1210.             unset($data[$id]);
  1211.         }
  1212.  
  1213.         $ret $this->_table->create($data);
  1214.         $modified array();
  1215.  
  1216.         foreach ($data as $key => $val{
  1217.             if ($val instanceof Doctrine_Null)) {
  1218.                 $ret->_modified[$key;
  1219.             }
  1220.         }
  1221.         
  1222.  
  1223.         return $ret;
  1224.     }
  1225.     /**
  1226.      * copyDeep
  1227.      * returns a copy of this object and all its related objects
  1228.      *
  1229.      * @return Doctrine_Record 
  1230.      */
  1231.     public function copyDeep(){
  1232.         $copy $this->copy();
  1233.  
  1234.         foreach ($this->_references as $key => $value{
  1235.             if ($value instanceof Doctrine_Collection{
  1236.                 foreach ($value as $record{
  1237.                     $copy->{$key}[$record->copyDeep();
  1238.                 }
  1239.             else {
  1240.                 $copy->set($key$value->copyDeep());
  1241.             }
  1242.         }
  1243.         return $copy;
  1244.     }
  1245.     
  1246.     /**
  1247.      * assignIdentifier
  1248.      *
  1249.      * @param integer $id 
  1250.      * @return void 
  1251.      */
  1252.     public function assignIdentifier($id false)
  1253.     {
  1254.         if ($id === false{
  1255.             $this->_id       array();
  1256.             $this->_data     $this->_filter->cleanData($this->_data);
  1257.             $this->_state    Doctrine_Record::STATE_TCLEAN;
  1258.             $this->_modified array();
  1259.         elseif ($id === true{
  1260.             $this->prepareIdentifiers(true);
  1261.             $this->_state    Doctrine_Record::STATE_CLEAN;
  1262.             $this->_modified array();
  1263.         else {
  1264.             $name             $this->_table->getIdentifier();   
  1265.             $this->_id[$name$id;
  1266.             $this->_data[$name$id;
  1267.             $this->_state     Doctrine_Record::STATE_CLEAN;
  1268.             $this->_modified  array();
  1269.         }
  1270.     }
  1271.     /**
  1272.      * returns the primary keys of this object
  1273.      *
  1274.      * @return array 
  1275.      */
  1276.     public function identifier()
  1277.     {
  1278.         return $this->_id;
  1279.     }
  1280.     /**
  1281.      * returns the value of autoincremented primary key of this object (if any)
  1282.      *
  1283.      * @return integer 
  1284.      */
  1285.     final public function getIncremented()
  1286.     {
  1287.         $id current($this->_id);
  1288.         if ($id === false{
  1289.             return null;
  1290.         }
  1291.  
  1292.         return $id;
  1293.     }
  1294.     /**
  1295.      * getLast
  1296.      * this method is used internally be Doctrine_Query
  1297.      * it is needed to provide compatibility between
  1298.      * records and collections
  1299.      *
  1300.      * @return Doctrine_Record 
  1301.      */
  1302.     public function getLast()
  1303.     {
  1304.         return $this;
  1305.     }
  1306.     /**
  1307.      * hasRefence
  1308.      * @param string $name 
  1309.      * @return boolean 
  1310.      */
  1311.     public function hasReference($name)
  1312.     {
  1313.         return isset($this->_references[$name]);
  1314.     }
  1315.     /**
  1316.      * obtainReference
  1317.      *
  1318.      * @param string $name 
  1319.      * @throws Doctrine_Record_Exception        if trying to get an unknown related component
  1320.      */
  1321.     public function obtainReference($name)
  1322.     {
  1323.         if (isset($this->_references[$name])) {
  1324.             return $this->_references[$name];
  1325.         }
  1326.         throw new Doctrine_Record_Exception("Unknown reference $name");
  1327.     }
  1328.     /**
  1329.      * getReferences
  1330.      * @return array    all references
  1331.      */
  1332.     public function getReferences()
  1333.     {
  1334.         return $this->_references;
  1335.     }
  1336.     /**
  1337.      * setRelated
  1338.      *
  1339.      * @param string $alias 
  1340.      * @param Doctrine_Access $coll 
  1341.      */
  1342.     final public function setRelated($aliasDoctrine_Access $coll)
  1343.     {
  1344.         $this->_references[$alias$coll;
  1345.     }
  1346.     /**
  1347.      * loadReference
  1348.      * loads a related component
  1349.      *
  1350.      * @throws Doctrine_Table_Exception             if trying to load an unknown related component
  1351.      * @param string $name 
  1352.      * @return void 
  1353.      */
  1354.     public function loadReference($name)
  1355.     {
  1356.         $rel $this->_table->getRelation($name);
  1357.         $this->_references[$name$rel->fetchRelatedFor($this);
  1358.     }
  1359.  
  1360.     /**
  1361.      * merge
  1362.      * merges this record with an array of values
  1363.      *
  1364.      * @param array $values 
  1365.      * @return void 
  1366.      */
  1367.     public function merge(array $values)
  1368.     {
  1369.         foreach ($this->_table->getColumnNames(as $value{
  1370.             try {
  1371.                 if (isset($values[$value])) {
  1372.                     $this->set($value$values[$value]);
  1373.                 }
  1374.             catch(Exception $e{
  1375.                 // silence all exceptions
  1376.             }
  1377.         }
  1378.     }
  1379.     /**
  1380.      * call
  1381.      *
  1382.      * @param string|array$callback    valid callback
  1383.      * @param string $column            column name
  1384.      * @param mixed arg1 ... argN       optional callback arguments
  1385.      * @return Doctrine_Record 
  1386.      */
  1387.     public function call($callback$column)
  1388.     {
  1389.         $args func_get_args();
  1390.         array_shift($args);
  1391.  
  1392.         if (isset($args[0])) {
  1393.             $column $args[0];
  1394.             $args[0$this->get($column);
  1395.  
  1396.             $newvalue call_user_func_array($callback$args);
  1397.  
  1398.             $this->_data[$column$newvalue;
  1399.         }
  1400.         return $this;
  1401.     }
  1402.     /**
  1403.      * getter for node assciated with this record
  1404.      *
  1405.      * @return mixed if tree returns Doctrine_Node otherwise returns false
  1406.      */    
  1407.     public function getNode(
  1408.     {
  1409.         if $this->_table->isTree()) {
  1410.             return false;
  1411.         }
  1412.  
  1413.         if isset($this->_node)) {
  1414.             $this->_node Doctrine_Node::factory($this,
  1415.                                               $this->getTable()->getOption('treeImpl'),
  1416.                                               $this->getTable()->getOption('treeOptions')
  1417.                                               );
  1418.         }
  1419.         
  1420.         return $this->_node;
  1421.     }
  1422.     /**
  1423.      * revert
  1424.      * reverts this record to given version, this method only works if versioning plugin
  1425.      * is enabled
  1426.      *
  1427.      * @throws Doctrine_Record_Exception    if given version does not exist
  1428.      * @param integer $version      an integer > 1
  1429.      * @return Doctrine_Record      this object
  1430.      */
  1431.     public function revert($version)
  1432.     {
  1433.         $data $this->_table
  1434.                 ->getTemplate('Doctrine_Template_Versionable')
  1435.                 ->getAuditLog()
  1436.                 ->getVersion($this$version);
  1437.  
  1438.         if isset($data[0])) {
  1439.             throw new Doctrine_Record_Exception('Version ' $version ' does not exist!');
  1440.         }
  1441.  
  1442.         $this->_data $data[0];
  1443.  
  1444.         return $this;
  1445.     }
  1446.     /**
  1447.      * removeLinks
  1448.      * removes links from this record to given records
  1449.      *
  1450.      * @param string $alias     related component alias
  1451.      * @param array $ids        the identifiers of the related records
  1452.      * @return Doctrine_Record  this object
  1453.      */
  1454.     public function unlink($alias$ids)
  1455.     {
  1456.         $ids = (array) $ids;
  1457.         
  1458.         $q new Doctrine_Query();
  1459.  
  1460.         $rel $this->getTable()->getRelation($alias);
  1461.  
  1462.         if ($rel instanceof Doctrine_Relation_Association{
  1463.             $q->delete()
  1464.               ->from($rel->getAssociationTable()->getComponentName())
  1465.               ->where($rel->getLocal(' = ?'array_values($this->identifier()))
  1466.               ->whereIn($rel->getForeign()$ids);
  1467.  
  1468.             $q->execute();
  1469.  
  1470.  
  1471.         elseif ($rel instanceof Doctrine_Relation_ForeignKey{
  1472.             $q->update($rel->getTable()->getComponentName())
  1473.               ->set($rel->getForeign()'?'array(null))
  1474.               ->addWhere($rel->getForeign(' = ?'array_values($this->identifier()))
  1475.               ->whereIn($rel->getTable()->getIdentifier()$ids);
  1476.  
  1477.             $q->execute();
  1478.         }
  1479.         if (isset($this->_references[$alias])) {
  1480.             foreach ($this->_references[$aliasas $k => $record{
  1481.                 if (in_array(current($record->identifier())$ids)) {
  1482.                     $this->_references[$alias]->remove($k);
  1483.                 }
  1484.             }
  1485.             $this->_references[$alias]->takeSnapshot();
  1486.         }
  1487.         return $this;
  1488.     }
  1489.     /**
  1490.      * __call
  1491.      * this method is a magic method that is being used for method overloading
  1492.      *
  1493.      * the function of this method is to try to find given method from the templates
  1494.      * this record is using and if it finds given method it will execute it
  1495.      *
  1496.      * So, in sense, this method replicates the usage of mixins (as seen in some programming languages)
  1497.      *
  1498.      * @param string $method        name of the method
  1499.      * @param array $args           method arguments
  1500.      * @return mixed                the return value of the given method
  1501.      */
  1502.     public function __call($method$args
  1503.     {
  1504.         foreach ($this->_table->getTemplates(as $template{
  1505.             if (method_exists($template$method)) {
  1506.                 return call_user_func_array(array($template$method)$args);
  1507.             }
  1508.         }
  1509.         
  1510.         throw new Doctrine_Record_Exception('Unknown method ' $method);
  1511.     }
  1512.     /**
  1513.      * used to delete node from tree - MUST BE USE TO DELETE RECORD IF TABLE ACTS AS TREE
  1514.      *
  1515.      */    
  1516.     public function deleteNode({
  1517.         $this->getNode()->delete();
  1518.     }
  1519.     public function toString()
  1520.     {
  1521.         return Doctrine::dump(get_object_vars($this));
  1522.     }
  1523.     /**
  1524.      * returns a string representation of this object
  1525.      */
  1526.     public function __toString()
  1527.     {
  1528.         return (string) $this->_oid;
  1529.     }
  1530. }