MixPHP_数据库操作基类

一、前言

因为使用习惯了TP和Laravel的数据库操作方式,所以又重新在代码写SQL操作会不太习惯,且代码看着也不直观,于是根据MixPHP的数据库操作方式进行了二次封装。

二、数据库操作基础类

<?php

namespace apps\common\models\database;

use mix\facades\PDO;

abstract class BaseModel
{
	protected $table;			// 当前执行数据表名称
	protected $field = '';		// 当前查询的字段
	protected $join	 = '';		// 关联sql语句
	protected $where = '';		// 条件sql语句
	protected $order = '';		// 排序sql语句
        protected $group = '';      // 分组sql语句
        protected $having = '';     // 分组条件sql语句
	protected $limit = '';		// 分页sql语句
	protected $param = [];		// 数据绑定数组

	public function __construct()
	{
		$this->setTable();
	}

	private function setTable()
	{
		$this->table = $this->table();
	}

	public abstract function table();

	public function field(string $field)
	{
		$this->field = $field;

		return $this;
	}

	/**
     *  组建关连表查询片段sql语句
     *  @param string $relaTabel 关联表名称
     *  @param string $param     关联表关联字段    
     *  @param string $relaParam 当前表外键字段
     *  @param string $alias     连表别名
     *  @param string $joinType  连表连表查询类别
     */
	public function join(string $relaTable, string $param, string $relaParam, string $alias = '', string $type = 'LEFT')
    {
        $relaTable = $alias ? "`{$relaTable}` AS `{$alias}`" : "`{$relaTable}`";
        $param = $alias ? "`{$alias}`.{$param}" : "{$relaTable}.{$param}";

        if ($this->join) {
        	$this->join .= " {$type} JOIN {$relaTable} ON {$param} = `" . $this->table . "`.{$relaParam}";
        } else {
        	$this->join = " {$type} JOIN {$relaTable} ON {$param} = `" . $this->table . "`.{$relaParam}";
        }

        return $this;
    }

    public function where(array $where)
    {
        if (empty($where)) return $this;
        
        $map = [];
        $param = [];
		
		// 判断1维 or 2维数组
    	if (count($where) == count($where, 1)) {
    		foreach ($where as $k => $v) {
	            $map[] = "`" . $this->table . "`.`{$k}` = :{$k}";
	        }
	        $param = $where;
    	} else {
    		foreach ($where as $k => $v) {
	            $map[] = "`" . $this->table . "`.`{$v[0]}` {$v[1]} :{$v[0]}";
	            $param[$v[0]] = $v[2];
	        }
    	}

    	if ($this->where) {
    		$this->where .=  ' AND ' . implode(' AND ', $map);
    	} else {
    		$this->where = " WHERE " . implode(' AND ', $map);
    	}

    	$this->param = array_merge($this->param, $param);

        return $this;
    }

    public function orWhere(array $where)
    {
    	if ($this->where)
    	app()->dump("orWhere方法必须在where方法前调用!", true);
		
		if (empty($where)) return $this;
		
    	$map = [];
        $param = [];

    	// 判断1维 or 2维数组
    	if (count($where) == count($where, 1)) {
    		foreach ($where as $k => $v) {
	            $map[] = "`" . $this->table . "`.`{$k}` = :{$k}";
	        }
	        $param = $where;
    	} else {
    		foreach ($where as $k => $v) {
	            $map[] = "`" . $this->table . "`.`{$v[0]}` {$v[1]} :{$v[0]}";
	            $param[$v[0]] = $v[2];
	        }
    	}

    	$this->where = ' WHERE (' . implode(' OR ', $map) . ')';
    	$this->param = array_merge($this->param, $param);

        return $this;
    }

    /**
     * $where :
            ['register_time', '1', '12121212']

            or

            [ 
                ['register_time', '1', '12121212'], 
                ['register_time', '1', '12121212']
            ]
     */
    public function betweenWhere(array $where)
    {
        if (empty($where)) return $this;
        
        $map = [];
        $param = [];
        
        // 判断1维 or 2维数组
        if (count($where) == count($where, 1)) {
            $map[] = "`" . $this->table . "`.`{$where[0]}` BETWEEN :{$where[0]}1 AND :{$where[0]}2";
            $param = ["{$where[0]}1"=>$where[1], "{$where[0]}2"=>$where[2]];
        } else {
            foreach ($where as $k => $v) {
                $map[] = "`" . $this->table . "`.`{$v[0]}` BETWEEN :{$v[0]}1 AND :{$v[0]}2";
                $param["{$v[0]}1"] = $v[1];
                $param["{$v[0]}2"] = $v[2];
            }
        }

        if ($this->where) {
            $this->where .=  ' AND ' . implode(' AND ', $map);
        } else {
            $this->where = " WHERE " . implode(' AND ', $map);
        }

        $this->param = array_merge($this->param, $param);

        return $this;
    }

    public function whereIn(string $column, array $arr, $status = 'AND')
    {
        $this->where = $this->where ? " {$status} " : " WHERE ";
        $this->where .= "`" . $this->table . "`.`{$column}` IN (" . implode(',', $arr) . ")";

        return $this;
    }

    public function order(string $order)
    {
        $this->order = " ORDER BY " . $order;

        return $this;
    }
    
    public function group(string $group)
    {
        $this->group = " GROUP BY " . $group;

        return $this;
    }

    public function having(string $having)
    {
        $this->having = " HAVING " . $having;

        return $this;
    }

    public function page(int $page, int $perPage, int $status = 1)
    {
		$this->limit = ' LIMIT :offset,:rows';
		$this->param = array_merge($this->param, ['offset'=>(($page-1) * $perPage), 'rows'=>$perPage]);

		return $this->select($status);
    }

    public function selectSql()
    {
    	$sql = "SELECT ";
    	$sql .= $this->field ? $this->field : '*';
    	$sql .= ' FROM `' . $this->table . '`';
    	$sql .= $this->join ? $this->join : '';
    	$sql .= $this->where ? $this->where : '';
        $sql .= $this->group ? $this->group : '';
        $sql .= $this->having ? $this->having : '';
    	$sql .= $this->order ? $this->order : '';
    	$sql .= $this->limit ? $this->limit : '';

    	return $sql;
    }	

    public function select($status = 1)
    {
    	$list = PDO::createCommand($this->selectSql())->bindParams($this->param)->queryAll();

    	if ($status) $this->clear();

    	return $list;
    }

    public function find(int $id = 0)
    {
    	if ($id) $this->where(['id'=>$id]);

    	$info = PDO::createCommand($this->selectSql())->bindParams($this->param)->queryOne();

        $this->clear();

        return $info;
    }

    public function value($field, $id = 0)
    {
    	if ($id) $this->where(['id'=>$id]);
    	
        $this->field("`{$field}`");

    	$info = PDO::createCommand($this->selectSql())->bindParams($this->param)->queryOne();

        $this->clear();
    	
    	return $info[$field];
    }

    public function count()
    {
        $sql = 'SELECT COUNT(*) FROM `' . $this->table . '`';
        $sql .= $this->where ? $this->where : '';
        $sql .= $this->group ? $this->group : '';

        if(isset($this->param['rows'])) unset($this->param['rows']);
        if(isset($this->param['offset'])) unset($this->param['offset']);

        return PDO::createCommand($sql)->bindParams($this->param)->queryScalar();
    }

    public function insert(array $data)
   	{
   		PDO::insert($this->table, $data)->execute();

    	return PDO::getLastInsertId();
   	}
	
   	public function insertAll(array $data)
    {
    	PDO::batchInsert($this->table, $data)->execute();

		return PDO::getRowCount();
    }

   	public function update(array $data, array $where, string $method = 'AND')
   	{
   		PDO::update($this->table, $data, $where, $method)->execute();

		return PDO::getRowCount();
   	}

   	public function delete(array $where)
    {
    	PDO::delete($this->table, $where)->execute();

		return PDO::getRowCount();
    }

    public function getLastSql()
    {
        return PDO::getRawSql();
    }

    public function clear()
    {
    	$this->field = '';
        $this->join  = '';
        $this->where = '';
        $this->group = '';
        $this->having = '';
        $this->order = '';
        $this->limit = '';
        $this->param = [];
	
        return $this;
    }

}

三、数据库基类的使用示例

1. 创建数据库操作层

一般我们会设置一个数据库操作层,此处架构在common公共模块下的models下,在models下创建database文件夹,将我们上一步创建好的数据库操作基类放在这个文件夹下,如:

2. 创建具体的数据表模型,如用户数据表操作模型,还是在数据库模型文件夹下apps/common/models/database/UserModel.php:

<?php

namespace apps\common\models\database;

class UserModel extends BaseModel
{
    const TABLE = 'user';

    public function table()
    {
        return self::TABLE;
    }
    
}

3. 控制器内操作数据库

写接口的时候我们往往还需要一个验证层,这个MixPHP的作者也有考虑,每个应用中也对应有设计models文件夹对应验证模型层,而控制器中肯定会引入对应的验证器模型,且验证器中大多也需要操作数据库,所以数据库模型就直接选择在验证器模型中引入,具体操作如下:

(1)验证器,/apps/api/models/UserForm.php

<?php

namespace apps\api\models;

use mix\validators\Validator;
use apps\common\models\database\UserModel;

class UserForm extends Validator
{
    // 实例化数据库操作模型
    public function model()
    {
        return (new UserModel());
    }
    
    public function rules()
    {
        return [
            'id'    => ['call', 'callback' => [$this, 'mustBeId']],
            'email' => ['email', 'length' => 10, 'minLength' => 3, 'maxLength' => 50],
            'age'   => ['integer', 'unsigned' => true, 'min' => 1, 'max' => 120]
        ];
    }

    public function scenarios()
    {
        return [
            'index' => ['required' => ['id'], 'optional' => ['email', 'age']]
        ];
    }

    public function messages()
    {
        return [
            'id.callback'   => 'id参数错误'
        ];
    }
    
    // 自定义验证规则
    public function mustBeId($id)
    {
        if (!$id || !$this->model()->find($id))
        return false;

        return true;
    }

}

(2)控制器,/apps/api/controllers/UserController.php

<?php

namespace apps\api\controllers;

use mix\facades\Request;
use apps\api\models\UserForm;    // 引入用户验证模型类

class UserController extends BaseController
{
    protected $data;
    protected $model;
    protected $validate;

    public function onConstruct()
    {
        parent::onConstruct();
        
        // 实例化用户验证模型类
        $this->validate = new UserForm();
        // 实例化用户数据表操作模型类
        $this->model    = $this->validate->model();

        // 获取接口参数
        $this->data = Request::getRawBody();
    }

    protected function responseError($code, $msg)
    {
        return [
            'code' => $code,
            'msg'  => $msg
        ];
    }

    protected function responseOk($data = [])
    {
        return [
            'code' => 200,
            'msg'  => 'Ok',
            'data' => $data
        ];
    }
    
    /**
     *  接口参数验证公共方法
     *  @param $method 指定验证场景
     *  @return 返回所有获取的参数
     */
    protected function vali($method)
	{
        $data = $this->data == '' ? [] : json_decode($this->jsonData, true);

        if ($this->data && !$data)
        return $this->responseError(-1, 'json参数错误');

        $data = $data + Request::get() + Request::post() + Request::route();
		$this->validate->attributes = $data;
        
        if (!$this->validate->setScenario($method)->validate())
        return $this->responseError(-1, $this->validate->getError());

    	return $data;
	}


    public function actionIndex()
    {
        $data = $this->vali('index');

        if ($this->data && !$data)
        return $this->responseError(-1, 'json参数错误');

        $info = $this->model()->find($data['id']);

        return responseOk($info);
    }

}

注意:验证核心类 setScenario()函数需要修改,具体地址为:\vendor\mixstart\mixphp-framework\src\validators\Validator.php,修改如下:

    // 设置当前场景
    public function setScenario($scenario)
    {
        $scenarios = $this->scenarios();
        if (!isset($scenarios[$scenario])) {
            throw new \mix\exceptions\ValidatorException("场景不存在:{$scenario}");
        }
        if (!isset($scenarios[$scenario]['required'])) {
            throw new \mix\exceptions\ValidatorException("场景 {$scenario} 未定义 required 选项");
        }
        if (!isset($scenarios[$scenario]['optional'])) {
            $scenarios[$scenario]['optional'] = [];
        }
        $this->_scenario = $scenarios[$scenario];

        return $this;        // 添加返回当前对象
    }

到此,数据库操作生产流程基本完成,有疑问或者有优化建议的朋友欢迎留言告知。 

猜你喜欢

转载自blog.csdn.net/createNo_1/article/details/87542056