<?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 ( ‘程序流程不应该到达这里‘ );
}
}