<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think\Model;

use Think\Model;

/**
 * ThinkPHP视图模型扩展
 */
class ViewModel extends Model
{

    protected $viewFields = array();

    /**
     * 自动检测数据表信息
     * @access protected
     * @return void
     */
    protected function _checkTableInfo()
    {}

    /**
     * 得到完整的数据表名
     * @access public
     * @return string
     */
    public function getTableName()
    {
        if (empty($this->trueTableName)) {
            $tableName = '';
            foreach ($this->viewFields as $key => $view) {
                // 获取数据表名称
                if (isset($view['_table'])) {
                    // 2011/10/17 添加实际表名定义支持 可以实现同一个表的视图
                    $tableName .= $view['_table'];
                    $prefix    = $this->tablePrefix;
                    $tableName = preg_replace_callback("/__([A-Z_-]+)__/sU", function ($match) use ($prefix) {return $prefix . strtolower($match[1]);}, $tableName);
                } else {
                    $class = parse_res_name($key, C('DEFAULT_M_LAYER'));
                    $Model = class_exists($class) ? new $class() : M($key);
                    $tableName .= $Model->getTableName();
                }
                // 表别名定义
                $tableName .= !empty($view['_as']) ? ' ' . $view['_as'] : ' ' . $key;
                // 支持ON 条件定义
                $tableName .= !empty($view['_on']) ? ' ON ' . $view['_on'] : '';
                // 指定JOIN类型 例如 RIGHT INNER LEFT 下一个表有效
                $type = !empty($view['_type']) ? $view['_type'] : '';
                $tableName .= ' ' . strtoupper($type) . ' JOIN ';
                $len = strlen($type . '_JOIN ');
            }
            $tableName           = substr($tableName, 0, -$len);
            $this->trueTableName = $tableName;
        }
        return $this->trueTableName;
    }

    /**
     * 表达式过滤方法
     * @access protected
     * @param string $options 表达式
     * @return void
     */
    protected function _options_filter(&$options)
    {
        if (isset($options['field'])) {
            $options['field'] = $this->checkFields($options['field']);
        } else {
            $options['field'] = $this->checkFields();
        }

        if (isset($options['group'])) {
            $options['group'] = $this->checkGroup($options['group']);
        }

        if (isset($options['where'])) {
            $options['where'] = $this->checkCondition($options['where']);
        }

        if (isset($options['order'])) {
            $options['order'] = $this->checkOrder($options['order']);
        }

    }

    /**
     * 检查是否定义了所有字段
     * @access protected
     * @param string $name 模型名称
     * @param array $fields 字段数组
     * @return array
     */
    private function _checkFields($name, $fields)
    {
        if (false !== $pos = array_search('*', $fields)) {
            // 定义所有字段
            $fields = array_merge($fields, M($name)->getDbFields());
            unset($fields[$pos]);
        }
        return $fields;
    }

    /**
     * 检查条件中的视图字段
     * @param $where 条件表达式
     * @return array
     */
    protected function checkCondition($where)
    {
        if (is_array($where)) {
            $fields = $field_map_table = array();
            foreach ($this->viewFields as $key => $val) {
                $table_alias = isset($val['_as']) ? $val['_as'] : $key;
                $val         = $this->_checkFields($key, $val);
                foreach ($val as $as_name => $v) {
                    if (is_numeric($as_name)) {
                        $fields[]          = $v; //所有表字段集合
                        $field_map_table[] = $table_alias; //所有表字段对应表名集合
                    } else {
                        $fields[$as_name]          = $v;
                        $field_map_table[$as_name] = $table_alias;
                    }
                }
            }
            $where = $this->_parseWhere($where, $fields, $field_map_table);
        }

        return $where;
    }

    /**
     * 解析where表达式
     * @param $where
     * @param $fields
     * @param $field_map_table
     * @return array
     */
    private function _parseWhere($where, $fields, $field_map_table)
    {
        $view = array();
        foreach ($where as $name => $val) {
            if ('_complex' == $name) {
                //复合查询
                foreach ($val as $k => $v) {
                    if (false === strpos(substr($k, 0, 1), '_')) {
                        if (false !== $field = array_search($k, $fields, true)) { // 存在视图字段
                            $k = is_numeric($field) ? $field_map_table[$field] . '.' . $k : $field_map_table[$field] . '.' . $field; //字段别名
                        }
                    } else if (is_array($v)) {
                        //数组复合查询
                        $v = $this->_parseWhere($val[$k], $fields, $field_map_table);
                    }
                    $view[$name][$k] = $v;
                }
            } else {
                if (strpos($name, '|')) {
                    //name|title快捷查询
                    $arr = explode('|', $name);
                    foreach ($arr as $k => $v) {
                        if (false !== $field = array_search($v, $fields, true)) {
                            $arr[$k] = is_numeric($field) ? $field_map_table[$field] . '.' . $v : $field_map_table[$field] . '.' . $field;
                        }
                    }
                    $view[implode('|', $arr)] = $val;
                } else if (strpos($name, '&')) {
                    //name&title快捷查询
                    $arr = explode('&', $name);
                    foreach ($arr as $k => $v) {
                        if (false !== $field = array_search($v, $fields, true)) {
                            $arr[$k] = is_numeric($field) ? $field_map_table[$field] . '.' . $v : $field_map_table[$field] . '.' . $field;
                        }
                    }
                    $view[implode('&', $arr)] = $val;
                } else {
                    if (false !== $field = array_search($name, $fields, true)) {
                        $name = is_numeric($field) ? $field_map_table[$field] . '.' . $name : $field_map_table[$field] . '.' . $field;
                    }
                    $view[$name] = $val;
                }
            }
        }

        return $view;
    }

    /**
     * 检查Order表达式中的视图字段
     * @access protected
     * @param string $order 字段
     * @return string
     */
    protected function checkOrder($order = '')
    {
        if (is_string($order) && !empty($order)) {
            $orders = explode(',', $order);
            $_order = array();
            foreach ($orders as $order) {
                $array = explode(' ', trim($order));
                $field = $array[0];
                $sort  = isset($array[1]) ? $array[1] : 'ASC';
                // 解析成视图字段
                foreach ($this->viewFields as $name => $val) {
                    $k   = isset($val['_as']) ? $val['_as'] : $name;
                    $val = $this->_checkFields($name, $val);
                    if (false !== $_field = array_search($field, $val, true)) {
                        // 存在视图字段
                        $field = is_numeric($_field) ? $k . '.' . $field : $k . '.' . $_field;
                        break;
                    }
                }
                $_order[] = $field . ' ' . $sort;
            }
            $order = implode(',', $_order);
        }
        return $order;
    }

    /**
     * 检查Group表达式中的视图字段
     * @access protected
     * @param string $group 字段
     * @return string
     */
    protected function checkGroup($group = '')
    {
        if (!empty($group)) {
            $groups = explode(',', $group);
            $_group = array();
            foreach ($groups as $field) {
                // 解析成视图字段
                foreach ($this->viewFields as $name => $val) {
                    $k   = isset($val['_as']) ? $val['_as'] : $name;
                    $val = $this->_checkFields($name, $val);
                    if (false !== $_field = array_search($field, $val, true)) {
                        // 存在视图字段
                        $field = is_numeric($_field) ? $k . '.' . $field : $k . '.' . $_field;
                        break;
                    }
                }
                $_group[] = $field;
            }
            $group = implode(',', $_group);
        }
        return $group;
    }

    /**
     * 检查fields表达式中的视图字段
     * @access protected
     * @param string $fields 字段
     * @return string
     */
    protected function checkFields($fields = '')
    {
        if (empty($fields) || '*' == $fields) {
            // 获取全部视图字段
            $fields = array();
            foreach ($this->viewFields as $name => $val) {
                $k   = isset($val['_as']) ? $val['_as'] : $name;
                $val = $this->_checkFields($name, $val);
                foreach ($val as $key => $field) {
                    if (is_numeric($key)) {
                        $fields[] = $k . '.' . $field . ' AS ' . $field;
                    } elseif ('_' != substr($key, 0, 1)) {
                        // 以_开头的为特殊定义
                        if (false !== strpos($key, '*') || false !== strpos($key, '(') || false !== strpos($key, '.')) {
                            //如果包含* 或者 使用了sql方法 则不再添加前面的表名
                            $fields[] = $key . ' AS ' . $field;
                        } else {
                            $fields[] = $k . '.' . $key . ' AS ' . $field;
                        }
                    }
                }
            }
            $fields = implode(',', $fields);
        } else {
            if (!is_array($fields)) {
                $fields = explode(',', $fields);
            }

            // 解析成视图字段
            $array = array();
            foreach ($fields as $key => $field) {
                if (strpos($field, '(') || strpos(strtolower($field), ' as ')) {
                    // 使用了函数或者别名
                    $array[] = $field;
                    unset($fields[$key]);
                }
            }
            foreach ($this->viewFields as $name => $val) {
                $k   = isset($val['_as']) ? $val['_as'] : $name;
                $val = $this->_checkFields($name, $val);
                foreach ($fields as $key => $field) {
                    if (false !== $_field = array_search($field, $val, true)) {
                        // 存在视图字段
                        if (is_numeric($_field)) {
                            $array[] = $k . '.' . $field . ' AS ' . $field;
                        } elseif ('_' != substr($_field, 0, 1)) {
                            if (false !== strpos($_field, '*') || false !== strpos($_field, '(') || false !== strpos($_field, '.'))
                            //如果包含* 或者 使用了sql方法 则不再添加前面的表名
                            {
                                $array[] = $_field . ' AS ' . $field;
                            } else {
                                $array[] = $k . '.' . $_field . ' AS ' . $field;
                            }

                        }
                    }
                }
            }
            $fields = implode(',', $array);
        }
        return $fields;
    }
}