Skip to content
Extraits de code Groupes Projets
DataTable.php 47,9 ko
Newer Older
            $row->cleanPostSerialize();
        }

        return $aSerializedDataTable;
    }

    /**
     * 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
     * @throws Exception
     */
    public function addRowsFromSerializedArray($stringSerialized)
    {
        $serialized = unserialize($stringSerialized);
        if ($serialized === false) {
            throw new Exception("The unserialization has failed!");
        }
        $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( ... ),
     *                       )
     */
    public function addRowsFromArray($array)
    {
        foreach ($array as $id => $row) {
            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, ...),
     *                       )
     * @throws Exception
sgiehl's avatar
sgiehl a validé
     * @return void
     */
    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)) {
                        throw $e;
                    }
                }
                $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
     */
    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));
        }
    }

    /**
     * Set the array of parent ids
     *
     * @param array $parents
     */
    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.
     *
     * @param int $atLeastLevel
     */
    public static 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)
    {
        if (!isset($this->metadata[$name])) {
            return false;
        }
        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.
     *
sgiehl's avatar
sgiehl a validé
     * @param array        $path            The path to walk. An array of label values.
     * @param array|bool   $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);

                    if ($next !== $row) // if the row wasn't added, the table is full
                    {
                        // 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);
                    // Summary row, has no metadata
                    $next->deleteMetadata();
                }
            }
        }

        return array($next, $i);
    }

    /**
sgiehl's avatar
sgiehl a validé
     * Returns a new DataTable that contains the rows of each of this table's subtables.
sgiehl's avatar
sgiehl a validé
     * @param string|bool  $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/
sgiehl's avatar
sgiehl a validé
     *                                        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.
     *
     * @return Piwik_DataTable
     */
    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);
                    }
                }
            }
        }
        return $result;
    }

    /**
     * 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;
    }