<?php
class Bvb_Grid_Source_Doctrine implements Bvb_Grid_Source_Interface
{
    /**
     * Stores the supplied Doctrine_Query
     * 
     * @var Doctrine_Query
     */
    protected $_query;
    
    /**
     * Stores the parsed out DQL
     * 
     * @var array
     */
    protected $_queryParts = array(
        'select' => array(),
        'from'   => array(),
        'join'   => array()
    );
    
    /**
     * Intialize the Doctrine_Query. We will parse out
     * all the provided DQL or start a Doctrine_Query
     * if an instance of Doctrine_Record is provided
     * 
     * @param mixed $q
     */
    public function __construct($q)
    {
        if ($q instanceof Doctrine_Record) {
            $q = get_class($q);
        }
        
        if (is_string($q)) {
            $q = Doctrine_Query::create()->from($q);
        }
        
        if (!$q instanceof Doctrine_Query) {
            require_once 'Bvb/Grid/Source/Doctrine/Exception.php';
            throw new Bvb_Grid_Source_Doctrine_Exception(
                "Please provide only an instance of Doctrine_Query "
                 . "or a valid Doctrine_Record instance"
            );
        }
        
        $this->_query = $q;
        $this->_setFromParts();
        $this->_setSelectParts();
    }
    
    /**
     * Simple method for the Grid to determine if this
     * Source can handle CRUD
     * 
     * @return boolean
     */
    public function hasCrud()
    {
        return true;
    }
    
    /**
     * Returns the "main" table
     * the one after select * FROM {MAIN_TABLE}
     * 
     * @return array
     */
    public function getMainTable()
    {
        $table = Doctrine::getTable($this->_queryParts['from']['tableModel']);
        return array('table' => $table->getTableName());
    }
    
    /**
     * builds a key=>value array
     *
     * they must have two options
     * title and field
     * field is used to perform queries.
     * Must have table name or table alias as a prefix
     * ex: user.id | country.population
     *
     * The key for this array is the output field
     * If raw sql is somehting like
     *
     * select name as alias, country from users
     *
     * the return array must be like this:
     *
     * array('alias'=>array('title'=>'alias','field'=>'users.name'));
     *
     * its not bad idea to apply this to fields titles
     * $title = ucwords(str_replace('_',' ',$title));
     * 
     * @return array
     */
    public function buildFields()
    {
        $this->_setSelectParts();
        return $this->_queryParts['select'];
    }
    
    /**
     * Use the supplied Doctrine_Query to find its primary ID
     * 
     * TODO : Implement usage of $table param
     */
    public function getPrimaryKey($table = null)
    {
        $return = array();
        $table = Doctrine::getTable($this->_queryParts['from']['tableModel']);
        $alias = $this->_queryParts['from']['alias'];
        
        //Get the Primary Key(s) for provided table
        $ids = $table->getIdentifierColumnNames();
        
        //Append the alias to each Primary key field
        foreach ($ids as $id) {
            $return[] = (!empty($alias)) ? $alias . '.' . $id : $id;
        }
        
        return $return;
    }
    
    /**
     * Gets a unique record as a associative array
     * 
     * @param $table
     * @param $condition
     * @return array
     */
    public function getRecord($table, array $condition)
    {
        $tableModel = $this->_getModelFromTable($table);
        
        $query = Doctrine_Query::create()->from($tableModel);
        
        foreach ($condition as $field => $value) {
            $query->addWhere($field . ' = ?', $value);
        }
        
        $results = $query->fetchArray(array(), Doctrine::HYDRATE_SCALAR);
        
        $newResults = $this->_cleanQueryResults($results);
        
        if (count($newResults) == 1) {
            return $newResults[0];
        }
        
        return $newResults;
    }
    /**
     * Should return the database server name or source name
     *
     * Ex: mysql, pgsql, array, xml
     * 
     * @return string
     */
    public function getSourceName()
    {
        return strtolower($this->_query->getConnection()->getDriverName());
    }
    /**
     * Runs the query and returns the result as a associative array
     * 
     * @return array
     */
    public function execute()
    {
        $newQuery = clone $this->_query;
        $results = $newQuery->execute(array(), Doctrine::HYDRATE_SCALAR);
        
        $newArray = $this->_cleanQueryResults($results);
        
        return $newArray;
    }
    /**
     * Get a record detail based the current query
     * 
     * <code>
     * $where = array(
     *     array('columnName' => 'searchValue')
     * )
     * </code>
     * 
     * @param array $where 
     * @return array
     */
    public function fetchDetail(array $where)
    {
        /**
         * Remove these since we are trying to retrieve 
         * a specific row
         */
        $this->_query->removeDqlQueryPart('limit')
                     ->removeDqlQueryPart('offset');
        foreach ($where as $column => $value) {
            $this->_query->addWhere($column . ' = ?', $value);
        }
        
        return $this->execute();
    }
    /**
     * Return the total of records
     * 
     * @return integer
     */
    public function getTotalRecords()
    {
        return (int) $this->_query->count();
    }
    /**
     * Ex: array('c'=>array('tableName'=>'Country'));
     * where c is the table alias. If the table as no alias,
     * c should be the table name
     * 
     * @return array
     */
    function getTableList ()
    {
        die('getTableList');
    }
    /**
     * Return possible filters values based on field definion
     * This is mostly used for enum fields where the possibile
     * values are extracted
     *
     * Ex: enum('Yes','No','Empty');
     *
     * should return
     *
     * array('Yes'=>'Yes','No'=>'No','Empty'=>'Empty');
     *
     * @param $field
     * @return string
     */
    public function getFilterValuesBasedOnFieldDefinition($field)
    {
        $table = $this->_getModelFromColumn($field);
        
        $tableClass = Doctrine::getTable($table);
        
        if (strpos($field, '.') !== false) {
            list($alias, $column) = explode('.', $field);
        } else {
            $column = $field;
        }
        
        $definition = $tableClass->getDefinitionOf($column);
        
        if ($definition['type'] == 'enum') {
            foreach ($definition['values'] as $val) {
                $return[$val] = $val;
            }
            
            return $return;
        }
        
        return 'text';
    }
    /**
     * Return te field type
     * char, varchar, int
     *
     * Note: If the field is enum or set,
     * the value returned must be set or enum,
     * and not the full definition
     *
     * @param string $field
     * @return string|null
     */
    public function getFieldType($field)
    {
        $tableModel = $this->_getModelFromColumn($field);
        
        if (isset($this->_queryParts['select'][$field]['field'])) {
            if (strpos($this->_queryParts['select'][$field]['field'], '.') !== false) {
                list($alias, $fieldName) = explode('.', $this->_queryParts['select'][$field]['field']);
            } else {
                $fieldName = $field;
            }
        } else {
            return null;
        }
        
        $fieldType = Doctrine::getTable($tableModel)->getTypeOfColumn($fieldName);
        
        return $fieldType;
    }
    /**
     *
     * Build the order part from the query.
     *
     * The first arg is the field to be ordered and the $order
     * arg is the correspondent order (ASC|DESC)
     *
     * If the $reset is set to true, all previous order should be removed
     *
     * @param string $field
     * @param string $order
     * @param bool $reset
     * @return Bvb_Grid_Source_Doctrine
     */
    public function buildQueryOrder($field, $order, $reset = false)
    {
        if ($reset) {
            $this->_query->removeDqlQueryPart('orderby');
        }
        
        $this->_query->addOrderBy($field . ' ' . $order);
        
        return $this;
    }
    /**
     * Build the query limit clause
     * 
     * @param $start
     * @param $offset
     * @return Bvb_Grid_Source_Doctrine
     */
    public function buildQueryLimit($start, $offset)
    {
        $this->_query->limit($start)->offset($offset);
        
        return $this;
    }
    /**
     * Returns the select object
     */
    function getSelectObject ()
    {
        die('getSelectObject');
    }
    /**
     * Returns the selected order
     * that was defined by the user in the query entered
     * and not the one generated by the system
     *
     * If empty an empty array must be returned.
     *
     * Else the array must be like this:
     * 
     * <code>
     * $return = array(
     *     0 => field
     *     1 => (ASC|DESC)
     * );
     * </code>
     *
     * @return array
     */
    public function getSelectOrder()
    {
        $newOrderBys = array();
        $orderBy = $this->_query->getDqlPart('orderby');
        
        if (!empty($orderBy)) {
            foreach ($orderBy as $anOrderby) {
                $orderBys = explode(',', $anOrderby);
                
                foreach ($orderBys as $order) {
                    $parts = explode(' ', trim($order));
                    if (strtolower($parts[1]) != 'desc' && strtolower($parts[1]) != 'asc') {
                        $parts[1] = '';
                    }
                    $newOrderBys[] = $parts;
                }
            }
        }
        
        return $newOrderBys;
    }
    /**
     * Should preform a query based on the provided by the user
     * select the two fields and return an array $field=>$value
     * as result
     *
     * ex: SELECT $field, $value FROM *
     * array('1'=>'Something','2'=>'Number','3'=>'history')....;
     *
     * @param string $field
     * @param string $value
     * @return array
     */
    public function getDistinctValuesForFilters($field, $value)
    {
        $return = array();
        
        $newQuery = clone $this->_query;
        
        $distinct = new Doctrine_Expression("DISTINCT($field)");
        
        $newQuery->removeDqlQueryPart('select')
                 ->removeDqlQueryPart('orderby')
                 ->removeDqlQueryPart('limit')
                 ->removeDqlQueryPart('offset')
                 ->select("$distinct AS field, $value AS value")
                 ->orderBy('value ASC');
        
        $results = $newQuery->fetchArray();
        
        foreach ($results as $value ) {
            $return[$value['field']] = $value['value'];
        }
        
        return $return;
    }
    /**
     * Perform a sqlexp
     *
     * $value =  array ('functions' => array ('AVG'), 'value' => 'Population' );
     *
     * Should be converted to
     * SELECT AVG(Population) FROM *
     *
     * $value =  array ('functions' => array ('SUM','AVG'), 'value' => 'Population' );
     *
     * Should be converted to
     * SELECT SUM(AVG(Population)) FROM *
     *
     * @param array $value
     */
    public function getSqlExp(array $value)
    {
        $return = array();
        
        $newQuery = clone $this->_query;
        
        foreach (array_reverse($value['functions']) as $key => $func) {
            if ($key == 0) {
                $exp = $func . '(' . $value['value'] . ')';
            } else {
                $exp = $func . '(' . $exp . ')';
            }
        }
        
        $exp = new Doctrine_Expression($exp);
        
        $newQuery->removeDqlQueryPart('select')
                 ->removeDqlQueryPart('orderby')
                 ->removeDqlQueryPart('limit')
                 ->removeDqlQueryPart('offset')
                 ->select("$exp AS total");
        
        $result = $newQuery->fetchOne(array(), Doctrine::HYDRATE_ARRAY);
        
        return $result['total'];
    }
    /**
     * Adds a fulltext search instead of a addcondition method
     *
     * $field has an index search
     * $field['search'] = array('extra'=>'boolean|queryExpansion','indexes'=>'string|array');
     *
     * if no indexes provided, use the field name
     *
     * boolean =>  IN BOOLEAN MODE
     * queryExpansion =>  WITH QUERY EXPANSION
     *
     * @param $filter
     * @param $field
     */
    function addFullTextSearch ($filter, $field)
    {
        die('addFullTextSearch');
    }
    /**
     * Adds a new condition to the current query
     * $filter is the value to be filtered
     * $op is the opreand to be used: =,>=, like, llike,REGEX,
     * $completeField. use the index $completField['field'] to
     * specify the field, to avoid ambiguous
     *
     * @param $filter
     * @param $op
     * @param $completeField
     * @return Bvb_Grid_Source_Doctrine
     */
    public function addCondition($filter, $op, $completeField)
    {
        $field = $completeField['field'];
        
        switch (strtolower($op)) {
            case 'equal':
            case '=':
                $this->_query->addWhere($field . ' = ?', $filter);
                break;
            case 'regex':
                $this->_query->addWhere($field . " REGEXP ?", $filter);
                break;
            case 'rlike':
                $this->_query->addWhere($field . " LIKE ?", $filter . "%");
                break;
            case 'llike':
                $this->_query->addWhere($field . " LIKE ?", "%" . $filter);
                break;
            case '>=':
                $this->_query->addWhere($field . " >= ?", $filter);
                break;
            case '>':
                $this->_query->addWhere($field . " > ?", $filter);
                break;
            case '<>':
            case '!=':
                $this->_query->addWhere($field . " <> ?", $filter);
                break;
            case '<=':
                $this->_query->addWhere($field . " <= ?", $filter);
                break;
            case '<':
                $this->_query->addWhere($field . " < ?", $filter);
                break;
            case 'in':
                $filter = explode(',', $filter);
                $this->_query->whereIn($field, $filter);
                break;
            case 'range':
                $start = substr($filter, 0, strpos($filter, '<>'));
                $end = substr($filter, strpos($filter, '<>') + 2);
                $this->_query->addWhere($field . " between ? and ?", array($start, $end));
                break;
            case 'like':
            default:
                $this->_query->addWhere($field . " LIKE ?", "%" . $filter . "%");
                break;
        }
        
        return $this;
    }
    /**
     * Insert an array of key=>values in the specified table
     *
     * @param string $table
     * @param array $post
     * @return boolean
     */
    public function insert($table, array $post)
    {
        $tableModel = $this->_getModelFromTable($table);
        $id = Doctrine::getTable($tableModel)->getIdentifier();
        
        /**
         * @var Doctrine_Record
         */
        $model = new $tableModel;
        $model->fromArray($post);
        
        return $model->trySave();
    }
    /**
     * Update values in a table using the $condition clause
     *
     * The condition clause is a $field=>$value array
     * that should be escaped by YOU (if your class doesn't do that for you)
     * and usinf the AND operand
     *
     * Ex: array('user_id'=>'1','id_site'=>'12');
     *
     * Raw SQL: * WHERE user_id='1' AND id_site='12'
     *
     * @param string $table
     * @param array $post
     * @param array $condition
     * @return integer Of Affected rows
     */
    public function update($table, array $post, array $condition)
    {
        $tableModel = $this->_getModelFromTable($table);
        
        $query = Doctrine_Query::create()->update($tableModel);
        
        foreach ($post as $field => $value) {
            $query->set($field, '?', $value);
        }
        
        foreach ($condition as $field => $value) {
            $query->addWhere($field . ' = ?', $value);
        }
        
        return $query->execute();
    }
    /**
     * Delete a record from a table
     *
     * The condition clause is a $field=>$value array
     * that should be escaped by YOU (if your class doesn't do that for you)
     * and usinf the AND operand
     *
     * Ex: array('user_id'=>'1','id_site'=>'12');
     * Raw SQL: * WHERE user_id='1' AND id_site='12'
     *
     * @param string $table
     * @param array $condition
     * @return integer Of Affected rows
     */
    public function delete($table, array $condition)
    {
        $tableModel = $this->_getModelFromTable($table);
        
        $query = Doctrine_Query::create()->delete($tableModel);
        
        foreach ($condition as $field => $value) {
            $query->addWhere($field . ' = ?', $value);
        }
        
        return $query->execute();
    }
    /**
     * Removes any order in query
     * 
     * @return Bvb_Grid_Source_Doctrine
     */
    public function resetOrder()
    {
        $this->_query->removeDqlQueryPart('orderby');
        return $this;
    }
    /**
     * Cache handler.
     * 
     * TODO: Research what 'cache' does, might just need to look at the 
     *       bool and see if we need to set Doctrine Cache or not
     */
    function setCache($cache)
    {
        //die(Zend_Debug::dump($cache));
        //die('setCache');
    }
    /**
     * Build the form based on a Model or query
     * 
     * @return array
     */
    public function buildForm()
    {
        $table = $this->_queryParts['from']['tableModel'];
        $columns = Doctrine::getTable($table)->getColumns();
        
        return $this->buildFormElements($columns);
    }
    
    /**
     * Will build out an array of form elements, 
     * based on the column type and return the array
     * to be used when loading the Bvb_Grid_Form
     * 
     * @param array $cols
     * @param array $info
     * @return array
     */
    public function buildFormElements(array $cols, $info = array())
    {
        $form = array();
        
        foreach ($cols as $column => $detail) {
            
            if (isset($detail['primary']) && $detail['primary']) {
                continue;
            }
            
            $label = ucwords(str_replace('_', ' ', $column));
            
            switch ($detail['type']) {
                case 'enum':
                    $form['elements'][$column] = array('select', array('multiOptions' => $detail['values'], 'required' => ($detail['notnull'] == 1) ? false : true, 'label' => $label));
                    break;
                
                case 'string':
                case 'varchar':
                case 'char':
                    $length = $detail['length'];
                    $form['elements'][$column] = array('text', array('validators' => array(array('stringLength', false, array(0, $length))), 'size' => 40, 'label' => $label, 'required' => ($detail['notnull'] == 1) ? false : true, 'value' => (! empty($detail['default']) ? $detail['default'] : "")));
                    break;
                case 'date':
                    $form['elements'][$column] = array('text', array('validators' => array(array('Date')), 'size' => 10, 'label' => $label, 'required' => ($detail['notnull'] == 1) ? false : true, 'value' => (! empty($detail['default']) ? $detail['default'] : "")));
                    break;
                case 'datetime':
                case 'timestamp':
                    $form['elements'][$column] = array('text', array('validators' => array(array(new Zend_Validate_Date('Y-m-d H:i:s'))), 'size' => 19, 'label' => $label, 'required' => ($detail['notnull'] == 1) ? false : true, 'value' => (! empty($detail['default']) ? $detail['default'] : "")));
                    break;
                
                case 'text':
                case 'mediumtext':
                case 'longtext':
                case 'smalltext':
                    $form['elements'][$column] = array('textarea', array('label' => $label, 'required' => ($detail['notnull'] == 1) ? false : true, 'filters' => array('StripTags')));
                    break;
                
                case 'integer':
                case 'int':
                case 'bigint':
                case 'mediumint':
                case 'smallint':
                case 'tinyint':
                    $isZero = (! empty($detail['default']) && $detail['default'] == "0") ? true : false;
                    $form['elements'][$column] = array('text', array('validators' => array('Digits'), 'label' => $label, 'size' => 10, 'required' => ($isZero == false && $detail['notnull'] == 1) ? false : true, 'value' => (! empty($detail['default']) ? $detail['default'] : "")));
                    break;
                case 'float':
                case 'decimal':
                case 'double':
                    $form['elements'][$column] = array('text', array('validators' => array('Float'), 'size' => 10, 'label' => $label, 'required' => ($detail['notnull'] == 1) ? false : true, 'value' => (! empty($detail['default']) ? $detail['default'] : "")));
                    break;
                default:
                    break;
            }
        }
        
        return $form;
    }
    
    /**
     * Used within this class to clean the hydrated result
     * to be something more Grid friendly
     * 
     * @param array $results
     * @return array Cleaned results
     */
    protected function _cleanQueryResults($results)
    {
        $newArray = array();
        
        if (empty($this->_queryParts['from']['alias'])) {
            foreach ($results as $rows) {
                $temp = array();
                foreach ($rows as $col => $val) {
                    $name = str_replace($this->_queryParts['from']['tableModel'] . '_', '', $col);
                    $temp[$name] = $val;
                }
                
                $newArray[] = $temp;
            }
        } else {
            foreach ($results as $rows) {
                $temp = array();
                
                foreach ($rows as $col => $val) {
                    $parts = explode('_', $col, 2);
                    
                    foreach ($this->_queryParts['select'] as $alias => $select) {
                        if (implode('.', $parts) == $select['field'] || $parts[1] == $alias) {
                            $temp[$alias] = $val;
                        }
                    }
                }
                
                $newArray[] = $temp;
            }
        }
        
        return $newArray;
    }
    
    /**
     * Used to parse out the SELECT pieces of the DQL
     * and place it in the $_queryParts array for use
     * in many other places
     * 
     * @return Bvb_Grid_Source_Doctrine
     */
    protected function _setSelectParts()
    {
        $return = array();
        
        $selects = $this->_query->getDqlPart('select');
        
        if (empty($selects)) {
            $this->_findAndSetSelect();
            $selects = $this->_query->getDqlPart('select');
        }
        
        //Remove all 'as' instances
        $selects = $this->_removeAs($selects);
        
        foreach ($selects as $select) {
            $fields = explode(',', $select);
            $fields = array_map('trim', $fields);
            
            foreach ($fields as $field) {
                $fieldName = trim($field);
                $fieldAlias = null;
                
                if (count(explode(' ', trim($field))) > 1) {
                    list($fieldName, $fieldAlias) = explode(' ', trim($field));
                }
                
                if (empty($fieldAlias)) {
                    $pos = strpos($fieldName, '.');
                    $fieldAlias = substr($fieldName, ++$pos);
                }
                
                $return[$fieldAlias] = array(
                    'title' => ucwords(str_replace('_', ' ', $fieldAlias)),
                    'field' => $fieldName
                );
            }
        }
        
        $this->_queryParts['select'] = $return;
        return $this;
    }
    
    /**
     * Used to parse out the FROM and JOIN pieces of the DQL
     * and place it in the $_queryParts array for use
     * in many other places
     * 
     * @return Bvb_Grid_Source_Doctrine
     */
    protected function _setFromParts()
    {
        $return = array();
        
        //Remove all 'as' instances
        $froms = $this->_removeAs($this->_query->getDqlPart('from'));
        
        foreach ($froms as $from) {
            $fields = explode(',', $from);
            $fields = array_map('trim', $fields);
            
            foreach ($fields as $field) {
                if (strpos(strtoupper($field), 'JOIN') === false) {
                    $this->_queryParts = array_merge($this->_queryParts, $this->_explodeFrom($field));
                } else {
                    $join = explode('JOIN', $field);
                    $join = array_map('trim', $join);
                    
                    $joinType = strtolower($join[0]);
                    
                    $this->_queryParts = array_merge($this->_queryParts, $this->_explodeJoin($join[1], $joinType));
                }
            }
            
        }
        
        return $this;
    }
    
    /**
     * Used to set SELECT values to the DQL when
     * no SELECT is provided.  We will just add
     * ALL columns for all tables given.
     * 
     * NOTE: Since no SELECT was provided, to access these
     * from within the Bvb_Grid_Data class, you will need to
     * use the table alias + "_" + column name
     * 
     * <code>
     * $grid->setGridColumns(array('co_code', 'co_name', 'co_continent', 'ci_name'));
     * </code>
     * 
     * @return void
     */
    protected function _findAndSetSelect()
    {
        $return = array();
        
        //Make sure we have the FROM set
        $this->_setFromParts();
        
        $fromTableModel = $this->_queryParts['from']['tableModel'];
        $fromClass = Doctrine::getTable($fromTableModel);
        $fromColumns = array_keys($fromClass->getColumns());
        $fromAlias = $this->_queryParts['from']['alias'];
        
        foreach ($fromColumns as $fromColumn) {
            /**
             * Do this check here because a DQL with no JOIN,
             * does not need a table alias
             * 
             * @var string
             */
            $addColumn = (!empty($fromAlias)) ? $fromAlias . '.' . $fromColumn : $fromColumn;
            $this->_query->addSelect($addColumn);
        }
        
        $joins = $this->_queryParts['join'];
        
        if (!empty($joins)) {
            foreach ($joins as $joinType) {
                foreach ($joinType as $join) {
                    $joinClass = Doctrine::getTable($join['tableModel']);
                    $joinColumns = array_keys($joinClass->getColumns());
                    
                    foreach ($joinColumns as $joinColumn) {
                        $this->_query->addSelect($join['alias'] . '.' . $joinColumn);
                    }
                }
            }
        }
    }
    
    /**
     * Take a DQL SELECT string and parse it into 
     * a usable array
     * 
     * <code>
     * $return = array(
     *     'from' => array(
     *         'alias' => 'c',
     *         'table' => 'country'
     *     )
     * )
     * </code>
     * 
     * @param string $from A DQL SELECT statement
     * @return array
     */
    private function _explodeFrom($from)
    {
        if (!is_string($from)) {
            require_once 'Bvb/Grid/Source/Doctrine/Exception.php';
            throw new Bvb_Grid_Source_Doctrine_Exception('Provided param needs to be a string only');
        }
        
        $return = array();
        $table = $from;
        $alias = null;
        
        if (count(explode(' ', $from)) > 1) {
            list($table, $alias) = explode(' ', $from);
        }
        
        if (strpos($table, '.') !== false) {
            $return = $this->_explodeJoin($from, 'left');
        } else {
            $return['from'] = array(
                'alias' => $alias,
                'tableModel' => $table,
                'tableName' => Doctrine::getTable($table)->getTableName()
            );
        }
        
        return $return;
    }
    
    /**
     * Take a DQL JOIN string and parse it into
     * a usable array
     * 
     * <code>
     * $return = array(
     *     'join' => array(
     *         'left' => array(
     *             array(
     *                 'alias' => 'ci'
     *                 'table' => 'Model_City' //Doctrine Table name
     *                 'tableAlias' => 'City' //What is used in the JOIN statement
     *                 'joinOn' => 'c'
     *             )
     *         )
     *     )
     * )
     * </code>
     * 
     * @param string $join
     * @param string $joinType The type of join - LEFT, RIGHT, etc
     */
    private function _explodeJoin($join, $joinType)
    {
        $return = array();
        
        list($table, $alias) = explode(' ', $join);
        list($joinOn, $tableAlias) = explode('.', $table);
        $mainTable = $this->_getModelFromAlias($joinOn);
        
        $tableModel = Doctrine::getTable($mainTable)->getRelation($tableAlias)->getClass();
        
        $return['join'][strtolower($joinType)][] = array(
            'alias'      => $alias,
            'tableModel' => $tableModel,
            'tableAlias' => $tableAlias,
            'joinOn'     => $joinOn
        );
        
        return $return;
    }
    
    /**
     * Simple utility for removing "as" from the DQL
     * 
     * @param string $subject
     * @return string No-As DQL expression
     */
    private function _removeAs($subject)
    {
        return str_replace(array(' AS', ' As', ' aS', ' as'), array('', '', '', ''), $subject);
    }
    
    /**
     * Find the table for which a column belongs
     * 
     * @param string $column
     * @return string Name of the table used
     */
    private function _getModelFromColumn($column)
    {
        if (!is_string($column)) {
            $type = gettype($column);
            require_once 'Bvb/Grid/Source/Doctrine/Exception.php';
            throw new Bvb_Grid_Source_Doctrine_Exception('The $column param needs to be a string, ' . $type . ' provided');
        }
        
        if (empty($this->_queryParts['from']['alias'])) {
            return $this->_queryParts['from']['tableModel'];
        }
        
        list($alias, $field) = explode('.', $column);
        
        return $this->_getModelFromAlias($alias);
    }
    
    /**
     * Find the table/model based on the table alias provided
     * 
     * @param string $alias
     * @return string
     */
    private function _getModelFromAlias($alias)
    {
        if (!is_string($alias)) {
            $type = gettype($alias);
            require_once 'Bvb/Grid/Source/Doctrine/Exception.php';
            throw new Bvb_Grid_Source_Doctrine_Exception('The $alias param needs to be a string, ' . $type . ' provided');
        }
        
        if ($this->_queryParts['from']['alias'] == $alias) {
            return $this->_queryParts['from']['tableModel'];
        }
        
        foreach ($this->_queryParts['join'] as $joins) {
            foreach ($joins as $join) {
                if ($join['alias'] == $alias) {
                    return $join['tableModel'];
                }
            }
        }
    }
    
    /**
     * Find the table/model based on the table alias provided
     * 
     * @param string $table Name of table to find model from
     * @return string Name of the model associated with the provided table
     */
    private function _getModelFromTable($table)
    {
        if (!is_string($table)) {
            $type = gettype($table);
            require_once 'Bvb/Grid/Source/Doctrine/Exception.php';
            throw new Bvb_Grid_Source_Doctrine_Exception('The $table param needs to be a string, ' . $type . ' provided');
        }
        
        if ($this->_queryParts['from']['tableName'] == $table) {
            return $this->_queryParts['from']['tableModel'];
        }
        
        foreach ($this->_queryParts['join'] as $joins) {
            foreach ($joins as $join) {
                if ($join['tableAlias'] == $table) {
                    return $join['tableModel'];
                }
            }
        }
    }
} 
  |