<?php

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

/**
 * Worker class for propagate selecting objects.
 *
 * This class handles object selection for relations. It constructs an 
 * <em>OrmSqlSelect</em> (or completes one, if it's received as the second 
 * parameter), and returns it. It handle all kind of relations, and in the 
 * proper order.
 * This is process is complemented later by <em>OrmWorkerPopulate</em>.
 *
 * @author  $Author: pruna $
 * @package orm/worker
 * @version $Revision: 1.3 $
 * @created 19-Oct-2005
 * @modified $Date: 2005/12/06 02:56:45 $
 */
class OrmWorkerSelect extends OrmWorker {
    
    /**
     * Count action.
     * 
     * This method executes a count over same base query used for 
     * <em>select</em> method, with joins.
     * If you need anything more elaborated, or if query needs fields addded 
     * to select expression, you must build it manually.
     * 
     * @access  public
     * @param   OrmMap          $map       Map to use
     * @param   OrmSqlSelect    $sql       SQL object to build
     * @return  OrmSqlSelect
     */
    public function count($map, $sql = null) {
        
        if (is_null($sql)) {
            $sql = $this->getDb()->createSqlSelect();
            $sql->addToTable($map->getTable() . ' AS ' . $map->getTableAlias());
        }
        
        // add o2o joins
        foreach ($map->getO2os() as $attr) {
            if ($attr->getFetch() === OrmMapReader::VALUE_JOIN) {
                $this->doJoin($map, $sql, $attr);
            }
        }
        
        // add m2o joins
        foreach ($map->getO2os() as $attr) {
            if ($attr->getFetch() === OrmMapReader::VALUE_JOIN) {
                $this->doJoin($map, $sql, $attr);
            }
        }
        
        return $sql;
    }
    
    /**
     * Select action.
     *
     * @access  public
     * @param   OrmMap          $map       Map to use
     * @param   OrmSqlSelect    $sql       SQL object to build
     * @return  OrmSqlSelect
     */
    public function select($map, $sql = null) {
        if (is_null($sql)) {
            $sql = $this->getDb()->createSqlSelect();
            $sql->addToTable($map->getTable() . ' AS ' . $map->getTableAlias());
        }
        
        // id
        $sql->addToSelect($this->doSelectField($map->getId(), $map));
        
        // prop
        foreach ($map->getProps() as $attr) {
            $sql->addToSelect($this->doSelectField($attr, $map));
        }
        
        // o2o
        $this->o2o($map, $sql);
        
        // m2o
        $this->m2o($map, $sql);
        
        // o2m
        $this->o2m($map, $sql);
        
        // m2m
        $this->m2m($map, $sql);
        
        return $sql;
    }
    
    /**
     * Apply select action to o2o relations.
     *
     * Note: this method is basically the same than m2o
     * 
     * @access  protected
     * @param   OrmMap        $map       Map to use
     * @param   OrmSql        $sql       SQL object to build
     */
    protected function o2o($map, $sql) {
        foreach ($map->getO2os() as $attr) {
            $this->any2o($map, $sql, $attr);
        }
    }
    
    /**
     * Apply select action to m2o relations.
     *
     * Note: this method is basically the same than o2o
     * 
     * @access  protected
     * @param   OrmMap        $map       Map to use
     * @param   OrmSql        $sql       SQL object to build
     */
    protected function m2o($map, $sql) {
        foreach ($map->getM2os() as $attr) {
            $this->any2o($map, $sql, $attr);
        }
    }
    
    /**
     * Apply select action to m2o and o2o relations.
     *
     * This method performs the same work for both kind of relations.
     * 
     * @access  protected
     * @param   OrmMap                     $map    Map to use
     * @param   OrmSql                     $sql    SQL object to build
     * @param   OrmMapElementCollection    $attr   M2o or O2o element to parse
     */
    protected function any2o($map, $sql, $attr) {
        if ($attr->getFetch() === OrmMapReader::VALUE_SELECT) {
             $sql->addToSelect($this->doSelectField($attr, $map));
        } else {
            // adds all fields defined for this bean
            $sql = $this->select($attr->getFields(), $sql);
            $this->doJoin($map, $sql, $attr);
        }
    }
    
    /**
     * Build join expression for m2o and o2o relations.
     *
     * This auxiliar method builds the join expression for query.
     * 
     * @access  protected
     * @param   OrmMap                     $map    Map to use
     * @param   OrmSql                     $sql    SQL object to build
     * @param   OrmMapElementCollection    $attr   M2o or O2o element to parse
     */
    protected function doJoin($map, $sql, $attr) {
        $sql->addToJoin($attr->getJoin() . ' ' . $attr->getFields()->getTable() 
                . ' AS ' . $attr->getFields()->getTableAlias()
                . ' ON ' . $map->getTableAlias() . '.' . $attr->getColumn()
                . '=' . $attr->getTableAlias() . '.' . $attr->getPropertyRef());
    }
    
    /**
     * Apply select action to o2m relations.
     *
     * @access  protected
     * @param   OrmMap        $map       Map to use
     * @param   OrmSql        $sql       SQL object to build
     */
    protected function o2m($map, $sql) {
        foreach ($map->getO2ms() as $attr) {
            $sql->addToSelect($this->doSelectField($attr, $map));
        }
    }
    
    /**
     * Apply select action to m2m relations.
     *
     * @access  protected
     * @param   OrmMap        $map       Map to use
     * @param   OrmSql        $sql       SQL object to build
     */
    protected function m2m($map, $sql) {
        foreach ($map->getM2ms() as $attr) {
            $sql->addToSelect($this->doSelectField($attr, $map));
        }
    }
    
    /**
     * Auxiliar method that builds a 'select' part string.
     * 
     * Table param could be null only when using formulas.
     * 
     * @access  protected
     * @param   OrmMapElement     $field      Field to add
     * @param   OrmMap            $table      Table to use
     * @return  string
     */
    protected function doSelectField($field, $table = null) {
        if (ObjectInspector::isA($field, 'OrmMapElementProp') 
                && $field->getFormula() != '') {

            // this is only for props with formula
            return '('.$field->getFormula().') AS '.$field->getQName();
        } else {
            return $table->getTableAlias() . '.' . $field->getColumn()
                    . ' AS ' . $field->getQName();
        }
    }
    
}

?>