Skip to content
Extraits de code Groupes Projets
DataTable.php 41,2 ko
Newer Older
sgiehl's avatar
sgiehl a validé
	 * 					0 => 'eghuighahgaueytae78yaet7yaetae',
	 *
	 * 					// first Datatable level1
	 * 					1 => 'gaegae gh gwrh guiwh uigwhuige',
sgiehl's avatar
sgiehl a validé
	 *
	 * 					//second Datatable level1
	 * 					2 => 'gqegJHUIGHEQjkgneqjgnqeugUGEQHGUHQE',
	 *
	 * 					//first Datatable level3 (child of second Datatable level1 for example)
 	 *					3 => 'eghuighahgaueytae78yaet7yaetaeGRQWUBGUIQGH&QE',
	 * 					);
	 */
	public function getSerialized(	$maximumRowsInDataTable = null, 
									$maximumRowsInSubDataTable = null,
									$columnToSortByBeforeTruncation = null )
		if($depth > self::$maximumDepthLevelAllowed)
			throw new Exception("Maximum recursion level of ".self::$maximumDepthLevelAllowed. " reached. Maybe you have set a DataTable_Row with an associated DataTable belonging already to one of its parent tables?");
mattpiwik's avatar
mattpiwik a validé
			$this->filter('AddSummaryRow', 
							array(	$maximumRowsInDataTable - 1, 
									Piwik_DataTable::LABEL_SUMMARY_ROW, 
									$columnToSortByBeforeTruncation)
					);
		}
		
		// For each row, get the serialized row
		// If it is associated to a sub table, get the serialized table recursively ;
		// but returns all serialized tables and subtable in an array of 1 dimension
		$aSerializedDataTable = array();
		foreach($this->rows as $row)
		{
			if(($idSubTable = $row->getIdSubDataTable()) !== null)
			{
				$subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable);
				$depth++;
				$aSerializedDataTable = $aSerializedDataTable + $subTable->getSerialized( $maximumRowsInSubDataTable, $maximumRowsInSubDataTable, $columnToSortByBeforeTruncation );
				$depth--;
			}
		}
		// we load the current Id of the DataTable
		$forcedId = $this->getId();
		
		// if the datatable is the parent we force the Id at 0 (this is part of the specification)
		if($depth == 0)
		{
			$forcedId = 0;
		}
		
		// we then serialize the rows and store them in the serialized dataTable
		$addToRows = array( self::ID_SUMMARY_ROW => $this->summaryRow );
		if ($this->parents && Piwik_Config::getInstance()->General['enable_archive_parents_of_datatable'])
		{
			$addToRows[self::ID_PARENTS] = $this->parents;
		}
		$aSerializedDataTable[$forcedId] = serialize($this->rows + $addToRows);
		foreach($this->rows as &$row)
		{
			$row->cleanPostSerialize();
		}
		
sgiehl's avatar
sgiehl a validé
	/**
	 * Load a serialized string of a datatable.
	 *
	 * Does not load recursively all the sub DataTable.
	 * They will be loaded only when requesting them specifically.
	 *
	 * The function creates all the necessary DataTable_Row
	 *
	 * @param string  $stringSerialized  string of serialized datatable
sgiehl's avatar
sgiehl a validé
	 * @throws Exception
	 */
mattpiwik's avatar
mattpiwik a validé
	public function addRowsFromSerializedArray( $stringSerialized )
	{
		$serialized = unserialize($stringSerialized);
		if($serialized === false)
		{
			throw new Exception("The unserialization has failed!");
		}
mattpiwik's avatar
mattpiwik a validé
		$this->addRowsFromArray($serialized);
	}

	/**
	 * Loads the DataTable from a PHP array data structure
	 * 
	 * @param array  $array  Array with the following structure
	 *                       array(
 	 *                             // row1
	 *                             array(
	 *                                   Piwik_DataTable_Row::COLUMNS => array( col1_name => value1, col2_name => value2, ...),
	 *                                   Piwik_DataTable_Row::METADATA => array( metadata1_name => value1,  ...), // see Piwik_DataTable_Row
	 *                             ),
	 *                             // row2
	 *                             array( ... ),
	 *                       )
mattpiwik's avatar
mattpiwik a validé
	public function addRowsFromArray( $array )
			if($id == self::ID_PARENTS)
			{
				$this->parents = $row;
				continue;
			}
						
			if(is_array($row))
			{
				$row = new Piwik_DataTable_Row($row);
			}
			if($id == self::ID_SUMMARY_ROW)
			{
				$this->summaryRow = $row;
			}
			else 
			{
				$this->addRow($row);
			}
		}
	}

	/**
	 * Loads the data from a simple php array.
	 * Basically maps a simple multidimensional php array to a DataTable.
	 * Not recursive (if a row contains a php array itself, it won't be loaded)
	 * @param array  $array  Array with the simple structure:
	 *                       array(
	 *                             array( col1_name => valueA, col2_name => valueC, ...),
	 *                             array( col1_name => valueB, col2_name => valueD, ...),
	 *                       )
sgiehl's avatar
sgiehl a validé
	 * @throws Exception
	 * @return
mattpiwik's avatar
mattpiwik a validé
	public function addRowsFromSimpleArray( $array )
	{
		if(count($array) === 0)
		{
			return;
		}
		
		// we define an exception we may throw if at one point we notice that we cannot handle the data structure
		$e = new Exception(" Data structure returned is not convertible in the requested format.".
						" Try to call this method with the parameters '&format=original&serialize=1'".
						"; you will get the original php data structure serialized.".
						" The data structure looks like this: \n \$data = " . var_export($array, true) . "; ");
				
		
		// first pass to see if the array has the structure
		// array(col1_name => val1, col2_name => val2, etc.)
		// with val* that are never arrays (only strings/numbers/bool/etc.)
		// if we detect such a "simple" data structure we convert it to a row with the correct columns' names
		$thisIsNotThatSimple = false;
		
		foreach($array as $columnValue )
		{
			if(is_array($columnValue) || is_object($columnValue)) 
			{
				$thisIsNotThatSimple = true;
				break;
			}
		}
		if($thisIsNotThatSimple === false)
		{
			// case when the array is indexed by the default numeric index
			if( array_keys($array) == array_keys(array_fill(0, count($array), true)) )
			{
				foreach($array as $row)
				{
					$this->addRow( new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS => array($row) ) ) );					
				}
			}
			else
			{
				$this->addRow( new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS => $array ) ) );
			}
			// we have converted our simple array to one single row
			// => we exit the method as the job is now finished 
			return;
		}
		
		foreach($array as $key => $row)
		{
			// stuff that looks like a line
			if(is_array($row))
			{
				/**
				 * We make sure we can convert this PHP array without losing information.
				 * We are able to convert only simple php array (no strings keys, no sub arrays, etc.)
				 * 
				 */
				
				// if the key is a string it means that some information was contained in this key. 
				// it cannot be lost during the conversion. Because we are not able to handle properly
				// this key, we throw an explicit exception.
				if(is_string($key))
				{
					throw $e;
				}
				// if any of the sub elements of row is an array we cannot handle this data structure...
				foreach($row as $subRow)
				{
					if(is_array($subRow))
					{
					}
				}
				$row = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS => $row ) );		
			}
			// other (string, numbers...) => we build a line from this value
			else
			{
				$row = new Piwik_DataTable_Row( array( Piwik_DataTable_Row::COLUMNS => array($key => $row)) );
			}				
			$this->addRow($row);
		}
	}

	/**
	 * Rewrites the input $array 
	 * array (
	 * 	 LABEL => array(col1 => X, col2 => Y),
	 * 	 LABEL2 => array(col1 => X, col2 => Y),
	 * )
	 * to the structure 
	 * array (
	 * 	 array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL, col1 => X, col2 => Y)),
	 * 	 array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL2, col1 => X, col2 => Y)),
	 * )
	 * 
	 * It also works with array having only one value per row, eg.
	 * array (
	 * 	 LABEL => X,
	 * 	 LABEL2 => Y,
	 * )
	 * would be converted to the structure 
	 * array (
	 * 	 array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL, 'value' => X)),
	 * 	 array( Piwik_DataTable_Row::COLUMNS => array('label' => LABEL2, 'value' => Y)),
	 * )
	 * 
	 * The optional parameter $subtablePerLabel is an array of subTable associated to the rows of the $array
	 * For example if $subtablePerLabel is given
	 * array(
	 * 		LABEL => #Piwik_DataTable_ForLABEL,
	 * 		LABEL2 => #Piwik_DataTable_ForLABEL2,
	 * )
	 * 
	 * the $array would become 
	 * array (
	 * 	 array( 	Piwik_DataTable_Row::COLUMNS => array('label' => LABEL, col1 => X, col2 => Y),
	 * 				Piwik_DataTable_Row::DATATABLE_ASSOCIATED => #ID DataTable For LABEL
	 * 		),
	 * 	 array( 	Piwik_DataTable_Row::COLUMNS => array('label' => LABEL2, col1 => X, col2 => Y)
	 * 				Piwik_DataTable_Row::DATATABLE_ASSOCIATED => #ID2 DataTable For LABEL2
	 * 		),
	 * )
	 * 
	 * @param array       $array             See method description
	 * @param array|null  $subtablePerLabel  See method description
mattpiwik's avatar
mattpiwik a validé
	public function addRowsFromArrayWithIndexLabel( $array, $subtablePerLabel = null)
	{
		$cleanRow = array();
		foreach($array as $label => $row)
		{
			if(!is_array($row))
			{
				$row = array('value' => $row);
			}
			$cleanRow[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = null;
			// we put the 'label' column first as it looks prettier in API results
			$cleanRow[Piwik_DataTable_Row::COLUMNS] = array('label' => $label) + $row;
			if(!is_null($subtablePerLabel)
				// some rows of this table don't have subtables 
				// (for example case of campaigns without keywords)
				&& isset($subtablePerLabel[$label]) 
			)
			{
				$cleanRow[Piwik_DataTable_Row::DATATABLE_ASSOCIATED] = $subtablePerLabel[$label];
			}
			$this->addRow( new Piwik_DataTable_Row($cleanRow) );
		}
	}
	 */
	public function setParents($parents)
	{
		$this->parents = $parents;
	}
	
	/**
	 * Get parents
	 *
	 * @return array  of all parents, root level first
	 */
	public function getParents() {
		if ($this->parents == null)
		{
			return array();
		}
		return $this->parents;
	}
	
	/**
	 * Sets the maximum nesting level to at least a certain value. If the current value is
	 * greater than the supplied level, the maximum nesting level is not changed.
	 * 
	 */
	static public function setMaximumDepthLevelAllowedAtLeast( $atLeastLevel )
	{
		self::$maximumDepthLevelAllowed = max($atLeastLevel, self::$maximumDepthLevelAllowed);
		if(self::$maximumDepthLevelAllowed < 1) {
			self::$maximumDepthLevelAllowed = 1;
		}
	
	/**
	 * Returns all table metadata.
	 * 
	 * @return array
	 */
	public function getAllTableMetadata()
	{
		return $this->metadata;
	}
	
	/**
	 * Returns metadata by name.
	 * 
	 * @param string $name The metadata name.
	 * @return mixed
	 */
	public function getMetadata( $name )
	{
		return $this->metadata[$name];
	}
	
	/**
	 * Sets a metadata value by name.
	 * 
	 * @param string $name The metadata name.
	 * @param mixed $value
	 */
	public function setMetadata( $name, $value )
	{
		$this->metadata[$name] = $value;
	}
	
	/**
	 * Sets the maximum number of rows allowed in this datatable (including the summary
	 * row). If adding more then the allowed number of rows is attempted, the extra
	 * rows are added to the summary row.
	 * 
	 * @param int|null $maximumAllowedRows
	 */
	public function setMaximumAllowedRows( $maximumAllowedRows )
	{
		$this->maximumAllowedRows = $maximumAllowedRows;
	}
	
	/**
	 * Traverses a DataTable tree using an array of labels and returns the row
	 * it finds or false if it cannot find one, and the number of segments of
	 * the path successfully walked.
	 * 
	 * If $missingRowColumns is supplied, the specified path is created. When
	 * a subtable is encountered w/o the queried label, a new row is created
	 * with the label, and a subtable is added to the row.
	 * 
	 * @param array $path The path to walk. An array of label values.
	 * @param array|false $missingRowColumns
	 *						The default columns to use when creating new arrays.
	 * 						If this parameter is supplied, new rows will be
	 * 						created if labels cannot be found.
	 * @param int $maxSubtableRows The maximum number of allowed rows in new
	 *                             subtables.
	 * @return array First element is the found row or false. Second element is
	 *               the number of path segments walked. If a row is found, this
	 *               will be == to count($path). Otherwise, it will be the index
	 *               of the path segment that we could not find.
	 */
	public function walkPath( $path, $missingRowColumns = false, $maxSubtableRows = 0 )
	{
		$pathLength = count($path);
		
		$table = $this;
		$next = false;
		for ($i = 0; $i < $pathLength; ++$i)
		{
			$segment = $path[$i];
			
			$next = $table->getRowFromLabel($segment);
			if ($next === false)
			{
				// if there is no table to advance to, and we're not adding missing rows, return false
				if ($missingRowColumns === false)
				{
					return array(false, $i);
				}
				else // if we're adding missing rows, add a new row
				{
					$row = new Piwik_DataTable_Row_DataTableSummary();
					$row->setColumns(array('label' => $segment) + $missingRowColumns);
					
					$next = $table->addRow($row);
mattpiwik's avatar
 
mattpiwik a validé

					if ($next !== $row) // if the row wasn't added, the table is full
					{
mattpiwik's avatar
 
mattpiwik a validé
						// Summary row, has no metadata
						$next->deleteMetadata();
						return array($next, $i);
					}
				}
			}
			
			$table = $next->getSubtable();
			if ($table === false)
			{
				// if the row has no table (and thus no child rows), and we're not adding
				// missing rows, return false
				if ($missingRowColumns === false)
				{
					return array(false, $i);
				}
				else if ($i != $pathLength - 1) // create subtable if missing, but only if not on the last segment
				{
					$table = new Piwik_DataTable();
					$table->setMaximumAllowedRows($maxSubtableRows);
					$next->setSubtable($table);
mattpiwik's avatar
 
mattpiwik a validé
					// Summary row, has no metadata
					$next->deleteMetadata();

	/**
	 * Returns a new DataTable that contains the rows of each of this table's
	 * subtables.
	 * 
	 * @param string|false $labelColumn If supplied the label of the parent row will be
	 *                                  added to a new column in each subtable row. If set to,
	 *                                  'label' each subtable row's label will be prepended w/
	 *                                  the parent row's label.
	 * @param bool $useMetadataColumn If true and if $labelColumn is supplied, the parent row's
	 *                                label will be added as metadata.
	public function mergeSubtables( $labelColumn = false, $useMetadataColumn = false )
	{
		$result = new Piwik_DataTable();
		foreach ($this->getRows() as $row)
		{
			$subtable = $row->getSubtable();
			if ($subtable !== false)
			{
				$parentLabel = $row->getColumn('label');
				
				// add a copy of each subtable row to the new datatable
				foreach ($subtable->getRows() as $id => $subRow)
				{
					$copy = clone $subRow;
					
					// if the summary row, add it to the existing summary row (or add a new one)
					if ($id == self::ID_SUMMARY_ROW)
					{
						$existing = $result->getRowFromId(self::ID_SUMMARY_ROW);
						if ($existing === false)
						{
							$result->addSummaryRow($copy);
						}
						else
						{
							$existing->sumRow($copy);
						}
					}
					else
					{
						if ($labelColumn !== false)
						{
							// if we're modifying the subtable's rows' label column, then we make
							// sure to prepend the existing label w/ the parent row's label. otherwise
							// we're just adding the parent row's label as a new column/metadata.
							$newLabel = $parentLabel;
							if ($labelColumn == 'label')
							{
								$newLabel .= ' - '.$copy->getColumn('label');
							}
					
							// modify the child row's label or add new column/metadata
							if ($useMetadataColumn)
							{
								$copy->setMetadata($labelColumn, $newLabel);
							}
							else
							{
								$copy->setColumn($labelColumn, $newLabel);
							}
						}
					
						$result->addRow($copy);
					}
				}
	
	/**
	 * Returns a new DataTable created with data from a 'simple' array.
	 * 
	 * @param array $array
	 * @return Piwik_DataTable
	 */
	public static function makeFromSimpleArray( $array )
	{
		$dataTable = new Piwik_DataTable();
		$dataTable->addRowsFromSimpleArray($array);
		return $dataTable;
	}