123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- <?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 MergeModel extends Model {
- protected $modelList = array(); // 包含的模型列表 第一个必须是主表模型
- protected $masterModel = ''; // 主模型
- protected $joinType = 'INNER'; // 聚合模型的查询JOIN类型
- protected $fk = ''; // 外键名 默认为主表名_id
- protected $mapFields = array(); // 需要处理的模型映射字段,避免混淆 array( id => 'user.id' )
- /**
- * 架构函数
- * 取得DB类的实例对象 字段检查
- * @access public
- * @param string $name 模型名称
- * @param string $tablePrefix 表前缀
- * @param mixed $connection 数据库连接信息
- */
- public function __construct($name='',$tablePrefix='',$connection=''){
- parent::__construct($name,$tablePrefix,$connection);
- // 聚合模型的字段信息
- if(empty($this->fields) && !empty($this->modelList)){
- $fields = array();
- foreach($this->modelList as $model){
- // 获取模型的字段信息
- $result = $this->db->getFields(M($model)->getTableName());
- $_fields = array_keys($result);
- // $this->mapFields = array_intersect($fields,$_fields);
- $fields = array_merge($fields,$_fields);
- }
- $this->fields = $fields;
- }
- // 设置第一个模型为主表模型
- if(empty($this->masterModel) && !empty($this->modelList)){
- $this->masterModel = $this->modelList[0];
- }
- // 主表的主键名
- $this->pk = M($this->masterModel)->getPk();
- // 设置默认外键名 仅支持单一外键
- if(empty($this->fk)){
- $this->fk = strtolower($this->masterModel).'_id';
- }
- }
- /**
- * 得到完整的数据表名
- * @access public
- * @return string
- */
- public function getTableName() {
- if(empty($this->trueTableName)) {
- $tableName = array();
- $models = $this->modelList;
- foreach($models as $model){
- $tableName[] = M($model)->getTableName().' '.$model;
- }
- $this->trueTableName = implode(',',$tableName);
- }
- return $this->trueTableName;
- }
- /**
- * 自动检测数据表信息
- * @access protected
- * @return void
- */
- protected function _checkTableInfo() {}
- /**
- * 新增聚合数据
- * @access public
- * @param mixed $data 数据
- * @param array $options 表达式
- * @param boolean $replace 是否replace
- * @return mixed
- */
- public function add($data='',$options=array(),$replace=false){
- if(empty($data)) {
- // 没有传递数据,获取当前数据对象的值
- if(!empty($this->data)) {
- $data = $this->data;
- // 重置数据
- $this->data = array();
- }else{
- $this->error = L('_DATA_TYPE_INVALID_');
- return false;
- }
- }
- // 启动事务
- $this->startTrans();
- // 写入主表数据
- $result = M($this->masterModel)->strict(false)->add($data);
- if($result){
- // 写入外键数据
- $data[$this->fk] = $result;
- $models = $this->modelList;
- array_shift($models);
- // 写入附表数据
- foreach($models as $model){
- $res = M($model)->strict(false)->add($data);
- if(!$res){
- $this->rollback();
- return false;
- }
- }
- // 提交事务
- $this->commit();
- }else{
- $this->rollback();
- return false;
- }
- return $result;
- }
- /**
- * 对保存到数据库的数据进行处理
- * @access protected
- * @param mixed $data 要操作的数据
- * @return boolean
- */
- protected function _facade($data) {
- // 检查数据字段合法性
- if(!empty($this->fields)) {
- if(!empty($this->options['field'])) {
- $fields = $this->options['field'];
- unset($this->options['field']);
- if(is_string($fields)) {
- $fields = explode(',',$fields);
- }
- }else{
- $fields = $this->fields;
- }
- foreach ($data as $key=>$val){
- if(!in_array($key,$fields,true)){
- unset($data[$key]);
- }elseif(array_key_exists($key,$this->mapFields)){
- // 需要处理映射字段
- $data[$this->mapFields[$key]] = $val;
- unset($data[$key]);
- }
- }
- }
-
- // 安全过滤
- if(!empty($this->options['filter'])) {
- $data = array_map($this->options['filter'],$data);
- unset($this->options['filter']);
- }
- $this->_before_write($data);
- return $data;
- }
- /**
- * 保存聚合模型数据
- * @access public
- * @param mixed $data 数据
- * @param array $options 表达式
- * @return boolean
- */
- public function save($data='',$options=array()){
- // 根据主表的主键更新
- if(empty($data)) {
- // 没有传递数据,获取当前数据对象的值
- if(!empty($this->data)) {
- $data = $this->data;
- // 重置数据
- $this->data = array();
- }else{
- $this->error = L('_DATA_TYPE_INVALID_');
- return false;
- }
- }
- if(empty($data)){
- // 没有数据则不执行
- $this->error = L('_DATA_TYPE_INVALID_');
- return false;
- }
- // 如果存在主键数据 则自动作为更新条件
- $pk = $this->pk;
- if(isset($data[$pk])) {
- $where[$pk] = $data[$pk];
- $options['where'] = $where;
- unset($data[$pk]);
- }
- $options['join'] = '';
- $options = $this->_parseOptions($options);
- // 更新操作不使用JOIN
- $options['table'] = $this->getTableName();
- if(is_array($options['where']) && isset($options['where'][$pk])){
- $pkValue = $options['where'][$pk];
- }
- if(false === $this->_before_update($data,$options)) {
- return false;
- }
- $result = $this->db->update($data,$options);
- if(false !== $result) {
- if(isset($pkValue)) $data[$pk] = $pkValue;
- $this->_after_update($data,$options);
- }
- return $result;
- }
- /**
- * 删除聚合模型数据
- * @access public
- * @param mixed $options 表达式
- * @return mixed
- */
- public function delete($options=array()){
- $pk = $this->pk;
- if(empty($options) && empty($this->options['where'])) {
- // 如果删除条件为空 则删除当前数据对象所对应的记录
- if(!empty($this->data) && isset($this->data[$pk]))
- return $this->delete($this->data[$pk]);
- else
- return false;
- }
-
- if(is_numeric($options) || is_string($options)) {
- // 根据主键删除记录
- if(strpos($options,',')) {
- $where[$pk] = array('IN', $options);
- }else{
- $where[$pk] = $options;
- }
- $options = array();
- $options['where'] = $where;
- }
- // 分析表达式
- $options['join'] = '';
- $options = $this->_parseOptions($options);
- if(empty($options['where'])){
- // 如果条件为空 不进行删除操作 除非设置 1=1
- return false;
- }
- if(is_array($options['where']) && isset($options['where'][$pk])){
- $pkValue = $options['where'][$pk];
- }
-
- $options['table'] = implode(',',$this->modelList);
- $options['using'] = $this->getTableName();
- if(false === $this->_before_delete($options)) {
- return false;
- }
- $result = $this->db->delete($options);
- if(false !== $result) {
- $data = array();
- if(isset($pkValue)) $data[$pk] = $pkValue;
- $this->_after_delete($data,$options);
- }
- // 返回删除记录个数
- return $result;
- }
- /**
- * 表达式过滤方法
- * @access protected
- * @param string $options 表达式
- * @return void
- */
- protected function _options_filter(&$options) {
- if(!isset($options['join'])){
- $models = $this->modelList;
- array_shift($models);
- foreach($models as $model){
- $options['join'][] = $this->joinType.' JOIN '.M($model)->getTableName().' '.$model.' ON '.$this->masterModel.'.'.$this->pk.' = '.$model.'.'.$this->fk;
- }
- }
- $options['table'] = M($this->masterModel)->getTableName().' '.$this->masterModel;
- $options['field'] = $this->checkFields(isset($options['field'])?$options['field']:'');
- 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 mixed $data 条件表达式
- * @return array
- */
- protected function checkCondition($where) {
- if(is_array($where)) {
- $view = array();
- foreach($where as $name=>$value){
- if(array_key_exists($name,$this->mapFields)){
- // 需要处理映射字段
- $view[$this->mapFields[$name]] = $value;
- unset($where[$name]);
- }
- }
- $where = array_merge($where,$view);
- }
- return $where;
- }
- /**
- * 检查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';
- if(array_key_exists($field,$this->mapFields)){
- // 需要处理映射字段
- $field = $this->mapFields[$field];
- }
- $_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){
- // 解析成聚合字段
- if(array_key_exists($field,$this->mapFields)){
- // 需要处理映射字段
- $field = $this->mapFields[$field];
- }
- $_group[] = $field;
- }
- $group = implode(',',$_group);
- }
- return $group;
- }
- /**
- * 检查fields表达式中的聚合字段
- * @access protected
- * @param string $fields 字段
- * @return string
- */
- protected function checkFields($fields='') {
- if(empty($fields) || '*'==$fields ) {
- // 获取全部聚合字段
- $fields = $this->fields;
- }
- if(!is_array($fields))
- $fields = explode(',',$fields);
- // 解析成聚合字段
- $array = array();
- foreach ($fields as $field){
- if(array_key_exists($field,$this->mapFields)){
- // 需要处理映射字段
- $array[] = $this->mapFields[$field].' AS '.$field;
- }else{
- $array[] = $field;
- }
- }
- $fields = implode(',',$array);
- return $fields;
- }
- /**
- * 获取数据表字段信息
- * @access public
- * @return array
- */
- public function getDbFields(){
- return $this->fields;
- }
- }
|