null, FLT_NORMAL => null, FLT_SEARCH => null, FLT_VIEW => null);
/**
* Holds list HAVING filter object
*
* @var kMultipleFilter
* @access private
*/
var $HavingFilter = Array(FLT_SYSTEM => null, FLT_NORMAL => null, FLT_SEARCH => null, FLT_VIEW => null);
var $GroupByFields = Array();
var $Queried = false;
var $Counted = false;
/**
* Creates kDBList
*
* @return kDBList
*/
function kDBList() {
parent::kDBBase();
$this->OrderFields = Array();
$this->WhereFilter[FLT_SYSTEM] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_AND);
$this->WhereFilter[FLT_NORMAL] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_OR);
$this->WhereFilter[FLT_SEARCH] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_OR);
$this->WhereFilter[FLT_SEARCH]->setType(FLT_TYPE_OR);
$this->WhereFilter[FLT_VIEW] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_AND);
$this->HavingFilter[FLT_SYSTEM] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_AND);
$this->HavingFilter[FLT_NORMAL] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_OR);
$this->HavingFilter[FLT_SEARCH] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_OR);
$this->HavingFilter[FLT_SEARCH]->setType(FLT_TYPE_OR);
$this->HavingFilter[FLT_VIEW] =& $this->Application->makeClass('kMultipleFilter', FLT_TYPE_AND);
$this->PerPage = -1;
}
/**
* Adds new or replaces old filter with same name
*
* @param string $name filter name (for internal use)
* @param string $clause where/having clause part (no OR/AND allowed)
* @param int $filter_type is filter having filter or where filter
* @param int $filter_scope filter subtype: FLT_NORMAL,FLT_SYSTEM,FLT_SEARCH,FLT_VIEW
* @access public
*/
function addFilter($name, $clause, $filter_type = WHERE_FILTER, $filter_scope = FLT_SYSTEM)
{
$filter_name = ($filter_type == WHERE_FILTER) ? 'WhereFilter' : 'HavingFilter';
$filter =& $this->$filter_name;
$filter =& $filter[$filter_scope];
$filter->addFilter($name,$clause);
}
/**
* Removes specified filter from filters list
*
* @param string $name filter name (for internal use)
* @param int $filter_type is filter having filter or where filter
* @param int $filter_scope filter subtype: FLT_NORMAL,FLT_SYSTEM,FLT_SEARCH,FLT_VIEW
* @access public
*/
function removeFilter($name, $filter_type = WHERE_FILTER, $filter_scope = FLT_SYSTEM)
{
$filter_name = ($filter_type == WHERE_FILTER) ? 'WhereFilter' : 'HavingFilter';
$filter =& $this->$filter_name;
$filter =& $filter[$filter_scope];
$filter->removeFilter($name);
}
/**
* Clear list filters
*
* @param bool $user clear user filters
* @param bool $system clear system filters
*/
function clearFilters($user=true,$system=true,$search=true,$view=true)
{
if($system)
{
$this->WhereFilter[FLT_SYSTEM]->clearFilters();
$this->HavingFilter[FLT_SYSTEM]->clearFilters();
}
if($user)
{
$this->WhereFilter[FLT_NORMAL]->clearFilters();
$this->HavingFilter[FLT_NORMAL]->clearFilters();
}
if($search)
{
$this->WhereFilter[FLT_SEARCH]->clearFilters();
$this->HavingFilter[FLT_SEARCH]->clearFilters();
}
if($view)
{
$this->WhereFilter[FLT_VIEW]->clearFilters();
$this->HavingFilter[FLT_VIEW]->clearFilters();
}
}
/**
* Counts the total number of records base on the query resulted from {@link kDBList::GetSelectSQL()}
*
* The method modifies the query to substitude SELECT part (fields listing) with COUNT(*).
* Special care should be applied when working with lists based on grouped queries, all aggregate function fields
* like SUM(), AVERAGE() etc. should be added to CountedSQL by using {@link kDBList::SetCountedSQL()}
*
* @access public
* @param string
* @return void
*/
function CountRecs()
{
$all_sql = $this->GetSelectSQL(true,false);
$sql = $this->getCountSQL($all_sql);
$this->RecordsCount = (int)$this->Conn->GetOne($sql);
$system_sql = $this->GetSelectSQL(true,true);
if ($system_sql == $all_sql) { //no need to query the same again
$this->NoFilterCount = $this->RecordsCount;
return;
}
$sql = $this->getCountSQL($system_sql);
$this->NoFilterCount = (int)$this->Conn->GetOne($sql);
$this->Counted = true;
}
function getCountSQL($sql)
{
if ( preg_match("/DISTINCT(.*?)FROM(?!_)/is",$sql,$regs ) )
{
return preg_replace("/^.*SELECT DISTINCT(.*?)FROM(?!_)/is", "SELECT COUNT(DISTINCT ".$regs[1].") AS count FROM", $sql);
}
else
{
return preg_replace("/^.*SELECT(.*?)FROM(?!_)/is", "SELECT COUNT(*) AS count FROM ", $sql);
}
}
/**
* Queries the database with SQL resulted from {@link kDBList::GetSelectSQL()} and stores result in {@link kDBList::SelectRS}
*
* All the sorting, pagination, filtration of the list should be set prior to calling Query().
*
* @access public
* @param string
* @return void
*/
function Query($force=false)
{
if (!$force && $this->Queried) return true;
$q = $this->GetSelectSQL();
if ($this->DisplayQueries) {
echo get_class($this)." Query SQL: $q LIMIT ".$this->PerPage." OFFSET ".$this->Offset." Page: ".$this->Page."
";
}
//$rs = $this->Conn->SelectLimit($q, $this->PerPage, $this->Offset);
//in case we have not counted records try to select one more item to find out if we have something more than perpage
$limit = $this->Counted ? $this->PerPage : $this->PerPage+1;
$sql = $q.' '.$this->Conn->getLimitClause($this->Offset,$limit);
$this->Records = $this->Conn->Query($sql);
$this->SelectedCount = count($this->Records);
if (!$this->Counted) $this->RecordsCount = $this->SelectedCount;
if (!$this->Counted && $this->SelectedCount > $this->PerPage && $this->PerPage != -1) $this->SelectedCount--;
if ($this->Records === false) {
//handle errors here
return false;
}
$this->Queried = true;
return true;
}
/**
* Builds full select query except for LIMIT clause
*
* @access public
* @return string
*/
function GetSelectSQL($for_counting=false,$system_filters_only=false)
{
$q = parent::GetSelectSQL($this->SelectClause);
if(!$for_counting) $q = $this->addCalculatedFields($q);
$where = $this->GetWhereClause($for_counting,$system_filters_only);
$having = $this->GetHavingClause($for_counting,$system_filters_only);
$order = $this->GetOrderClause();
$group = $this->GetGroupClause();
if (!empty($where)) $q .= ' WHERE ' . $where;
if (!empty($group)) $q .= ' GROUP BY ' . $group;
if (!empty($having)) $q .= ' HAVING ' . $having;
if ( !$for_counting && !empty($order) ) $q .= ' ORDER BY ' . $order;
return str_replace('%1$s',$this->TableName,$q);
}
function extractCalculatedFields($clause)
{
if ( is_array($this->CalculatedFields) ) {
foreach($this->CalculatedFields as $field_name => $field_expression)
{
$clause = preg_replace('/`'.$field_name.'`/', $field_expression, $clause);
}
}
return $clause;
}
/**
* Returns WHERE clause of the query
*
* @access public
* @param bool $for_counting merge where filters with having filters + replace field names for having fields with their values
* @return string
*/
function GetWhereClause($for_counting=false,$system_filters_only=false)
{
$where =& $this->Application->makeClass('kMultipleFilter');
$where->addFilter('system_where', $this->WhereFilter[FLT_SYSTEM] );
if (!$system_filters_only) {
$where->addFilter('view_where', $this->WhereFilter[FLT_VIEW] );
$search_w = $this->WhereFilter[FLT_SEARCH]->getSQL();
if( $search_w || $for_counting ) // move search_having to search_where in case search_where isset or we are counting
{
$search_h = $this->extractCalculatedFields( $this->HavingFilter[FLT_SEARCH]->getSQL() );
$search_w = ($search_w && $search_h) ? $search_w.' OR '.$search_h : $search_w.$search_h;
$where->addFilter('search_where', $search_w );
}
}
if( $for_counting ) // add system_having and view_having to where
{
$where->addFilter('system_having', $this->extractCalculatedFields( $this->HavingFilter[FLT_SYSTEM]->getSQL() ) );
if (!$system_filters_only) $where->addFilter('view_having', $this->extractCalculatedFields( $this->HavingFilter[FLT_VIEW]->getSQL() ) );
}
return $where->getSQL();
}
/**
* Depricated method
*
* @param string $clause
* @todo REMOVE
*/
function SetWhereClause($clause)
{
if( $this->Application->isDebugMode() )
{
global $debugger;
$debugger->appendTrace();
}
trigger_error('Depricated method kDBList->SetWhereClause. Use kDBList->addFilter instead.', E_USER_ERROR);
}
/**
* Returns HAVING clause of the query
*
* @param bool $for_counting don't return having filter in case if this is counting sql
* @return string
* @access public
*/
function GetHavingClause($for_counting=false, $system_filters_only=false)
{
if( $for_counting ) return '';
$having =& $this->Application->makeClass('kMultipleFilter');
$having->addFilter('system_having', $this->HavingFilter[FLT_SYSTEM] );
if (!$system_filters_only) {
$having->addFilter('view_having', $this->HavingFilter[FLT_VIEW] );
$search_w = $this->WhereFilter[FLT_SEARCH]->getSQL();
if (!$search_w) {
$having->addFilter('search_having', $this->HavingFilter[FLT_SEARCH] );
}
}
return $having->getSQL();
}
/**
* Returns GROUP BY clause of the query
*
* @access public
* @return string
*/
function GetGroupClause()
{
return $this->GroupByFields ? implode(',', $this->GroupByFields) : '';
}
function AddGroupByField($field)
{
$this->GroupByFields[$field] = $field;
}
function RemoveGroupByField($field)
{
unset($this->GroupByFields[$field]);
}
/**
* Adds order field to ORDER BY clause
*
* @access public
* @param string $field Field name
* @param string $direction Direction of ordering (asc|desc)
* @return void
*/
function AddOrderField($field, $direction)
{
// original multilanguage field - convert to current lang field
if (getArrayValue($this->Fields, $field, 'formatter') == 'kMultiLanguage' && !getArrayValue($this->Fields, $field, 'master_field')) {
$lang = $this->Application->GetVar('m_lang');
$field = 'l'.$lang.'_'.$field;
}
$this->OrderFields[] = Array($field, $direction);
}
/**
* Removes all order fields
*
* @access public
* @return void
*/
function ClearOrderFields()
{
$this->OrderFields = Array();
}
/**
* Returns ORDER BY Clause of the query
*
* The method builds order by clause by iterating {@link kDBList::OrderFields} array and concatenating it.
*
* @access public
* @return string
*/
function GetOrderClause()
{
$ret = '';
foreach ($this->OrderFields as $field) {
$name = $field[0];
$ret .= isset($this->Fields[$name]) && !isset($this->VirtualFields[$name]) ? '`'.$this->TableName.'`.' : '';
if ($field[0] == 'RAND()') {
$ret .= $field[0].' '.$field[1].',';
}
else {
$ret .= '`'.$field[0] . '` ' . $field[1] . ',';
}
}
$ret = rtrim($ret, ',');
return $ret;
}
function GetOrderField($pos=NULL)
{
if(!(isset($this->OrderFields[$pos]) && $this->OrderFields[$pos]) )
{
$pos = 0;
}
return isset($this->OrderFields[$pos][0]) ? $this->OrderFields[$pos][0] : '';
}
function GetOrderDirection($pos=NULL)
{
if(!$this->OrderFields[$pos])
$pos = 0;
return $this->OrderFields[$pos][1];
}
/**
* Return unformatted field value
*
* @param string
* @return mixed
* @access public
*/
function GetDBField($name)
{
$row =& $this->getCurrentRecord();
return $row[$name];
}
function HasField($name)
{
$row =& $this->getCurrentRecord();
return isset($row[$name]);
}
function GetFieldValues()
{
return $this->getCurrentRecord();
}
function &getCurrentRecord()
{
return $this->Records[$this->CurrentIndex];
}
/**
* Description
*
* @access public
* @param string
* @return void
*/
function GoFirst()
{
$this->CurrentIndex = 0;
}
/**
* Description
*
* @access public
* @return void
*/
function GoNext()
{
$this->CurrentIndex++;
}
/**
* Description
*
* @access public
* @return void
*/
function GoPrev()
{
if ($this->CurrentIndex>0)
$this->CurrentIndex--;
}
/**
* Description
*
* @access public
* @return bool
*/
function EOL()
{
return ($this->CurrentIndex >= $this->SelectedCount);
}
/**
* Description
*
* @access public
* @param string
* @return void
*/
function GetTotalPages()
{
if (!$this->Counted) $this->CountRecs();
if ($this->PerPage == -1) return 1;
$this->TotalPages = (($this->RecordsCount - ($this->RecordsCount % $this->PerPage)) / $this->PerPage) // integer part of division
+ (($this->RecordsCount % $this->PerPage) != 0); // adds 1 if there is a reminder
return $this->TotalPages;
}
/**
* Sets number of records to query per page
*
* @access public
* @param int $per_page Number of records to display per page
* @return void
*/
function SetPerPage($per_page)
{
$this->PerPage = $per_page;
}
function GetPerPage()
{
return $this->PerPage == -1 ? $this->RecordsCount : $this->PerPage;
}
/**
* Description
*
* @access public
* @param int $page
* @return void
*/
function SetPage($page)
{
if ($this->PerPage == -1) {
$this->Page = 1;
return;
}
if ($page < 1) $page = 1;
$this->Offset = ($page-1)*$this->PerPage;
if ($this->Counted && $this->Offset > $this->RecordsCount) {
$this->SetPage(1);
}
else {
$this->Page = $page;
}
//$this->GoFirst();
}
/**
* Sets current item field value
* (doesn't apply formatting)
*
* @access public
* @param string $name Name of the field
* @param mixed $value Value to set the field to
* @return void
*/
function SetDBField($name,$value)
{
$this->Records[$this->CurrentIndex][$name] = $value;
}
/**
* Apply where clause, that links this object to it's parent item
*
* @param string $special
* @access public
*/
function linkToParent($special)
{
$parent_prefix = $this->Application->getUnitOption($this->Prefix, 'ParentPrefix');
if($parent_prefix)
{
$parent_table_key = $this->Application->getUnitOption($this->Prefix, 'ParentTableKey');
$foreign_key_field = $this->Application->getUnitOption($this->Prefix, 'ForeignKey');
$parent_object =& $this->Application->recallObject($parent_prefix.'.'.$special);
$parent_id = $parent_object->GetDBField($parent_table_key);
$this->addFilter('parent_filter', '`'.$this->TableName.'`.`'.$foreign_key_field.'` = '.$parent_id); // only for list in this case
}
}
}
?>