1
0
Fork 0
mirror of https://github.com/Oreolek/ifhub.club.git synced 2024-07-01 05:55:02 +03:00

Модуль валидации данных + поддержка валидации в сущностях

This commit is contained in:
Mzhelskiy Maxim 2012-03-02 21:27:16 +04:00
parent bfe4c1707e
commit 42dce73baa
7 changed files with 665 additions and 23 deletions

View file

@ -19,9 +19,13 @@
* Абстрактный класс сущности
*
*/
abstract class Entity extends Object {
abstract class Entity extends Object {
protected $_aData=array();
protected $sPrimaryKey = null;
protected $sPrimaryKey = null;
protected $aValidateRules=array();
protected $aValidateErrors=array();
protected $sValidateScenario='';
/**
* Если передать в конструктор ассоциативный массив свойств и их значений, то они автоматом загрузятся в сущность
@ -42,7 +46,7 @@ abstract class Entity extends Object {
public function _getData($aKeys=array()) {
if(!is_array($aKeys) or !count($aKeys)) return $this->_aData;
$aReturn=array();
foreach ($aKeys as $key) {
if(array_key_exists($key,$this->_aData)) {
@ -58,7 +62,7 @@ abstract class Entity extends Object {
}
return null;
}
/**
* Рекурсивное преобразование объекта и вложенных объектов в массив
*/
@ -74,7 +78,7 @@ abstract class Entity extends Object {
}
return $aResult;
}
/**
* Ставим хук на вызов неизвестного метода и считаем что хотели вызвать метод какого либо модуля
*
@ -106,25 +110,171 @@ abstract class Entity extends Object {
}
}
/**
* Получение первичного ключа сущности (ключ, а не значение!)
*/
public function _getPrimaryKey()
{
if (!$this->sPrimaryKey) {
if (isset($this->_aData['id'])) {
$this->sPrimaryKey = 'id';
} else {
// Получение primary_key из схемы бд (пока отсутствует)
$this->sPrimaryKey = 'id';
}
}
/**
* Получение первичного ключа сущности (ключ, а не значение!)
*/
public function _getPrimaryKey()
{
if (!$this->sPrimaryKey) {
if (isset($this->_aData['id'])) {
$this->sPrimaryKey = 'id';
} else {
// Получение primary_key из схемы бд (пока отсутствует)
$this->sPrimaryKey = 'id';
}
}
return $this->sPrimaryKey;
}
return $this->sPrimaryKey;
}
public function _getPrimaryKeyValue() {
return $this->_getDataOne($this->_getPrimaryKey());
}
public function _getPrimaryKeyValue() {
return $this->_getDataOne($this->_getPrimaryKey());
}
/**
* Выполняет валидацию данных сущности
* Если $aFields=null, то выполняется валидация по всем полям из $this->aValidateRules, иначе по пересечению
*
* @param null $attributes
* @param bool $clearErrors
*
* @return bool
*/
public function _Validate($aFields=null, $clearErrors=true) {
if($clearErrors) {
$this->_clearValidateErrors();
}
foreach($this->_getValidators() as $validator) {
$validator->validateEntity($this,$aFields);
}
return !$this->_hasValidateErrors();
}
/**
* Возвращает список валидаторов с учетом текущего сценария
*
* @param null|string $sField Поле сущности для которого необходимо вернуть валидаторы, если нет, то возвращается для всех полей
*
* @return array
*/
public function _getValidators($sField=null) {
$aValidators=$this->createValidators();
$aValidatorsReturn=array();
$sScenario=$this->_getValidateScenario();
foreach($aValidators as $oValidator) {
/**
* Проверка на текущий сценарий
*/
if($oValidator->applyTo($sScenario)) {
if($sField===null || in_array($sField,$oValidator->fields,true)) {
$aValidatorsReturn[]=$oValidator;
}
}
}
return $aValidatorsReturn;
}
/**
* Создает и возвращает список валидаторов для сущности
*
* @return array
* @throws Exception
*/
public function createValidators() {
$aValidators=array();
foreach($this->aValidateRules as $aRule) {
if(isset($aRule[0],$aRule[1])) {
$aValidators[]=$this->Validate_CreateValidator($aRule[1],$this,$aRule[0],array_slice($aRule,2));
} else {
throw new Exception(get_class($this).' has an invalid validation rule');
}
}
return $aValidators;
}
/**
* Проверяет есть ли ошибки валидации
*
* @param null|string $sField Поле сущности, если нет, то проверяется для всех полей
*
* @return bool
*/
public function _hasValidateErrors($sField=null) {
if($sField===null) {
return $this->aValidateErrors!==array();
} else {
return isset($this->aValidateErrors[$sField]);
}
}
/**
* Возвращает список ошибок для всех полей или одного поля
*
* @param null|string $sField Поле сущности, если нет, то возвращается для всех полей
*
* @return array
*/
public function _getValidateErrors($sField=null) {
if($sField===null) {
return $this->aValidateErrors;
} else {
return isset($this->aValidateErrors[$sField]) ? $this->aValidateErrors[$sField] : array();
}
}
/**
* Возвращает первую ошибку для поля
*
* @param string $sField Поле сущности
*
* @return string|null
*/
public function _getValidateError($sField) {
return isset($this->aValidateErrors[$sField]) ? reset($this->aValidateErrors[$sField]) : null;
}
/**
* Добавляет для поля ошибку в список ошибок
*
* @param string $sField Поле сущности
* @param string $error Сообщение об ошибке
*/
public function _addValidateError($sField,$error) {
$this->aValidateErrors[$sField][]=$error;
}
/**
* Очищает список всех ошибок или для конкретного поля
*
* @param null|string $sField Поле сущности
*/
public function _clearValidateErrors($sField=null) {
if($sField===null) {
$this->aValidateErrors=array();
} else {
unset($this->aValidateErrors[$sField]);
}
}
/**
* Возвращает текущий сценарий валидации
*
* @return string
*/
public function _getValidateScenario() {
return $this->sValidateScenario;
}
/**
* Устанавливает сценарий валидации
*
* @param string $sValue
*/
public function _setValidateScenario($sValue) {
$this->sValidateScenario=$sValue;
}
}
?>

View file

@ -0,0 +1,177 @@
<?php
/*-------------------------------------------------------
*
* LiveStreet Engine Social Networking
* Copyright © 2008 Mzhelskiy Maxim
*
*--------------------------------------------------------
*
* Official site: www.livestreet.ru
* Contact e-mail: rus.engine@gmail.com
*
* GNU General Public License, version 2:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
---------------------------------------------------------
*/
/**
* Модуль Validate
* Выполняет валидацию данных по определенным правилам
*
*/
class ModuleValidate extends Module {
/**
* Список ошибок при валидации, заполняется только если использовать валидацию напрямую без сущности
*
* @var array
*/
protected $aErrors=array();
/**
* Инициализируем модуль
*
*/
public function Init() {
}
/**
* Запускает валидацию данных
*
* @param $sNameValidator Имя валидатора или метода при использовании параметра $oObject
* @param $mValue Валидируемое значение
* @param array $aParams Параметры валидации
* @param null $oObject Объект в котором необходимо вызвать метод валидации
*
* @return bool
*/
public function Validate($sNameValidator,$mValue,$aParams=array(),$oObject=null) {
if (is_null($oObject)) {
$oObject=$this;
}
$oValidator=$this->CreateValidator($sNameValidator,$oObject,null,$aParams);
if (($sMsg=$oValidator->validate($mValue))!==true) {
$sMsg=str_replace('%%field%%',is_null($oValidator->label) ? '' : $oValidator->label,$sMsg);
$this->AddError($sMsg);
return false;
} else {
return true;
}
}
/**
* Создает и возвращает объект валидатора
*
* @param $sName Имя валидатора или метода при использовании параметра $oObject
* @param $oObject Объект в котором необходимо вызвать метод валидации
* @param null $aFields Список полей сущности для которых необходимо провести валидацию
* @param array $aParams
*
* @return mixed
*/
public function CreateValidator($sName,$oObject,$aFields=null,$aParams=array()) {
if(is_string($aFields)) {
$aFields=preg_split('/[\s,]+/',$aFields,-1,PREG_SPLIT_NO_EMPTY);
}
/**
* Определяем список сценариев валидации
*/
if(isset($aParams['on'])) {
if(is_array($aParams['on'])) {
$aOn=$aParams['on'];
} else {
$aOn=preg_split('/[\s,]+/',$aParams['on'],-1,PREG_SPLIT_NO_EMPTY);
}
} else {
$aOn=array();
}
/**
* Если в качестве имени валидатора указан метод объекта, то создаем специальный валидатор
*/
if(method_exists($oObject,$sName)) {
$oValidator=Engine::GetEntity('ModuleValidate_EntityValidatorInline');
if (!is_null($aFields)) {
$oValidator->fields=$aFields;
}
$oValidator->object=$oObject;
$oValidator->method=$sName;
$oValidator->params=$aParams;
if(isset($aParams['skipOnError'])) {
$oValidator->skipOnError=$aParams['skipOnError'];
}
} else {
/**
* Иначе создаем валидатор по имени
*/
if (!is_null($aFields)) {
$aParams['fields']=$aFields;
}
$sValidateName='Validator'.func_camelize($sName);
$oValidator=Engine::GetEntity('ModuleValidate_Entity'.$sValidateName);
foreach($aParams as $sNameParam=>$sValue) {
$oValidator->$sNameParam=$sValue;
}
}
$oValidator->on=empty($aOn) ? array() : array_combine($aOn,$aOn);
return $oValidator;
}
/**
* Возвращает факт наличия ошибки после валидации
*
* @return bool
*/
public function HasErrors() {
return count($this->aErrors) ? true : false;
}
/**
* Возвращает список ошибок после валидации
*
* @return array
*/
public function GetErrors() {
return $this->aErrors;
}
/**
* Возвращает последнюю ошибку после валидации
*
* @param bool $bRemove Удалять или нет ошибку из списка ошибок
* @return bool|string
*/
public function GetErrorLast($bRemove=false) {
if (!$this->HasErrors()) {
return false;
}
if ($bRemove) {
return array_pop($this->aErrors);
} else {
return $this->aErrors[count($this->aErrors)-1];
}
}
/**
* Содовляет ошибку в список
*
* @param $sError
*/
public function AddError($sError) {
$this->aErrors[]=$sError;
}
/**
* Очищает список ошибок
*/
public function ClearErrors() {
$this->aErrors=array();
}
}
?>

View file

@ -0,0 +1,156 @@
<?php
/*-------------------------------------------------------
*
* LiveStreet Engine Social Networking
* Copyright © 2008 Mzhelskiy Maxim
*
*--------------------------------------------------------
*
* Official site: www.livestreet.ru
* Contact e-mail: rus.engine@gmail.com
*
* GNU General Public License, version 2:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
---------------------------------------------------------
*/
abstract class ModuleValidate_EntityValidator extends Entity {
/**
* Пропускать или нет ошибку
*
* @var bool
*/
public $bSkipOnError=false;
/**
* Список полей сущности для валидации
*
* @var array
*/
public $fields=array();
/**
* Название поля сущности для отображения в тексте ошибки
*
* @var null|string
*/
public $label=null;
/**
* Текст ошибки валидации, переопределяет текст валидатора
*
* @var null|string
*/
public $msg=null;
/**
* Список сценариев в которых участвует валидатор
*
* @var null|array
*/
public $on=null;
/**
* Основной метод валидации
*
* @abstract
* @param $sValue
*/
abstract public function validate($sValue);
/**
* Проверяет данные на пустое значение
*
* @param $mValue Данные
* @param bool $bTrim Не учитывать пробелы
*
* @return bool
*/
protected function isEmpty($mValue,$bTrim=false) {
return $mValue===null || $mValue===array() || $mValue==='' || $bTrim && is_scalar($mValue) && trim($mValue)==='';
}
/**
* Применять или нет сценарий к текущему валидатору
* Для сценария учитываются только те правила, где явно прописан необходимый сценарий
*
* @param $sScenario
*
* @return bool
*/
public function applyTo($sScenario) {
return (empty($this->on) && !$sScenario) || isset($this->on[$sScenario]);
}
/**
* Возвращает сообщение, используется для получения сообщения об ошибке валидатора
*
* @param $sMsgDefault Дефолтное сообщение
* @param null $sMsgFieldCustom Поле/параметр в котором может храниться кастомное сообщение. В поле $sMsgFieldCustom."Id" можно хранить ключ текстовки из языкового файла
* @param array $aReplace Список параметров для замены в сообщении (плейсхолдеры)
*
* @return string
*/
protected function getMessage($sMsgDefault,$sMsgFieldCustom=null,$aReplace=array()) {
if (!is_null($sMsgFieldCustom)) {
if (!is_null($this->$sMsgFieldCustom)) {
$sMsgDefault=$this->$sMsgFieldCustom;
} else {
$sMsgFieldCustomId=$sMsgFieldCustom.'Id';
if (property_exists($this,$sMsgFieldCustomId) and !is_null($this->$sMsgFieldCustomId)) {
$sMsgDefault=$this->Lang_Get($this->$sMsgFieldCustomId,array(),false);
}
}
}
foreach ($aReplace as $sFrom => $sTo) {
$aReplacePairs["%%{$sFrom}%%"]=$sTo;
}
return strtr($sMsgDefault,$aReplacePairs);
}
/**
* Запускает валидацию полей сущности
*
* @param $oEntity Объект сущности
* @param null $aFields Список полей для валидации, если пуст то валидируются все поля указанные в правиле
*/
public function validateEntity($oEntity,$aFields=null) {
if(is_array($aFields)) {
$aFields=array_intersect($this->fields,$aFields);
} else {
$aFields=$this->fields;
}
/**
* Запускаем валидацию для каждого поля
*/
foreach($aFields as $sField) {
if(!$this->bSkipOnError || !$oEntity->_hasValidateErrors($sField)) {
$this->validateEntityField($oEntity,$sField);
}
}
}
/**
* Запускает валидацию конкретного поля сущности
*
* @param $oEntity Объект сущности
* @param $sField Поле сущности
*
* @return bool
*/
public function validateEntityField($oEntity,$sField) {
/**
* Получаем значение поля у сущности через геттер
*/
$sValue=call_user_func_array(array($oEntity,'get'.func_camelize($sField)),array());
if (($sMsg=$this->validate($sValue))!==true) {
/**
* Подставляем имя поля в сообщение об ошибке валидации
*/
$sMsg=str_replace('%%field%%',is_null($this->label) ? $sField : $this->label,$sMsg);
$oEntity->_addValidateError($sField,$sMsg);
} else {
return true;
}
}
}
?>

View file

@ -0,0 +1,53 @@
<?php
/*-------------------------------------------------------
*
* LiveStreet Engine Social Networking
* Copyright © 2008 Mzhelskiy Maxim
*
*--------------------------------------------------------
*
* Official site: www.livestreet.ru
* Contact e-mail: rus.engine@gmail.com
*
* GNU General Public License, version 2:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
---------------------------------------------------------
*/
/**
* Валидатор для кастомных методов объектов
*/
class ModuleValidate_EntityValidatorInline extends ModuleValidate_EntityValidator {
/**
* Метод объекта для валидации, в него передаются параметры: $sValue и $aParam
*
* @var string
*/
public $method;
/**
* Объект у которого будет вызван метод валидации, дляя сущности - это сам объект сущности
*
* @var object
*/
public $object;
/**
* Список параметров для передачи в метод валидации
*
* @var array
*/
public $params;
/**
* Запуск валидации
*
* @param $sValue Данные для валидации
* @return bool|string
*/
public function validate($sValue) {
$sMethod=$this->method;
return $this->object->$sMethod($sValue,$this->params);
}
}
?>

View file

@ -0,0 +1,88 @@
<?php
/*-------------------------------------------------------
*
* LiveStreet Engine Social Networking
* Copyright © 2008 Mzhelskiy Maxim
*
*--------------------------------------------------------
*
* Official site: www.livestreet.ru
* Contact e-mail: rus.engine@gmail.com
*
* GNU General Public License, version 2:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
---------------------------------------------------------
*/
/**
* Валидатор текстовых данных на длину
*/
class ModuleValidate_EntityValidatorString extends ModuleValidate_EntityValidator {
/**
* Максимальня длина строки
*
* @var int
*/
public $max;
/**
* Минимальная длина строки
*
* @var int
*/
public $min;
/**
* Конкретное значение длины строки
*
* @var int
*/
public $is;
/**
* Кастомное сообщение об ошибке при короткой строке
*
* @var string
*/
public $msgTooShort;
/**
* Кастомное сообщение об ошибке при слишком длинной строке
*
* @var string
*/
public $msgTooLong;
/**
* Допускать или нет пустое значение
*
* @var bool
*/
public $allowEmpty=true;
/**
* Запуск валидации
*
* @param $sValue Данные для валидации
*
* @return bool|string
*/
public function validate($sValue) {
if($this->allowEmpty && $this->isEmpty($sValue)) {
return true;
}
$iLength=mb_strlen($sValue, 'UTF-8');
if($this->min!==null && $iLength<$this->min) {
return $this->getMessage($this->Lang_Get('validate_string_too_short',null,false),'msgTooShort',array('min'=>$this->min));
}
if($this->max!==null && $iLength>$this->max) {
return $this->getMessage($this->Lang_Get('validate_string_too_long',null,false),'msgTooLong',array('max'=>$this->max));
}
if($this->is!==null && $iLength!==$this->is) {
return $this->getMessage($this->Lang_Get('validate_string_no_lenght',null,false),'msg',array('length'=>$this->is));
}
return true;
}
}
?>

View file

@ -869,6 +869,15 @@ return array(
'plugins_activation_requires_error' => 'Plugin requires activated <b>%%plugin%%</b> plugin',
'plugins_submit_delete' => 'Delete plugins',
'plugins_delete_confirm' => 'Do you really want to delete those plugins?',
/**
* Validation
*/
'validate_string_too_long' => 'Field %%field%% is too long (maximum is %%max%% characters).',
'validate_string_too_short' => 'Field %%field%% is too short (minimum is %%min%% characters)',
'validate_string_no_lenght' => 'Field %%field%% is of the wrong length (should be %%length%% characters)',
/**
* System
*/
'system_error_event_args' => 'Wrong number of arguments during event addition',
'system_error_event_method' => 'Added event method not found',
'system_error_404' => 'Unfortunately there is no such page. Probably deleted or wasn\'t there in the first place.',

View file

@ -869,6 +869,15 @@ return array(
'plugins_activation_requires_error' => 'Для работы плагина необходим активированный плагин <b>%%plugin%%</b>',
'plugins_submit_delete' => 'Удалить плагины',
'plugins_delete_confirm' => 'Вы уверены, что желаете удалить указанные плагины?',
/**
* Валидация данных
*/
'validate_string_too_long' => 'Поле %%field%% слишком длинное (максимально допустимо %%max%% символов)',
'validate_string_too_short' => 'Поле %%field%% слишком короткое (минимально допустимо %%min%% символов)',
'validate_string_no_lenght' => 'Поле %%field%% неверной длины (необходимо %%length%% символов)',
/**
* Системные сообщения
*/
'system_error_event_args' => 'Некорректное число аргументов при добавлении евента',
'system_error_event_method' => 'Добавляемый метод евента не найден',
'system_error_404' => 'К сожалению, такой страницы не существует. Вероятно, она была удалена с сервера, либо ее здесь никогда не было.',