<?php

require_once 'orm/OrmWorker.php';
require_once 'orm/OrmMapReader.php';

/**
 * Worker class for propagate selecting objects.
 *
 * This class handles object population after selects. This class's job is to 
 * populate the <em>Bean</em> with the results of the previous select, 
 * prepared by a <em>OrmWorkerSelect</em>.
 *
 * @author  $Author: pruna $
 * @package orm/worker
 * @version $Revision: 1.3 $
 * @created 19-Oct-2005
 * @modified $Date: 2005/12/06 02:56:45 $
 */
class OrmWorkerPopulate extends OrmWorker {
    
    /**
     * Populates <em>Bean</em> based on result's values.
     *
     * @access  public
     * @param   OrmMap       $map      Map to use
     * @param   array        $values   Values from SQL row result array
     * @param   Bean         $bean     Bean to populate, or null for a new one
     * @return  Bean
     */
    function populateBean($map, $values, $bean = null) {
        if (is_null($bean) || is_string($bean)) {
           $bean = $this->createBean($map, $bean);
        }

        // id
        $attr = $map->getId();
        $this->getBu()->setter(
                $bean, $attr->getName(), $values[$attr->getQName()]);

        // props
        foreach ($map->getProps() as $attr) {
            $this->getBu()->setter(
                    $bean, $attr->getName(), $values[$attr->getQName()]);
        }
        
        // o2o
        $this->o2o($map, $values, $bean);
        
        // o2m
        $this->o2m($map, $values, $bean);
        
        // m2o
        $this->m2o($map, $values, $bean);
        
        // m2m
        $this->m2m($map, $values, $bean);
        
        return $bean;
    }
    
    /**
     * Populate o2o relations.
     *
     * Note: this method is basically the same than m2o
     * 
     * @access  protected
     * @param   OrmMap        $map       Map to use
     * @param   array         $vaules    Values from SQL row result array
     * @param   Bean          $bean      Bean to populate
     */
    protected function o2o($map, $values, $bean) {
        foreach ($map->getO2os() as $attr) {
            $this->any2o($attr, $values, $bean, $attr->isExtends());
        }
    }
    
    /**
     * Populate m2o relations.
     *
     * Note: this method is basically the same than o2o
     * 
     * @access  protected
     * @param   OrmMap        $map       Map to use
     * @param   array         $values    Values from SQL row result array
     * @param   Bean          $bean      Bean to populate
     */
    protected function m2o($map, $values, $bean) {
        foreach ($map->getM2os() as $attr) {
            $this->any2o($attr, $values, $bean, FALSE);
        }
    }
    
    
    /**
     * Populate o2o and m2o relations.
     * 
     * This method does the job for o2o and m2o. The only difference between 
     * them is that <em>extends</em> is only supported for o2o, and m2o allways 
     * set it to false.
     * 
     * @access  protected
     * @param   OrmMapElementCollection   $attr     M2o or O2o element to parse
     * @param   array                     $values   Values from SQL row result
     * @param   Bean                      $bean     Bean to populate
     * @param   boolean                   $extends  If extends is true
     */
    private function any2o($attr, $values, $bean, $extends) {
        $setter = $this->getBu()->setterName($attr->getName());
        if ($attr->getFetch() === OrmMapReader::VALUE_SELECT) {
            if ($attr->isNullable() && 
                    $this->isNullValue($values[$attr->getQName()])) {
                $bean->$setter(null);
            } else {
                if ($attr->isLazy()) {
                    $bean->$setter($this->createLazyAttribute(
                            $attr->getName(), $attr->getFields(), 
                            self::ACTION_GET, 
                            $values[$attr->getQName()]));
                } else {
                    $dao = new OrmCore($attr->getFields(), $this->getDao());
                    if ($extends) {
                        $b = $dao->get($values[$attr->getQName()]);
                        $this->getBu()->copy($b, $bean, 
                                BeanUtils::COPY_METHOD_INNER);
                    } else {
                        $bean->$setter($dao->get($values[$attr->getQName()]));
                    }
                }
            }
        } else {
            // Create bean, using values from this query
            // but also continue with other bean's own stuff.
            // To reach here if values are null it had should used some kind 
            // of outer join, and then bean will be populated with null values
            if ($extends) {
                $this->populateBean($attr->getFields(), $values, $bean);
            } else {
                $bean->$setter($this->populateBean($attr->getFields(), 
                        $values, $attr->getClass()));
            }
        }
    }
    
    /**
     * Populate o2m relations.
     *
     * @access  protected
     * @param   OrmMap        $map       Map to use
     * @param   array         $values    Values from SQL row result array
     * @param   Bean          $bean      Bean to populate
     */
    protected function o2m($map, $values, $bean) {
        foreach ($map->getO2ms() as $attr) {
            $setter = $this->getBu()->setterName($attr->getName());
            
            // if $values[$attr->getQName()] could be null, and add isNullable 
            // to o2m, could avoid all this setting value as null: 
            // $bean->$setter(null);
            $sql = $this->getDb()->createSqlSelect();
            $sql->addToWhere($attr->getTableAlias() . '.' . 
                    $attr->getPropertyRef() . '= ?');
            $sql->addToValues($values[$attr->getQName()]);

            if ($attr->isLazy()) {
                $bean->$setter($this->createLazyAttribute($attr->getName(), 
                        $attr->getFields(), self::ACTION_SEARCH, $sql));
            } else {
                $dao = new OrmCore($attr->getFields(), $this->getDao());

                // search all in other table referencing to this object
                $bean->$setter($dao->search($sql));
            }
        }
     }
    
    /**
     * Populate m2m relations.
     *
     * @access  protected
     * @param   OrmMap        $map       Map to use
     * @param   array         $values    Values from SQL row result array
     * @param   Bean          $bean      Bean to populate
     */
    protected function m2m($map, $values, $bean) {
        foreach ($map->getM2ms() as $attr) {
            $setter = $this->getBu()->setterName($attr->getName());

            // if $values[$attr->getQName()] could be null, and add isNullable 
            // to m2m, could avoid all this setting value as null: 
            // $bean->$setter(null);
            $sql = $this->getDb()->createSqlSelect();
            $sql->addToTable($attr->getPivotTable());
            $sql->addToWhere($attr->getPivotTable() . '.' . 
                    $attr->getPivotToLeft() . '= ?');
            $sql->addToWhere($attr->getPivotTable() . '.' . 
                    $attr->getPivotToRight() . '=' . $attr->getTableAlias() . 
                    '.' . $attr->getPropertyRef());
            $sql->addToValues($values[$attr->getQName()]);
            
            if ($attr->isLazy()) {
                $bean->$setter($this->createLazyAttribute($attr->getName(), 
                        $attr->getFields(), self::ACTION_SEARCH, $sql));
            } else {
                $dao = new OrmCore($attr->getFields(), $this->getDao());
                
                // search all in other table referencing to this object
                $bean->$setter($dao->search($sql));
            }
        }
    }
    
    /**
     * Auxiliar method that creates a OrmLazyAttribute
     *
     * @access  protected
     * @param   string       $key       Key of attribute
     * @param   OrmMap       $map       Map to be used
     * @param   string       $method    Method to be used
     * @param   mixed        $param     Param to be used
     * @return  OrmLazyAttribute
     */
    protected function createLazyAttribute($key, $map, $method, $param) {
        return new OrmLazyAttribute(array(
            'key' => $key,
            'map' => $map,
            'method' => $method,
            'param' => $param,
            'db' => $this->getDb(),
        ));
    }
    
}

?>
