|
<?php |
|
|
|
// |
|
// Open Web Analytics - An Open Source Web Analytics Framework |
|
// |
|
// Copyright 2006 Peter Adams. All rights reserved. |
|
// |
|
// Licensed under GPL v2.0 http://www.gnu.org/copyleft/gpl.html |
|
// |
|
// Unless required by applicable law or agreed to in writing, software |
|
// distributed under the License is distributed on an "AS IS" BASIS, |
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
// |
|
// $Id$ |
|
// |
|
|
|
require_once(OWA_BASE_CLASS_DIR.'pagination.php'); |
|
require_once(OWA_BASE_CLASS_DIR.'timePeriod.php'); |
|
require_once(OWA_DIR.'owa_template.php'); |
|
|
|
/** |
|
* Result Set Manager |
|
* |
|
* Responsible for creating a data result set from various metrics and dimensions |
|
* |
|
* @author Peter Adams <peter@openwebanalytics.com> |
|
* @copyright Copyright © 2006 Peter Adams <peter@openwebanalytics.com> |
|
* @license http://www.gnu.org/copyleft/gpl.html GPL v2.0 |
|
* @category owa |
|
* @package owa |
|
* @version $Revision$ |
|
* @since owa 1.3.0 |
|
*/ |
|
|
|
class owa_resultSetManager extends owa_base { |
|
|
|
/** |
|
* The params of the caller, either a report or graph |
|
* |
|
* @var array |
|
*/ |
|
var $params = array(); |
|
|
|
/** |
|
* The lables for calculated measures |
|
* |
|
* @var array |
|
*/ |
|
var $labels = array(); |
|
|
|
/** |
|
* Data Access Object |
|
* |
|
* @var object |
|
*/ |
|
var $db; |
|
|
|
/** |
|
* The dimensions to groupby |
|
* |
|
* @var array |
|
*/ |
|
var $dimensions = array(); |
|
|
|
/** |
|
* The Number of Dimensions to groupby |
|
* |
|
* @var integer |
|
*/ |
|
var $dimensionCount; |
|
|
|
/** |
|
* The table/column or denormalized dimensions |
|
* associated with this metric |
|
* |
|
* @var array |
|
*/ |
|
var $denormalizedDimensions = array(); |
|
|
|
var $_default_offset = 0; |
|
var $page; |
|
var $limit; |
|
var $order; |
|
var $format; |
|
var $constraint_operators = array('==','!=','>=', '<=', '>', '<', '=~', '!~', '=@','!@'); |
|
var $related_entities = array(); |
|
var $related_dimensions = array(); |
|
var $related_metrics = array(); |
|
var $resultSet; |
|
var $base_table; |
|
var $metrics = array(); |
|
var $metricsByTable = array(); |
|
var $childMetrics = array(); |
|
var $calculatedMetrics = array(); |
|
var $query_params = array(); |
|
var $baseEntity; |
|
var $metricObjectsByEntityMap = array(); |
|
var $errors = array(); |
|
var $formatters = array(); |
|
|
|
function __construct($db = '') { |
|
|
|
if ($db) { |
|
$this->db = $db; |
|
} else { |
|
$this->db = owa_coreAPI::dbSingleton(); |
|
} |
|
|
|
$this->formatters = array( |
|
//'yyyymmdd' => array($this, 'dateFormatter'), |
|
'timestamp' => array($this, 'formatSeconds'), |
|
'percentage' => array($this, 'formatPercentage'), |
|
'integer' => array($this, 'numberFormatter'), |
|
'currency' => array($this, 'formatCurrency') |
|
); |
|
|
|
return parent::__construct(); |
|
} |
|
|
|
|
|
function setConstraint($name, $value, $operator = '') { |
|
|
|
if (empty($operator)) { |
|
$operator = '='; |
|
} |
|
|
|
if (!empty($value)) { |
|
$this->params['constraints'][] = array('operator' => $operator, 'value' => $value, 'name' => $name); |
|
} |
|
} |
|
|
|
function setConstraints($array) { |
|
|
|
if (is_array($array)) { |
|
|
|
if (is_array($this->params['constraints'])) { |
|
$this->params['constraints'] = array_merge($array, $this->params['constraints']); |
|
} else { |
|
$this->params['constraints'] = $array; |
|
} |
|
} |
|
} |
|
|
|
function constraintsStringToArray($string) { |
|
|
|
if ($string) { |
|
//print_r($string); |
|
// add string to query params array for use in URLs. |
|
$this->query_params['constraints'] = $string; |
|
|
|
$constraints = explode(',', $string); |
|
//print_r($constraints); |
|
$constraint_array = array(); |
|
|
|
foreach($constraints as $constraint) { |
|
|
|
foreach ($this->constraint_operators as $operator) { |
|
|
|
if (strpos($constraint, $operator)) { |
|
list ($name, $value) = split($operator, $constraint); |
|
|
|
$constraint_array[] = array('name' => $name, 'value' => $value, 'operator' => $operator); |
|
|
|
|
|
break; |
|
} |
|
} |
|
} |
|
//print_r($constraint_array); |
|
return $constraint_array; |
|
} |
|
} |
|
|
|
function getConstraints() { |
|
|
|
return $this->params['constraints']; |
|
} |
|
|
|
function applyConstraints() { |
|
|
|
$nconstraints = array(); |
|
|
|
foreach ($this->getConstraints() as $k => $constraint) { |
|
|
|
$dim = $this->lookupDimension($constraint['name'], $this->baseEntity); |
|
|
|
//$dimEntity = owa_coreAPI::entityFactory($dim['entity']); |
|
|
|
|
|
$col = $dim['column']; |
|
$constraint['name'] = $col; |
|
$nconstraints[$col] = $constraint; |
|
$this->db->multiWhere($nconstraints); |
|
//print_r($nconstraints); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
function chooseBaseEntity() { |
|
|
|
$metric_imps = array(); |
|
|
|
// load metric implementations |
|
foreach ($this->metrics as $metric_name) { |
|
|
|
$metric_imps = array_merge($this->getMetricEntities($metric_name), $metric_imps); |
|
|
|
|
|
} |
|
//print_r($metric_imps); |
|
owa_coreAPI::debug('pre-reduce set of entities to choose from: '.print_r($metric_imps, true)); |
|
|
|
$entities = array(); |
|
// reduce entities |
|
foreach ($metric_imps as $mimp) { |
|
|
|
if (empty($entities)) { |
|
$entities = $mimp; |
|
} |
|
|
|
$entities = $this->reduceTables($mimp, $entities); |
|
|
|
if (empty($entities)) { |
|
return $this->addError('illegal metric combination'); |
|
} |
|
} |
|
|
|
owa_coreAPI::debug('post-reduce set of entities to choose from: '.print_r($entities, true)); |
|
|
|
// check summary level of entities |
|
$niceness = array(); |
|
|
|
foreach ($entities as $entity) { |
|
|
|
$niceness[$entity] = owa_coreAPI::entityFactory($entity)->getSummaryLevel(); |
|
} |
|
//sort by summary level |
|
arsort($niceness); |
|
|
|
owa_coreAPI::debug('Entities summary levels: '.print_r($niceness, true)); |
|
|
|
$entity_count = count($niceness); |
|
$i = 1; |
|
//check entities for dimension relations |
|
foreach ($niceness as $entity_name => $summary_level) { |
|
|
|
$error = false; |
|
|
|
//cycle through each dimension frm dim list and those found in constraints. |
|
$dims = array_unique(array_merge($this->dimensions, $this->getDimensionsFromConstraints())); |
|
|
|
owa_coreAPI::debug(sprintf('Dimensions: %s',print_r($this->dimensions, true))); |
|
|
|
owa_coreAPI::debug(sprintf('Checking the following dimensions for relation to %s: %s',$entity_name, print_r($dims, true))); |
|
|
|
foreach ($dims as $dimension) { |
|
|
|
$check = $this->isDimensionRelated($dimension, $entity_name); |
|
|
|
// is the realtionship check fails then move onto the next entity. |
|
if (!$check) { |
|
$error = true; |
|
owa_coreAPI::debug("$dimension is not related to $entity_name. Moving on to next entity..."); |
|
break; |
|
} else { |
|
owa_coreAPI::debug("Dimension: $dimension is related to $entity_name."); |
|
} |
|
} |
|
|
|
// is no error then everythig is related and we are good to go. |
|
if (!$error) { |
|
owa_coreAPI::debug('optimal base entity is: '.$entity_name); |
|
$this->baseEntity = owa_coreAPI::entityFactory($entity_name); |
|
return $this->baseEntity; |
|
} |
|
|
|
if ($i === $entity_count) { |
|
$this->addError('illegal dimension combination: '.$dimension); |
|
} else { |
|
$i++; |
|
} |
|
} |
|
} |
|
|
|
function getDimensionsFromConstraints() { |
|
|
|
$dims = array(); |
|
|
|
$constraints = $this->getConstraints(); |
|
//print_r($constraints); |
|
if (!empty($constraints)) { |
|
|
|
foreach ($constraints as $carray) { |
|
|
|
$dims[] = $carray['name']; |
|
} |
|
} |
|
|
|
return $dims; |
|
} |
|
|
|
function isDimensionRelated($dimension_name, $entity_name) { |
|
|
|
$entity = owa_coreAPI::entityFactory($entity_name); |
|
|
|
$dimension = $this->lookupDimension($dimension_name, $entity); |
|
|
|
if ($dimension['denormalized'] === true) { |
|
$this->related_dimensions[$dimension['name']] = $dimension; |
|
owa_coreAPI::debug("Dimension: $dimension_name is denormalized into $entity_name"); |
|
return true; |
|
} else { |
|
|
|
$fk = $this->getDimensionForeignKey($dimension, $entity); |
|
|
|
if ($fk) { |
|
owa_coreAPI::debug("Dimension: $dimension_name is related to $entity_name"); |
|
$this->related_dimensions[$dimension['name']] = $dimension; |
|
return true; |
|
} else { |
|
owa_coreAPI::debug("Could not find a foreign key for $dimension_name in $entity_name"); |
|
} |
|
} |
|
} |
|
|
|
function getMetricEntities($metric_name) { |
|
|
|
//get the class implementations |
|
$s = owa_coreAPI::serviceSingleton(); |
|
$classes = $s->getMetricClasses($metric_name); |
|
|
|
$entities = array(); |
|
|
|
// cycles through metric classes and get their entity names |
|
foreach ($classes as $name => $map) { |
|
$m = owa_coreAPI::metricFactory($map['class'], $map['params']); |
|
|
|
// check to see if this is a calculated metric |
|
if ($m->isCalculated()) { |
|
|
|
foreach ($m->getChildMetrics() as $cmetric_name) { |
|
$this->addCalculatedMetric($m); |
|
$entities = array_merge($this->getMetricEntities($cmetric_name), $entities); |
|
} |
|
|
|
} else { |
|
$this->metricObjectsByEntityMap[$m->getEntityName()][$metric_name] = $m; |
|
$entities[$metric_name][] = $m->getEntityName(); |
|
} |
|
|
|
} |
|
|
|
return $entities; |
|
} |
|
|
|
function reduceTables($new, $old) { |
|
|
|
return array_intersect($new, $old); |
|
} |
|
|
|
function getDimensionForeignKey($dimension, $entity) { |
|
|
|
if ($dimension) { |
|
//$entity = ; |
|
$dim = $dimension; |
|
$fk = array(); |
|
// check for foreign key column by name if dimension specifies one |
|
if (array_key_exists('foreign_key_name', $dim) && !empty($dim['foreign_key_name'])) { |
|
// get foreign key col by |
|
if ($entity->isForeignKeyColumn($dim['foreign_key_name'])){ |
|
$fk = array('col' => $dim['foreign_key_name'], 'entity' => $entity); |
|
} |
|
|
|
} else { |
|
// if not check for foreign key by entity name |
|
//check to see if the metric's entity has a foreign key to the dimenesion table. |
|
$fk = array(); |
|
|
|
$fkcol = $entity->getForeignKeyColumn($dim['entity']); |
|
owa_coreAPI::debug("Foreign Key check: ". print_r($fkcol, true)); |
|
if ($fkcol) { |
|
$fk['col'] = $fkcol; |
|
$fk['entity'] = $entity; |
|
} |
|
} |
|