Построитель простейших SQL запросов на примере выборок модуля File
Может кто-то и не знал, но в XText есть построитель простых SQL запросов, который я использую в мапперах этого плагина.
Согласитесь, как некрасиво переопределять метод маппера какого-либо модуля, ведь при этом полностью переписывается запрос, содержащийся в этом методе. А если его переопределят два плагина? Да, правильно, отработает только последний :)
Ради красивости решения, я придумал объект модифицируемого запроса в виде Entity. Называется он PluginXtext_ModuleSql_EntityQuery.
Давайте рассмотрим его на примере маппера тех же файлов, о которых недавно шла речь.
Для наглядности, в этом маппере, методы разделены на два типа: первые отдают объект Sql-запроса, вторые же — его исполняют и возвращают результат методу модуля.
Вот три метода маппера для затравки:
Путем переопределения одного из приведенных выше методов *Sql маппера можно докинуть туда поля из других таблиц LEFT JOIN'ом, для которого так же предусмотрен метод
дунуть добавить поля выборки для подключенной таблицы
Сейчас будет огромная тонна кода :) Метод построения фильтра для получения списка файлов:
Как видно, фильтр получается весьма насыщенный. Теперь посмотрим, как он применяется:
Да, тут чёрт, может быть и сломит себе не только ногу, но если постараться и разобраться, то выглядит оно весьма вкусно. И вообще, самое место ему в ядре LS :)
Вот такая, понимаешь, загогулина… (ц) Е.Б.Н.
Согласитесь, как некрасиво переопределять метод маппера какого-либо модуля, ведь при этом полностью переписывается запрос, содержащийся в этом методе. А если его переопределят два плагина? Да, правильно, отработает только последний :)
Ради красивости решения, я придумал объект модифицируемого запроса в виде Entity. Называется он PluginXtext_ModuleSql_EntityQuery.
Давайте рассмотрим его на примере маппера тех же файлов, о которых недавно шла речь.
Для наглядности, в этом маппере, методы разделены на два типа: первые отдают объект Sql-запроса, вторые же — его исполняют и возвращают результат методу модуля.
Вот три метода маппера для затравки:
/**
* @return PluginXtext_ModuleSql_EntityQuery
*/
public function GetFilesSql(){
$oQ = func_xtext_sql()
->addFromTable(Config::Get('plugin.xtext.table.file'), 'f')
->addSelect('f.*')
->addCacheTag(
'xtext_file'
)
->setCacheTime(60*60*24)
;
return $oQ;
}
/**
* @return PluginXtext_ModuleSql_EntityQuery
*/
public function GetFilesByIdArraySql($aFileId){
if(!is_array($aFileId)
|| !($aFileId = array_filter($aFileId))){
return;
}
$oQ = $this->GetFilesSql()
->addWhere('file_id', 'f.file_id in (?a)')
->A($aFileId)
->addCacheTag(
func_build_cache_keys($aFileId, 'xtext_file_')
)
;
return $oQ;
}
public function GetFilesByIdArray($aFileId){
if(!($oQ = $this->GetFilesByIdArraySql($aFileId))){
return array();
}
$aFiles = array();
if($aRows = $oQ->Exec()){
foreach($aRows as $aRow){
$aFiles[$aRow['file_id']] = Engine::GetEntity('PluginXtext_File', $aRow);
}
}
return $aFiles;
}
- GetFilesSql — задает общий вид выборки файлов, устанавливает общий тег кеша и его таймаут
- GetFilesByIdArraySql — добавляет к объекту запроса условие where с соответствующим аргументом для плейсхолдера ?a, еще N кеш-тегов и так же возвращает слегка модифицированный объект запроса
- GetFilesByIdArray — исполняет запрос, полученный от GetFilesByIdArraySql, создает список объектов файловых записей и возвращает их. Это тот самый конечный метод, к которому можно обращаться из метода модуля.
Путем переопределения одного из приведенных выше методов *Sql маппера можно докинуть туда поля из других таблиц LEFT JOIN'ом, для которого так же предусмотрен метод
public function addLeftJoinTable($sTable, $sShort = null, $sOn = null)
где- $sTable — имя подключаемой таблицы
- $sShort — алиас таблицы для запроса
- $sOn — условие присоединения
public function addSelect($sWhat, $sShort = null)
где- $sWhat — имя добавляемого в выборку поля в виде 'алиас_таблицы.имя_поля'
- $sShort — алиас выбираемого поля, если его надо переименовать в результате выборки
Построение фильтров
Сейчас будет огромная тонна кода :) Метод построения фильтра для получения списка файлов:
/**
* @return PluginXtext_ModuleSql_EntityQuery
*/
public function AddFilterGetFilesSql($oQ, $aFilter){
if(!$oQ){
return;
}
if(!empty($aFilter['id'])){
$oQ
->addWhere(
'file_id',
is_array($aFilter['id'])
? 'f.file_id in (?a)'
: 'f.file_id=?d'
)
->A($aFilter['id'])
->addCacheTag(
is_array($aFilter['id'])
? func_build_cache_keys($aFilter['id'], 'xtext_file_')
: 'xtext_file_'.$aFilter['id']
)
;
}
if(!empty($aFilter['user_id'])){
$oQ
->addWhere(
'user_id',
is_array($aFilter['user_id'])
? 'f.file_user_id in (?a)'
: 'f.file_user_id=?d'
)
->A($aFilter['user_id'])
->addCacheTag(
is_array($aFilter['user_id'])
? func_build_cache_keys($aFilter['user_id'], 'xtext_file_user_')
: 'xtext_file_user_'.$aFilter['user_id']
)
;
}
if(!empty($aFilter['ext'])){
$oQ
->addWhere(
'ext',
is_array($aFilter['ext'])
? 'f.file_ext in (?a)'
: 'f.file_ext=?'
)
->A($aFilter['ext'])
->addCacheTag(
is_array($aFilter['ext'])
? func_build_cache_keys($aFilter['ext'], 'xtext_file_ext_')
: 'xtext_file_ext_'.$aFilter['ext']
)
;
}
if(isset($aFilter['time_min'])){
$oQ
->addWhere(
'time_min',
'f.file_upload_time>=?d'
)
->A($aFilter['time_min'])
;
}
if(isset($aFilter['time_max'])){
$oQ
->addWhere(
'time_max',
'f.file_upload_time<?d'
)
->A($aFilter['time_max'])
;
}if(isset($aFilter['size_min'])){
$oQ
->addWhere(
'size_min',
'f.file_size>=?d'
)
->A($aFilter['size_min'])
;
}
if(isset($aFilter['size_max'])){
$oQ
->addWhere(
'size_max',
'f.file_size<?d'
)
->A($aFilter['size_max'])
;
}
if(isset($aFilter['parent_id'])){
$oQ
->addWhere(
'parent_id',
is_array($aFilter['parent_id'])
? 'f.file_parent_id in (?a)'
: 'f.file_parent_id=?d'
)
->A($aFilter['parent_id'])
->addCacheTag(
is_array($aFilter['parent_id'])
? func_build_cache_keys($aFilter['parent_id'], 'xtext_file_parent_')
: 'xtext_file_parent_'.$aFilter['parent_id']
)
;
}
if(!empty($aFilter['hash'])){
$oQ
->addWhere(
'hash',
is_array($aFilter['hash'])
? 'f.file_hash in (?a)'
: 'f.file_hash=?'
)
->A($aFilter['hash'])
->addCacheTag(
is_array($aFilter['hash'])
? func_build_cache_keys($aFilter['hash'], 'xtext_file_hash_')
: 'xtext_file_hash_'.$aFilter['hash']
)
;
}
if(!empty($aFilter['path'])){
$oQ
->addWhere(
'path',
is_array($aFilter['path'])
? 'f.file_path in (?a)'
: 'f.file_path=?'
)
->A($aFilter['path'])
;
}
if(!empty($aFilter['count'])){
return $oQ;
}
if(!empty($aFilter['order_by'])){
$oQ->addOrder(
$aFilter['order_by'],
!empty($aFilter['order_desc']) ? 'desc' : 'asc'
);
}
if(!empty($aFilter['on_page'])){
$iOffset = (int) @$aFilter['page']*$aFilter['on_page'];
$oQ
->setLimit($aFilter['on_page'])
->setOffset($iOffset)
;
}
return $oQ;
}
Как видно, фильтр получается весьма насыщенный. Теперь посмотрим, как он применяется:
/**
* @return PluginXtext_ModuleSql_EntityQuery
*/
public function GetCountFilesSql(){
$oQ = func_xtext_sql()
->addFromTable(Config::Get('plugin.xtext.table.file'), 'f')
->addSelect('count(f.file_id)', 'file_count')
->addCacheTag(
'xtext_file'
)
->setCacheTime(60*60*24)
;
return $oQ;
}
/**
* @return PluginXtext_ModuleSql_EntityQuery
*/
public function GetCountFilesByFilterSql($aFilter){
$oQ = $this->AddFilterGetFilesSql(
$this->GetCountFilesSql(),
array('count' => true) + $aFilter
);
return $oQ;
}
/**
* @return PluginXtext_ModuleSql_EntityQuery
*/
public function GetFilesByFilterSql($aFilter){
$oQ = $this->AddFilterGetFilesSql(
$this->GetFilesSql(),
$aFilter
);
return $oQ;
}
public function GetCountFilesByFilter($aFilter){
if(!($oQ = $this->GetCountFilesByFilterSql($aFilter))){
return 0;
}
return (int) $oQ->Exec('Cell');
}
public function GetFilesByFilter($aFilter){
if(!($oQ = $this->GetFilesByFilterSql($aFilter))){
return array();
}
$aFiles = array();
if($aRows = $oQ->Exec()){
foreach($aRows as $aRow){
$aFiles[$aRow['file_id']] = Engine::GetEntity('PluginXtext_File', $aRow);
}
}
return $aFiles;
}
Да, тут чёрт, может быть и сломит себе не только ногу, но если постараться и разобраться, то выглядит оно весьма вкусно. И вообще, самое место ему в ядре LS :)
Вот такая, понимаешь, загогулина… (ц) Е.Б.Н.
4 комментария
это для краткости вспомогательная функция.
вообще тот модуль есть в брошенной мной админке и примеры использования тоже есть вроде )