tp5 model 保存数据及验证

model源码

public function save($data = [], $where = [], $sequence = null) {

		if (!empty($data)) {
			// 数据自动验证
			if (!$this->validateData($data)) {
				return false;
			}
			// 数据对象赋值
			foreach ($data as $key => $value) {
				$this->setAttr($key, $value, $data);
			}
			if (!empty($where)) {
				$this->isUpdate = true;
			}
		}

		// 自动关联写入
		if (!empty($this->relationWrite)) {
			$relation = [];
			foreach ($this->relationWrite as $key => $name) {
				if (is_array($name)) {
					if (key($name) === 0) {
						$relation[$key] = [];
						foreach ($name as $val) {
							if (isset($this->data[$val])) {
								$relation[$key][$val] = $this->data[$val];
								unset($this->data[$val]);
							}
						}
					} else {
						$relation[$key] = $name;
					}
				} elseif (isset($this->relation[$name])) {
					$relation[$name] = $this->relation[$name];
				} elseif (isset($this->data[$name])) {
					$relation[$name] = $this->data[$name];
					unset($this->data[$name]);
				}
			}
		}
		// 数据自动完成
		$this->autoCompleteData($this->auto);
		// 事件回调
		if (false === $this->trigger('before_write', $this)) {
			return false;
		}
		$pk = $this->getPk();
		if ($this->isUpdate) {
			// 检测字段
			$this->checkAllowField($this->data, array_merge($this->auto, $this->update));

			// 自动更新
			$this->autoCompleteData($this->update);

			// 获取有更新的数据
			$data = $this->getChangedData();

			// 事件回调
			if (false === $this->trigger('before_update', $this)) {
				return false;
			}

			if (empty($data) || (count($data) == 1 && is_string($pk) && isset($data[$pk]))) {
				// 关联更新
				if (isset($relation)) {
					$this->autoRelationUpdate($relation);
				}
				return 0;
			} elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) {
				// 自动写入更新时间
				$data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
			}

			if (empty($where) && !empty($this->updateWhere)) {
				$where = $this->updateWhere;
			}

			// 保留主键数据
			foreach ($this->data as $key => $val) {
				if ($this->isPk($key)) {
					$data[$key] = $val;
				}
			}

			if (is_string($pk) && isset($data[$pk])) {
				if (!isset($where[$pk])) {
					unset($where);
					$where[$pk] = $data[$pk];
				}
				unset($data[$pk]);
			}

			// 模型更新
			$result = $this->getQuery()->where($where)->update($data);

			// 关联更新
			if (isset($relation)) {
				$this->autoRelationUpdate($relation);
			}

			// 更新回调
			$this->trigger('after_update', $this);

		} else {
			// 检测字段
			$this->checkAllowField($this->data, array_merge($this->auto, $this->insert));

			// 自动写入
			$this->autoCompleteData($this->insert);

			// 自动写入创建时间和更新时间
			if ($this->autoWriteTimestamp) {
				if ($this->createTime && !isset($this->data[$this->createTime])) {
					$this->data[$this->createTime] = $this->autoWriteTimestamp($this->createTime);
				}
				if ($this->updateTime && !isset($this->data[$this->updateTime])) {
					$this->data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
				}
			}

			if (false === $this->trigger('before_insert', $this)) {
				return false;
			}

			$result = $this->getQuery()->insert($this->data);

			// 获取自动增长主键
			if ($result && is_string($pk) && (!isset($this->data[$pk]) || '' == $this->data[$pk])) {
				$insertId = $this->getQuery()->getLastInsID($sequence);
				if ($insertId) {
					$this->data[$pk] = $insertId;
				}
			}

			// 关联写入
			if (isset($relation)) {
				foreach ($relation as $name => $val) {
					$method = Loader::parseName($name, 1, false);
					$this->$method()->save($val);
				}
			}

			// 标记为更新
			$this->isUpdate = true;

			// 新增回调
			$this->trigger('after_insert', $this);
		}
		// 写入回调
		$this->trigger('after_write', $this);

		// 重新记录原始数据
		$this->origin = $this->data;

		return $result;
	}

传入data不为空 执行顺序:验证 赋值 关联数据 自动补全 更新或保存数据

传入data为空 执行顺序:关联数据 自动补全 更新或保存数据

即 先验证后补全数据 ,若有需要根据表单其他数据做验证并且设置值的,应该是先验证后赋值。

比如判断传入文件的扩展名,并根据判断补全数据库一个字段,应先验证,再model赋值,两边部分代码会重复。

自动补全仅在save里被调用,所以不能验证自动补全的数据。

validate源码

protected function checkItem($field, $value, $rules, $data, $title = '', $msg = []) {
		// 支持多规则验证 require|in:a,b,c|... 或者 ['require','in'=>'a,b,c',...]
		if (is_string($rules)) {
			$rules = explode('|', $rules);
		}
		$i = 0;
		foreach ($rules as $key => $rule) {
			if ($rule instanceof \Closure) {
				$result = call_user_func_array($rule, [$value, $data]);
				$info = is_numeric($key) ? '' : $key;
			} else {
				// 判断验证类型
				if (is_numeric($key)) {
					if (strpos($rule, ':')) {
						list($type, $rule) = explode(':', $rule, 2);
						if (isset($this->alias[$type])) {
							// 判断别名
							$type = $this->alias[$type];
						}
						$info = $type;
					} elseif (method_exists($this, $rule)) {
						$type = $rule;
						$info = $rule;
						$rule = '';
					} else {
						$type = 'is';
						$info = $rule;
					}
				} else {
					$info = $type = $key;
				}

				// 如果不是require 有数据才会行验证
				if (0 === strpos($info, 'require') || (!is_null($value) && '' !== $value)) {
					// 验证类型
					$callback = isset(self::$type[$type]) ? self::$type[$type] : [$this, $type];

					// 验证数据
					$result = call_user_func_array($callback, [$value, $rule, $data, $field, $title]);
				} else {
					$result = true;
				}
			}

			if (false === $result) {
				// 验证失败 返回错误信息
				if (isset($msg[$i])) {
					$message = $msg[$i];
					if (is_string($message) && strpos($message, '{%') === 0) {
						$message = Lang::get(substr($message, 2, -1));
					}
				} else {
					$message = $this->getRuleMsg($field, $title, $info, $rule);
				}
				return $message;
			} elseif (true !== $result) {
				// 返回自定义错误信息
				if (is_string($result) && false !== strpos($result, ':')) {
					$result = str_replace([':attribute', ':rule'], [$title, (string) $rule], $result);
				}
				return $result;
			}
			$i++;
		}
		return $result;
	}

这个checkItem是验证数据,被同一个类里具有public访问权限的check方法调用。

没有设置require规则的字段,验证其他规则的情况为非空不验证。

call_user_func_array($callback, [$value, $rule, $data, $field, $title]); 这句调用对应验证方法。

对于自定义的验证规则来说应该可以写成这样:'type' => 'require|checkType',而不是这样:'type' => 'require|checkType:', 即自定义方法后面可以不必用“:”结尾,还没测试过。

check方法中,对于其错误信息直接赋值给$this->error,model也是直接赋值给$this->error,并且model和validate都有一个getError方法,也都是直接返回$this->error。

验证自动补全数据方法:

先自动补全再验证……

public function dataSave($data = null) {
		if (empty($data)) {
			$data = $this->param;
		}
		$result = false;
		if ($data !== false) {
			$data['content'] = htmlspecialchars_decode($data['content']);
			$data['content'] = preg_replace('/"/', '', $data['content']);
                        //手动数据补全
			foreach ($this->insert as $key => $value) {
				if (is_numeric($key)) {
					if (!isset($data[$value])) {
						$data[$value] = '';
					}
				} else {
					if (!isset($data[$key])) {
						$data[$key] = $value;
					}
				}
			}
			$this->autoCompleteData($data);
			$data = $this->getData();
                        //手动数据补全结束
			if (isset($data['id']) && empty($data['id'])) {
				//新增数据
				unset($data['id']);
				$id = $this->validate('OnlineTraining')->save($data);
				if ($id) {
					$data['id'] = $id;
					$result = $id;
				}
			} else {
				//更新数据
				$status = $this->validate('OnlineTraining')->save($data, array('id' => $data['id']));
				if (true === $status) {
					$result = $data['id'];
				}
			}
		}
		return $result;
	}

感觉modle应该加个可以用户调用的数据补全的方法。

上面的例子有个bug……create_time不自动填充,因为手动设置的值不为null。源码里判断创建时间为null才会自动填充。

你可能感兴趣的:(php,php)