Column name] pairs * * @var array */ protected $gridcols = []; /** * Create a new pivot table * * @param Query $query The query to fetch as pivot table * @param string $xAxisColumn X-axis pivot column * @param string $yAxisColumn Y-axis pivot column * @param array $gridcols Grid columns */ public function __construct(Query $query, string $xAxisColumn, string $yAxisColumn, array $gridcols) { foreach ($query->getOrderBy() as $sort) { $this->order[$sort[0]] = $sort[1]; } $this->baseQuery = $query->columns($gridcols)->resetOrderBy(); $this->xAxisColumn = $xAxisColumn; $this->yAxisColumn = $yAxisColumn; $this->gridcols = $gridcols; } /** * Set the filter to apply on the query for the x-axis * * @param Filter\Rule $filter * * @return $this */ public function setXAxisFilter(Filter\Rule $filter = null): self { $this->xAxisFilter = $filter; return $this; } /** * Set the filter to apply on the query for the y-axis * * @param Filter\Rule $filter * * @return $this */ public function setYAxisFilter(Filter\Rule $filter = null): self { $this->yAxisFilter = $filter; return $this; } /** * Get the x-axis header * * Defaults to {@link $xAxisColumn} in case no x-axis header has been set using {@link setXAxisHeader()} * * @return string */ public function getXAxisHeader(): string { if ($this->xAxisHeader === null && $this->xAxisColumn === null) { throw new \LogicException( 'You are accessing an unset property. Please make sure to set it beforehand.' ); } return $this->xAxisHeader !== null ? $this->xAxisHeader : $this->xAxisColumn; } /** * Set the x-axis header * * @param string $xAxisHeader * * @return $this */ public function setXAxisHeader(string $xAxisHeader): self { $this->xAxisHeader = $xAxisHeader; return $this; } /** * Get the y-axis header * * Defaults to {@link $yAxisColumn} in case no x-axis header has been set using {@link setYAxisHeader()} * * @return string */ public function getYAxisHeader(): string { if ($this->yAxisHeader === null && $this->yAxisColumn === null) { throw new \LogicException( 'You are accessing an unset property. Please make sure to set it beforehand.' ); } return $this->yAxisHeader !== null ? $this->yAxisHeader : $this->yAxisColumn; } /** * Set the y-axis header * * @param string $yAxisHeader * * @return $this */ public function setYAxisHeader(string $yAxisHeader): self { $this->yAxisHeader = $yAxisHeader; return $this; } /** * Return the value for the given request parameter * * @param string $axis The axis for which to return the parameter ('x' or 'y') * @param string $param The parameter name to return * @param int $default The default value to return * * @return int */ protected function getPaginationParameter(string $axis, string $param, int $default = null): int { /** @var Web $app */ $app = Icinga::app(); $value = $app->getRequest()->getParam($param, ''); if (strpos($value, ',') > 0) { $parts = explode(',', $value, 2); return intval($parts[$axis === 'x' ? 0 : 1]); } return $default !== null ? $default : 0; } /** * Query horizontal (x) axis * * @return Query */ protected function queryXAxis(): Query { if ($this->xAxisQuery === null) { $this->xAxisQuery = clone $this->baseQuery; $xAxisHeader = $this->getXAxisHeader(); $table = $this->xAxisQuery->getModel()->getTableName(); $xCol = explode('.', $this->gridcols[$this->xAxisColumn]); $columns = [ $this->xAxisColumn => $this->gridcols[$this->xAxisColumn], $xAxisHeader => $this->gridcols[$xAxisHeader] ]; // TODO: This shouldn't be required. Refactor this once ipl\Orm\Query has support for group by rules! if ($xCol[0] !== $table) { $groupCols = array_unique([ $this->xAxisColumn => $table . '_' . $this->gridcols[$this->xAxisColumn], $xAxisHeader => $table . '_' . $this->gridcols[$xAxisHeader] ]); } else { $groupCols = $columns; } $this->xAxisQuery->getSelectBase()->groupBy($groupCols); if (count($columns) !== 2) { $columns[] = $this->gridcols[$xAxisHeader]; } $this->xAxisQuery->columns($columns); if ($this->xAxisFilter !== null) { $this->xAxisQuery->filter($this->xAxisFilter); } $this->xAxisQuery->orderBy( $this->gridcols[$xAxisHeader], isset($this->order[$this->gridcols[$xAxisHeader]]) ? $this->order[$this->gridcols[$xAxisHeader]] : self::SORT_ASC ); } return $this->xAxisQuery; } /** * Query vertical (y) axis * * @return Query */ protected function queryYAxis(): Query { if ($this->yAxisQuery === null) { $this->yAxisQuery = clone $this->baseQuery; $yAxisHeader = $this->getYAxisHeader(); $table = $this->yAxisQuery->getModel()->getTableName(); $columns = [ $this->yAxisColumn => $this->gridcols[$this->yAxisColumn], $yAxisHeader => $this->gridcols[$yAxisHeader] ]; $yCol = explode('.', $this->gridcols[$this->yAxisColumn]); // TODO: This shouldn't be required. Refactor this once ipl\Orm\Query has support for group by rules! if ($yCol[0] !== $table) { $groupCols = array_unique([ $this->yAxisColumn => $table . '_' . $this->gridcols[$this->yAxisColumn], $yAxisHeader => $table . '_' . $this->gridcols[$yAxisHeader] ]); } else { $groupCols = $columns; } $this->yAxisQuery->getSelectBase()->groupBy($groupCols); if (count($columns) !== 2) { $columns[] = $this->gridcols[$yAxisHeader]; } $this->yAxisQuery->columns($columns); if ($this->yAxisFilter !== null) { $this->yAxisQuery->filter($this->yAxisFilter); } $this->yAxisQuery->orderBy( $this->gridcols[$yAxisHeader], isset($this->order[$this->gridcols[$yAxisHeader]]) ? $this->order[$this->gridcols[$yAxisHeader]] : self::SORT_ASC ); } return $this->yAxisQuery; } /** * Return a pagination adapter for the x-axis query * * $limit and $page are taken from the current request if not given. * * @param int $limit The maximum amount of entries to fetch * @param int $page The page to set as current one * * @return Paginatable */ public function paginateXAxis(int $limit = null, int $page = null): Paginatable { if ($limit === null || $page === null) { if ($limit === null) { $limit = $this->getPaginationParameter('x', 'limit', 20); } if ($page === null) { $page = $this->getPaginationParameter('x', 'page', 1); } } $query = $this->queryXAxis(); if ($limit !== 0) { $query->limit($limit); $query->offset($page > 0 ? ($page - 1) * $limit : 0); } return $query; } /** * Return a Paginatable for the y-axis query * * $limit and $page are taken from the current request if not given. * * @param int $limit The maximum amount of entries to fetch * @param int $page The page to set as current one * * @return Paginatable */ public function paginateYAxis(int $limit = null, int $page = null): Paginatable { if ($limit === null || $page === null) { if ($limit === null) { $limit = $this->getPaginationParameter('y', 'limit', 20); } if ($page === null) { $page = $this->getPaginationParameter('y', 'page', 1); } } $query = $this->queryYAxis(); if ($limit !== 0) { $query->limit($limit); $query->offset($page > 0 ? ($page - 1) * $limit : 0); } return $query; } /** * Return the pivot table as an array of pivot data and pivot header * * @return array */ public function toArray(): array { if ( ($this->xAxisFilter === null && $this->yAxisFilter === null) || ($this->xAxisFilter !== null && $this->yAxisFilter !== null) ) { $xAxis = $this->queryXAxis()->getDb()->fetchPairs($this->queryXAxis()->assembleSelect()); $xAxisKeys = array_keys($xAxis); $yAxis = $this->queryYAxis()->getDb()->fetchPairs($this->queryYAxis()->assembleSelect()); $yAxisKeys = array_keys($yAxis); } else { if ($this->xAxisFilter !== null) { $xAxis = $this->queryXAxis()->getDb()->fetchPairs($this->queryXAxis()->assembleSelect()); $xAxisKeys = array_keys($xAxis); $yQuery = $this->queryYAxis(); $yQuery->filter(Filter::equal($this->gridcols[$this->xAxisColumn], $xAxisKeys)); $yAxis = $this->queryYAxis()->getDb()->fetchPairs($this->queryYAxis()->assembleSelect()); $yAxisKeys = array_keys($yAxis); } else { // $this->yAxisFilter !== null $yAxis = $this->queryYAxis()->getDb()->fetchPairs($this->queryYAxis()->assembleSelect()); $yAxisKeys = array_keys($yAxis); $xQuery = $this->queryXAxis(); $xQuery->filter(Filter::equal($this->gridcols[$this->yAxisColumn], $yAxisKeys)); $xAxis = $this->queryXAxis()->getDb()->fetchPairs($this->queryXAxis()->assembleSelect()); $xAxisKeys = array_keys($yAxis); } } $pivotData = []; $pivotHeader = [ 'cols' => $xAxis, 'rows' => $yAxis ]; if (! empty($xAxis) && ! empty($yAxis)) { $this->baseQuery->filter(Filter::equal($this->gridcols[$this->xAxisColumn], $xAxisKeys)); $this->baseQuery->filter(Filter::equal($this->gridcols[$this->yAxisColumn], $yAxisKeys)); foreach ($yAxisKeys as $yAxisKey) { foreach ($xAxisKeys as $xAxisKey) { $pivotData[$yAxisKey][$xAxisKey] = null; } } foreach ($this->baseQuery as $row) { $pivotData[$row->{$this->yAxisColumn}][$row->{$this->xAxisColumn}] = $row; } } return [$pivotData, $pivotHeader]; } }