<?php

require_once 'orm/OrmWorker.php';

/**
 * Worker class for propagate removing objects.
 *
 * This class handles object removal for relations.
 *
 * @author  $Author: pruna $
 * @package orm/worker
 * @version $Revision: 1.3 $
 * @created 19-Oct-2005
 * @modified $Date: 2005/12/06 02:56:45 $
 */
class OrmWorkerRemove extends OrmWorker {
    
    /**
     * Remove action.
     *
     * @access  public
     * @param   Bean        $bean      Bean where propagate action
     * @return  $bean
     */
    public function remove($bean) {
        // First, it deletes in other tables (o2m and m2m)
        $this->o2m($bean);
        $this->m2m($bean);
        
        // Second, it deletes main table
        $sql = $this->getDb()->createSqlDelete();
        
        $sql->addToTable($this->getMap()->getTable());
        
        // id
        $attr = $this->getMap()->getId();
        $sql->addToWhere($this->getMap()->getTable() . '.' . $attr->getColumn() 
            . ' = ?');
        $sql->addToValues($this->getter($bean, $attr));
        
        $this->getDao()->getDb()->delete($sql);
        
        // Last, it delete other tables (m2o)
        $this->m2o($bean);
        
        // @todo must add o2o ?
        
        return $bean;
    }
    
    /**
     * Apply remove action to o2m relations.
     *
     * @access  protected
     * @param   Bean        $bean      Bean where propagate action
     */
    protected function o2m($bean) {
        foreach ($this->getMap()->getO2ms() as $attr) {
           $this->removeBeans($bean, $attr, null);
        }
    }
    
    /**
     * Apply remove action to m2m relations.
     *
     * @access  protected
     * @param   Bean           $bean      Bean where propagate action
     */
    protected function m2m($bean) {
        foreach ($this->getMap()->getM2ms() as $attr) {
            // Deletes in Pivot table
            if ($attr->inCascadePivot(self::ACTION_REMOVE)) {
                // removeM2mPivot() can look for $getter if it's not passed
                $this->removeM2mPivot($this->getMap(), $bean, $attr, 
                        $this->getBu()->getter($bean, $attr->getColumn()));
                        //$this->getter($bean, $this->getMap()->getId()));
            }
            
            $this->m2any($bean, $attr, 'removeBeans');
        }
    }
    
    /**
     * Checks if a row is orphan.
     * 
     * Check if the right side of a m2m relacion is orphan after deleting the 
     * left side.
     *
     * @access  protected
     * @param   Bean           $innerBean    Bean to use
     * @param   OrmMapElement  $attr         Attribute
     * @param   OrmCore        $dao          DAO to use
     * @return  boolean
     */
    protected function isOrphan($innerBean, $attr, $dao) {
        $sql = $this->getDb()->createSqlSelect();
        $sql->addToTable($attr->getPivotTable());
        $sql->addToWhere($attr->getPivotTable() . '.' . 
                $attr->getPivotToRight() . ' = ?');
        
        $innerBeanKey = $this->getBu()->getter($innerBean, 
                $attr->getPropertyRef());
        
        if (is_null($innerBeanKey)) {
            throw new OrmException('Can\'t locate back-reference ' .
                    'in m2m relation', 
                    OrmException::CODE_BACK_REFERENCE_ERROR, $this);
        }
        
        $sql->addToValues($innerBeanKey);
        
        return !$dao->count($sql);
    }
    
    /**
     * Apply remove action to m2o relations.
     *
     * @access  protected
     * @param   Bean          $bean      Bean where propagate action
     */
    protected function m2o($bean) {
        foreach ($this->getMap()->getM2os() as $attr) {
            $this->m2any($bean, $attr, 'removeBean');
        }
    }
    
    /**
     * Apply remove action to m2o and m2m relations.
     *
     * This method performs the same work for both kind of relations.
     * 
     * @access  protected
     * @param   Bean                     $bean    Bean where propagate action
     * @param   OrmMapElementCollection  $attr    M2o or O2o element to parse
     * @param   string                   $method  method to remove beans to call
     */
    protected function m2any($bean, $attr, $remove) {
        $dao = new OrmCore($attr->getFields(), $this->getDao());
        
        // Deletes in other main table
        if ($attr->inCascade(self::ACTION_REMOVE)) {
            $this->$remove($bean, $attr, $dao);
        } elseif ($attr->inCascade(self::ACTION_CLEAN_IF_ORPHAN) 
                || $attr->inCascade(self::ACTION_REMOVE_IF_ORPHAN)) {
            // @todo must 'check' in pivot that other is 'orphan' before 
            // delete
            foreach ($this->getter($bean, $attr) as $innerBean) {
                if ($this->isOrphan($innerBean, $attr, $dao)) {
                    if ($attr->inCascade(self::ACTION_CLEAN_IF_ORPHAN)) {
                        // If no other record in pivot table point to right, 
                        // delete this orphan record(s) from right.
                        $sql = $this->getDb()->createSqlDelete();
                        $sql->addToTable($attr->getTable());
                        $sql->addToWhere($attr->getTable() . '.' . 
                                $attr->getPropertyRef() . ' = ?');
                        $sql->addToValues($this->getBu()->getter($innerBean, 
                                $attr->getPropertyRef()));
                        
                        $this->getDao()->getDb()->delete($sql);
                    } else {
                        // If no other record in pivot table point to right, 
                        // call dao's remove() on them
                        
                        $ret = $dao->remove($innerBean);
                    }
                }
            }
        }
    }
    
    /**
     * Apply remove action to innerBeans.
     *
     * @access  protected
     * @param   Bean                      $bean   Bean where propagate action
     * @param   OrmMapElementCollection   $attr   M2o or O2o element to parse
     * @param   OrmCore                   $dao    DAO to use
     */
    protected function removeBeans($bean, $attr, $dao = null) {
        // Deletes in other main table
        if ($attr->inCascade(self::ACTION_REMOVE)) {
            if (is_null($dao)) {
                $dao = new OrmCore($attr->getFields(), $this->getDao());
            }
            foreach ($this->getter($bean, $attr) as $innerBean) {
                if (!is_null($innerBean)) {
                    $ret = $dao->remove($innerBean);
                }
            }
        }
    }
    
    /**
     * Apply remove action to bean.
     *
     * @access  protected
     * @param   Bean                      $bean   Bean where propagate action
     * @param   OrmMapElementCollection   $attr   M2o or O2o element to parse
     * @param   OrmCore                   $dao    DAO to use
     */
    protected function removeBean($bean, $attr, $dao = null) {
        // Deletes in other main table
        if ($attr->inCascade(self::ACTION_REMOVE)) {
            if (is_null($dao)) {
                $dao = new OrmCore($attr->getFields(), $this->getDao());
            }
            $ret = $dao->remove($this->getter($bean, $attr));
        }
    }
    
    /**
     * Remove pivot table used in m2m relations.
     * Key is the value of the column pointing to left (default is id)
     *
     * @access  protected
     * @param   OrmMap         $map        Map used 
     * @param   Bean           $bean       Bean to get Id from
     * @param   OrmMapElement  $attr       Attribute
     * @param   mixed          $key        Key to use
     * @return  mixed
     */
    protected function removeM2mPivot($map, $bean, $attr, $key = null) {
        // can't use DAO like others, because this is a 'special' structure
        // for pivot tables. So, this method creates an ad-hoc OrmSqlDelete
        
        if (is_null($key)) {
            $key = $this->getBu()->getter($bean, $attr->getColumn());
            //$key = $this->getter($bean, $map->getId());
        }
        
        $sql = $this->getDb()->createSqlDelete();
        $sql->addToTable($attr->getPivotTable());
        $sql->addToWhere($attr->getPivotTable() . '.' . 
                $attr->getPivotToLeft() . ' = ?');
        $sql->addToValues($key);
        
        return $this->getDao()->getDb()->delete($sql);
    }
    
}

?>