IcePHP框架中的快速后台中的通用CRUD功能框架(六) SCrudField 字段类

<?php
/**
 * CRUD字段类
 * @author bluehire
 *
 */
class SCrudField extends SCrudSub {
    // 以下属性来源数据库(配置文件,config/crud/*.config.php)
    public $name; // 字段名称
    private $scale; // 精度
    private $type; // 类型完整
    private $maxLength; // 最大长度
    public $simpleType; // 简单类型CILNDTXBR
    private $notNull; // 不允许空
    public $primaryKey; // 是否主键
    private $autoIncrement; // 是否自增长
    private $binary; // 是否二进制
    private $unsigned; // 无符号
    private $hasDefault; // 有否默认值
    public $defaultValue; // 默认值
    public $description; // 字段备注
    
    // 重新计算的默认值,可修改
    public $title; // 字段标题
    
    //以下属性,均为Boolean,可设置    
    public $isPassword; // 是否密码字段
    public $isAbandon; // 是否被放弃    
    public $inGrid; // 是否参与列表
    public $inInsert; // 是否参与创建
    public $inUpdate; // 是否参与修改
    public $inView; // 是否参与查看
    public $inSort; // 是否参与排序
    public $isCreated; // 是否创建时间字段
    public $isUpdated; // 是否修改时间字段
    
    public $showType;    //字段的CRUD类型 Text/Image/Date/Time/String
    public $updateType;    //字段的CRUD类型 Text/Image/Date/Time/String
    public $searchType;    //搜索类型 LIKE/EQUAL/DATE/TIME/RANGE/DATERANGE/CHECK/RADIO/TREE/LIST
    
    public $enum; //本字段是枚举,在此设置字段 存储值与显示值的对应
    public $foreignKey; //本字段是另一表的外键,在此设置主表名及主表字段名
    
    public $regular; //用于前后端验证的正则表达式

    public $width = false;    //图片的宽度设置
    public $height = false;    //图片的高度设置
    public $style = false;    //图片样式设置
    public $css = false;    //设置图片的样式 类
    public $alt = false;    //设置图片的替换文字
    public $format; // 格式
    
    public $searchDefault; //搜索条件的默认值
    public $searchMax; //搜索范围的上限
    public $searchMin; //搜索范围的下限
    
    private $config;    // 保存原始配置
    
    /**
     * @param SCrud $father                 主CRUD对象
     * @param array $c                 数据库配置
     */
    public function __construct(SCrud $father, array $c) {
        $this->crud = $father;
        $this->config = $c;
        
        //所有配置值记录到本对象的属性中
        foreach($c as $k=>$v){
            $this->$k=$v;
        }
        
        //处理一些属性的默认值
        $t=$c[‘simpleType‘];
        $n=$c[‘name‘];
        
        $default = array (
                ‘title‘ => $c [‘description‘] ?  : $n,    //标题的默认值: 备注/字段名
                ‘inSort‘ => strpos ( ‘>CIRNDT‘, $t ), //是否参与排序: C/I/N/D/T                
                ‘inGrid‘ => strpos ( ‘>CIRLNDT‘, $t ), //是否参与列表
                ‘inInsert‘ => strpos ( ‘>CILNDTX‘, $t ) and ! $c [‘primaryKey‘], //是否参与创建
                ‘inUpdate‘ => strpos ( ‘>CILNDTX‘, $t ) and ! $c [‘primaryKey‘], //是否参与编辑
                ‘inView‘ => strpos ( ‘>CIRLNDTX‘, $t ), //是否参与显示
                ‘isCreated‘ => strpos ( ‘>CIDT‘, $t ) and ($n == ‘created‘ or $n == ‘create_time‘), // 是否是创建时间字段
                ‘isUpdated‘ => strpos ( ‘>CIDT‘, $t ) and ($n == ‘updated‘ or $n == ‘update_time‘),  //是否是修改时间字段
        );
        
        foreach ( $default as $k => $v ) {
            if (! isset ( $c [$k] )) {
                $this->$k = $v;
            }
        }
        
        //设置字段的默认CRUD类型
        switch($t){
            //日期
            case ‘D‘:
                $this->showType=‘Date‘;
                $this->updateType=‘Date‘;
                $this->searchType=‘DateRange‘;
                break;
            //时间
            case ‘T‘:
                $this->showType=‘Time‘;
                $this->updateType=‘Time‘;
                $this->searchType=‘DateRange‘;
                break;
            //大文本
            case ‘X‘:
                $this->showType=‘String‘;
                $this->updateType=‘Text‘;
                $this->searchType=null;
                break;
            //字符串
            case ‘C‘:
                $this->showType=‘String‘;
                $this->updateType=‘String‘;
                $this->searchType=‘Like‘;
                break;
            //逻辑
            case ‘L‘:
                $this->showType=‘String‘;
                $this->updateType=‘Radio‘;
                $this->searchType=‘List‘;
                $this->enum=array(‘0‘=>‘否‘,‘1‘=>‘是‘);
                break;
            //整数
            case ‘I‘:
                $this->showType=‘String‘;
                $this->updateType=‘String‘;
                $this->searchType=‘Range‘;
                break;
            //自增长 整数
            case ‘R‘:
                $this->showType=‘String‘;
                $this->updateType=‘String‘;
                $this->searchType=‘Equal‘;
                break;
            //浮点
            case ‘N‘:
                $this->showType=‘String‘;
                $this->updateType=‘String‘;
                $this->searchType=‘Range‘;
                break;
            default:
                $this->showType=‘String‘;
                $this->updateType=‘String‘;
                $this->searchType=null;
        }        
    }
    
    /**
     * 在使用前,对字段再进行一次处理
     */
    public function process() {
        // 将外键处理成枚举
        if ($this->foreignKey) {
            $fk = $this->foreignKey;
            $t = table ( $fk [‘table‘] );
            if (isset ( $fk [‘where‘] )) {
                $t = $t->where ( $fk [‘where‘] );
            }
            if (isset ( $fk [‘orderby‘] )) {
                $t = $t->orderby ( $fk [‘orderby‘] );
            }
            $this->enum = $t->col ( $fk [‘field‘] );
        }
        
        //密码不参与搜索,修改/创建时,按密码显示
        if ($this->isPassword) {
            $this->searchType = null;
            $this->updateType = ‘Password‘;
        }
    }
    
    /**
     * 判断本字段是否可排序
     * @return boolean
     */
    public function isSortable(){
        if($this->isAbandon or $this->simpleType==‘X‘ or $this->simpleType==‘B‘ or $this->simpleType==‘L‘ or $this->isPassword or !$this->inGrid){
            return false;
        }
    
        return $this->inSort;
    }
    
    /**
     * 判断本字段是否参与创建
     * @return boolean
     */
    public function isInsertable(){
        if($this->isAbandon or $this->simpleType==‘B‘ or $this->isCreated or $this->isUpdated or $this->autoIncrement or $this->primaryKey){
            return false;
        }
        return $this->inInsert;
    }
    
    /**
     * 判断本字段是否参与编辑
     * @return boolean
     */
    public function isUpdatable(){
        if($this->isAbandon or $this->simpleType==‘B‘ or $this->isCreated or $this->isUpdated or $this->autoIncrement or $this->primaryKey){
            return false;
        }
        return $this->inInsert;
    }
    
    /**
     * 判断本字段是否参与列表显示
     * @return boolean
     */
    public function isGridable(){
        if($this->isAbandon or $this->simpleType==‘X‘ or $this->simpleType==‘B‘ or $this->isPassword){
            return false;
        }
        
        if($this->primaryKey or $this->isSortable()){
            return true;
        }
        
        return $this->inGrid;
    }
    
    /**
     * 判断本字段是否参与查看
     * @return boolean
     */
    public function isViewable(){
        if($this->isAbandon or $this->simpleType==‘B‘ or $this->isPassword){
            return false;
        }
        if($this->primaryKey){
            return true;
        }
        return $this->inView;
    }
    
    /**
     * 保存解码函数
     * @var Closure
     */
    public $decode;
    
    /**
     * 设置解码函数/解码
     * @param  Closure|mixed $decode
     * @return SCrudField
     */
    public function decode($decode) {
        if ($decode instanceof Closure) {
            //设置解码函数
            $this->decode = $decode;
            return $this;
        } else {
            //具体 解码
            $closure = $this->decode;
            return $closure ( $decode );
        }
    }
    
    /**
     * 保存编码函数
     * @var Closure
     */
    public $encode;
    
    /**
     * 设置编码函数
     * @param Closure|mixed $encode
     * @return SCrudField
     */
    public function encode($encode) {        
        if ($encode instanceof Closure) {
            //设置编码函数
            $this->encode = $encode;
            return $this;
        } else {
            //具体 编码
            $closure = $this->encode;
            return $closure ( $encode );
        }
    }
    
    /**
     * 在列表/查看显示本字段
     *
     * @param $value 字段值            
     * @return string
     */
    public function show($value) {
        //枚举,按表现值显示
        if ($this->enum) {
            $value = $this->enum [$value];
        }
        
        switch ($this->showType) {
            case ‘Image‘ :
                return $this->crud->display ( ‘grid_image‘, array (
                        ‘src‘ => $value,
                        ‘width‘ => $this->width,
                        ‘height‘ => $this->height,
                        ‘style‘ => $this->style,
                        ‘css‘ => $this->css,
                        ‘alt‘ => $this->alt
                ) );
            case ‘Time‘ :
                $format = $this->format ?  : ‘Y-m-d H:i:s‘;
                return date ( $format, $value );
            case ‘Date‘ :
                $format = $this->format ?  : ‘Y-m-d‘;
                return date ( $format, $value );
            case ‘Text‘ :
                return $this->showString ( $value );
            default :
                if ($this->decode) {
                    return $this->decode ( $value );
                }
                return $value;
        }
    }
    
    /**
     * 在创建/编辑 时显示
     * @param string $default
     */
    public function showUpdate($v=‘‘){    
        $tpl = ‘update_‘ . strtolower ( $this->updateType );
        $this->crud->display ( $tpl, array (
                ‘field‘ => $this,
                ‘value‘=>$v
        ) );
    }
    
    /**
     * 判断是否参与搜索
     * @return boolean
     */
    public function isSearchable(){
        if($this->isAbandon or !$this->searchType or $this->isPassword){
            return false;
        }
        return true;
    }
    
    /**
     * 显示一个搜索条件
     * @param string $default
     */
    public function showSearch() {
        if(!$this->isSearchable()){
            return;
        }
        
        //如果是枚举,增加一个 不限制 的参数
        if($this->enum){
            $enum=array_merge(array(null=>‘不限制‘),$this->enum);
        }else{
            $enum=null;
        }
        
        return $this->crud->display ( ‘search_‘ . strtolower ( $this->searchType ), array (
                ‘title‘ => $this->title,
                ‘name‘ => ‘crud_‘ . $this->name,
                ‘default‘ => $this->searchDefault,
                ‘min‘ => $this->searchMin,
                ‘max‘ => $this->searchMax,
                ‘enum‘=>$enum,                
        ) );
    }
    
    /**
     * 判断是否有不允许的字符
     * @param unknown $v
     * @param unknown $chars
     * @return boolean
     */
    private function antiInject($v,$chars){
        for($i=0;$i<strlen($chars);$i++){
            if(strpos($v,$chars[$i])!==false)
                return false;
        }
        return true;
    }
    
    /**
     * 构造 模糊匹配的查询条件
     * @param SRequest $req
     * @return boolean|string
     */
    private function whereLike(SRequest $req){
        //请求参数名
        $name=‘crud_‘.$this->name;
        
        //如果不存在此请求参数
        if(!$req->exist($name)){
            return false;
        }
        
        //如果请求参数为空
        $v=trim($req->$name);
        if(!$v){
            return false;
        }
        
        //如果请求参数中有非法字符
        if(!$this->antiInject($v, ‘\‘"\\%_‘)){
            return false;
        }
        
        //返回条件
        return ‘`‘.$this->name.‘` like "%‘.$v.‘%"‘;
    }
    
    /**
     * 构造 精确匹配的查询条件
     * @param SRequest $req
     * @return boolean|multitype:string
     */
    private function whereEqual(SRequest $req){
        //请求参数名
        $name=‘crud_‘.$this->name;
    
        //如果不存在此请求参数
        if(!$req->exist($name)){
            return false;
        }
    
        //如果请求参数为空
        $v=trim($req->$name);
        
        if(!strlen($v)){
            return false;
        }
    
        //如果请求参数中有非法字符
        if(!$this->antiInject($v, ‘\‘"\\‘)){
            return false;
        }
    
        //对参数进行标准化
        switch($this->simpleType){
            case ‘I‘:
                return array($this->name=>intval($v));
            case ‘L‘:
                return array($this->name=>($v==‘1‘ or strtolower($v)==‘true‘)?1:0);
            case ‘N‘:
                return array($this->name=>floatval($v));
            case ‘D‘:
                $p=strtotime($v);
                if(!$p){
                    return false;
                }
                return array($this->name=>date(‘Y-m-d‘,$p));
            case ‘T‘:
                $t=strtotime($v);
                if(!$t){
                    return false;
                }
                return array($this->name=>date(‘Y-m-d H:i:s‘,$t));
        }
    
        //返回条件
        return array($this->name=>$v);
    }
    
    /**
     * 构造日期匹配的搜索条件
     * @param SRequest $req
     * @return boolean|multitype:Ambigous <number, string>
     */
    private function whereDate(SRequest $req){
        //请求参数名
        $name=‘crud_‘.$this->name;
    
        //如果不存在此请求参数
        if(!$req->exist($name)){
            return false;
        }
    
        //如果请求参数为空
        $v=trim($req->$name);
        if(!$v){
            return false;
        }
    
        //如果请求参数中有非法字符
        if(!$this->antiInject($v, ‘\‘"\\‘)){
            return false;
        }
    
        //如果无法按日期解析
        $v=strtotime($v);
        if($v){
            return false;
        }
    
        //对参数进行标准化
        switch($this->simpleType){
            case ‘C‘:
            case ‘D‘:
                return array($this->name=>date(‘Y-m-d‘,$v));
            case ‘T‘:
                return array($this->name=>date(‘Y-m-d H:i:s‘,$v));
        }
    
        //返回条件
        return array($this->name=>$v);
    }
    
    /**
     * 从请求参数中获取一个日期范围边界参数
     * @param unknown $name
     * @param SRequest $req
     * @return boolean|string|number|Ambigous <string, number>
     */
    private function whereOne($name, SRequest $req) {
        // 如果不存在此请求参数
        if (! $req->exist ( $name )) {
            return false;
        }
    
        // 如果请求参数为空
        $v = trim ( $req->$name );
        if (! $v) {
            return false;
        }
    
        // 如果请求参数中有非法字符
        if (! $this->antiInject ( $v, ‘\‘"\\‘ )) {
            return false;
        }
    
        // 对参数进行标准化
        switch ($this->simpleType) {
            case ‘C‘ :
                return $v;
            case ‘I‘ :
            case ‘R‘:
                return intval ( $v );
            case ‘N‘ :
                return floatval ( $v );
            case ‘D‘ :
                // 如果无法按日期解析
                $v = strtotime ( $v );
                if ($v) {
                    return false;
                }
                return date ( ‘Y-m-d‘, $v );
            case ‘T‘ :
                // 如果无法按日期解析
                $v = strtotime ( $v );
                if ($v) {
                    return false;
                }
                return date ( ‘Y-m-d H:i:s‘, $v );
        }
    
        return $v;
    }
    
    /**
     * 根据请求参数创建搜索条件
     */
    private function whereRange(SRequest $req){
        //请求参数名
        $name=‘crud_‘.$this->name;
    
        //取边界值
        $min=$this->whereOne($name.‘_min‘,$req);
        $max=$this->whereOne($name.‘_max‘,$req);
    
        if(!$min and !$max){
            return false;
        }
    
        if(!$max){
            return ‘`‘.$this->name.‘`>="‘.$min.‘"‘;
        }
    
        if(!$min){
            return ‘`‘.$this->name.‘`<="‘.$max.‘"‘;
        }
    
        //返回条件
        return ‘`‘.$this->name.‘` BETWEEN "‘.$min.‘" AND "‘.$max.‘"‘;
    }
    
    /**
     * 构造日期范围的查询条件
     * @param SRequest $req
     * @return boolean|string
     */
    private function whereDateRange(SRequest $req){
        //请求参数名
        $name=‘crud_‘.$this->name;
        
        //计算边界值
        $min=$this->whereOne($name.‘_min‘,$req);
        $max=$this->whereOne($name.‘_max‘,$req);
    
        if(!$min and !$max){
            return false;
        }
    
        if(!$max){
            return ‘`‘.$this->name.‘`>="‘.$min.‘"‘;
        }
    
        if(!$min){
            return ‘`‘.$this->name.‘`<="‘.$max.‘"‘;
        }
    
        //返回条件
        return ‘`‘.$this->name.‘` BETWEEN "‘.$min.‘" AND "‘.$max.‘"‘;
    }
    
    private function whereTime(SRequest $req){
        //@todo:时间匹配的查询条件
    }
    
    /**
     * 构造 单选搜索的查询条件
     * @param SRequest $req
     * @return boolean|multitype:Ambigous <boolean, string>
     */
    private function whereRadio(SRequest $req){
        //请求参数名
        $name=‘crud_‘.$this->name;
    
        //如果不存在此请求参数
        if(!$req->exist($name)){
            return false;
        }
    
        //如果请求参数为空
        $v=trim($req->$name);
        if(!$v){
            return false;
        }
    
        //如果请求参数中有非法字符
        if(!$this->antiInject($v, ‘\‘"\\‘)){
            return false;
        }
    
        //对参数进行标准化
        switch($this->simpleType){
            case ‘I‘:
            case ‘R‘:
                return array($this->name=>intval($v));
            case ‘L‘:
                return array($this->name=>( $v==‘1‘ or strtolower($v)==‘true‘));
        }
    
        //返回条件
        return array($this->name=>$v);
    }
    
    /**
     * 根据用户请求构造多选搜索的查询条件
     * @param SRequest $req
     * @return boolean|multitype:Ambigous <boolean, string>
     */
    private function whereCheck(SRequest $req){
        //请求参数名
        $name=‘crud_‘.$this->name;
    
        //如果不存在此请求参数
        if(!$req->exist($name)){
            return false;
        }
    
        //如果请求参数为空
        $v=trim($req->$name);
        if(!$v){
            return false;
        }
    
        //如果请求参数中有非法字符
        if(!$this->antiInject($v, ‘\‘"\\‘)){
            return false;
        }
    
        //对参数进行标准化
        switch($this->simpleType){
            case ‘I‘:
            case ‘R‘:
                return array($this->name=>intval($v));
                break;
            case ‘L‘:
                return array($this->name=>( $v==‘1‘ or strtolower($v)==‘true‘));
        }
    
        //返回条件
        return array($this->name=>$v);
    }
    
    /**
     * 根据用户请求参数,构造 查询条件
     *
     * @param SRequest $req            
     * @throws Exception
     * @return Ambigous <boolean, string>|Ambigous <boolean, multitype:string >|Ambigous <boolean, multitype:Ambigous <number, string> >|Ambigous <boolean, multitype:Ambigous <boolean, string> >
     */
    public function where(SRequest $req) {
        switch ($this->searchType) {
            case ‘Like‘ :
                return $this->whereLike ( $req );
            case ‘Equal‘ :
                return $this->whereEqual ( $req );
            case ‘Date‘ :
                return $this->whereDate ( $req );
            case ‘Time‘ :
                return $this->whereTime ( $req );
            case ‘List‘ :
                return $this->whereEqual( $req );
            case ‘Tree‘ :
                return $this->whereEqual ( $req );
            case ‘Radio‘ :
                return $this->whereRadio ( $req );
            case ‘Check‘ :
                return $this->whereCheck ( $req );
            case ‘Range‘ :
                return $this->whereRange ( $req );
            case ‘DateRange‘ :
                return $this->whereDateRange ( $req );
        }
        throw new Exception ( ‘程序流程不应该到达这里‘ );
    }
}

IcePHP框架中的快速后台中的通用CRUD功能框架(六) SCrudField 字段类

上一篇:IcePHP框架中的快速后台中的通用CRUD功能框架(五) SCrud 主控类


下一篇:微服务实战之春云与刀客(一)—— 微服务开篇