All Data Structures Functions Variables Pages
ReportDataProvider.php
1 <?php
2  /*********************************************************************************
3  * Zurmo is a customer relationship management program developed by
4  * Zurmo, Inc. Copyright (C) 2017 Zurmo Inc.
5  *
6  * Zurmo is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Affero General Public License version 3 as published by the
8  * Free Software Foundation with the addition of the following permission added
9  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10  * IN WHICH THE COPYRIGHT IS OWNED BY ZURMO, ZURMO DISCLAIMS THE WARRANTY
11  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12  *
13  * Zurmo is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Affero General Public License along with
19  * this program; if not, see http://www.gnu.org/licenses or write to the Free
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA.
22  *
23  * You can contact Zurmo, Inc. with a mailing address at 27 North Wacker Drive
24  * Suite 370 Chicago, IL 60606. or at email address contact@zurmo.com.
25  *
26  * The interactive user interfaces in original and modified versions
27  * of this program must display Appropriate Legal Notices, as required under
28  * Section 5 of the GNU Affero General Public License version 3.
29  *
30  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31  * these Appropriate Legal Notices must retain the display of the Zurmo
32  * logo and Zurmo copyright notice. If the display of the logo is not reasonably
33  * feasible for technical reasons, the Appropriate Legal Notices must display the words
34  * "Copyright Zurmo Inc. 2017. All rights reserved".
35  ********************************************************************************/
36 
40  abstract class ReportDataProvider extends CDataProvider
41  {
47  abstract protected function isReportValidType();
48 
52  protected $report;
53 
58  protected $runReport = false;
59 
63  protected $offset;
64 
69  protected $haveGrandTotals = false;
70 
74  private $_rowsData;
75 
80  public function __construct(Report $report, array $config = array())
81  {
82  $this->report = $report;
83  $this->isReportValidType();
84  foreach ($config as $key => $value)
85  {
86  $this->$key = $value;
87  }
88  }
89 
93  public function setRunReport($runReport)
94  {
95  assert('is_bool($runReport)');
96  $this->runReport = $runReport;
97  }
98 
102  public function getReport()
103  {
104  return $this->report;
105  }
106 
110  public function resolveDisplayAttributes()
111  {
112  return $this->report->getDisplayAttributes();
113  }
114 
118  public function resolveGroupBys()
119  {
120  return $this->report->getGroupBys();
121  }
122 
127  public function calculateTotalItemCount()
128  {
129  $selectQueryAdapter = $this->makeSelectQueryAdapter();
130  $sql = $this->makeSqlQueryForFetchingTotalItemCount($selectQueryAdapter, true);
131  $count = ZurmoRedBean::getCell($sql);
132  if ($count === null || empty($count))
133  {
134  $count = 0;
135  }
136  return $count;
137  }
138 
143  {
144  $selectQueryAdapter = $this->makeSelectQueryAdapter();
145  return $this->makeSqlQueryForFetchingTotalItemCount($selectQueryAdapter, true);
146  }
147 
151  public function makeSqlQueryForDisplay()
152  {
153  $offset = $this->resolveOffset();
154  $limit = $this->resolveLimit();
155  $selectQueryAdapter = $this->makeSelectQueryAdapter();
156  return $this->makeSqlQueryForFetchingData($selectQueryAdapter, $offset, $limit);
157  }
158 
165  public function resolveFiltersForReadPermissions(array $filters, & $filtersStructure)
166  {
167  $attributeIndexes = $this->makeReadPermissionsAttributeIndexes($filters);
168  $existingFiltersCount = count($filters);
169  $structurePosition = $existingFiltersCount + 1;
170  $readStructure = null;
171  foreach ($attributeIndexes as $attributeIndexOrDerivedTypePrefix => $attributeOrDerivedAttributeTypes)
172  {
173  $structure = null;
174  foreach ($attributeOrDerivedAttributeTypes as $attributeOrDerivedAttributeType)
175  {
176  if ($structure != null)
177  {
178  $structure .= ' or ';
179  }
180  $structure .= $structurePosition;
181  $structurePosition++;
182  $filters[] = $this->resolveFilterForReadPermissionAttributeIndex($attributeIndexOrDerivedTypePrefix,
183  $attributeOrDerivedAttributeType);
184  }
185  if ($structure != null)
186  {
187  if ($readStructure != null)
188  {
189  $readStructure .= ' and ';
190  }
191  $readStructure .= '(' . $structure . ')';
192  }
193  }
194  if ($readStructure != null)
195  {
196  if ($filtersStructure != null)
197  {
198  $filtersStructure .= ' and (' . $readStructure . ')';
199  }
200  else
201  {
202  $filtersStructure .= $readStructure;
203  }
204  }
205  return $filters;
206  }
207 
214  public function resolveFiltersForVariableStates($filters, & $filtersStructure)
215  {
216  $attributeIndexes = $this->makeVariableStatesAttributeIndexes($filters);
217  $existingFiltersCount = count($filters);
218  $structurePosition = $existingFiltersCount + 1;
219  $readStructure = null;
220  foreach ($attributeIndexes as $attributeIndexOrDerivedTypePrefix => $variableStateData)
221  {
222  $structure = $structurePosition;
223  $structurePosition++;
224  $filters[] = $this->resolveFilterForVariableStateAttributeIndex($attributeIndexOrDerivedTypePrefix,
225  $variableStateData);
226  if ($readStructure != null)
227  {
228  $readStructure .= ' and ';
229  }
230  $readStructure .= $structure;
231  }
232  if ($readStructure != null)
233  {
234  if ($filtersStructure != null)
235  {
236  $filtersStructure .= ' and (' . $readStructure . ')';
237  }
238  else
239  {
240  $filtersStructure .= $readStructure;
241  }
242  }
243  return $filters;
244  }
245 
249  public function getData($refresh = false)
250  {
251  if ($refresh)
252  {
253  $this->_rowsData = null;
254  }
255  return parent::getData($refresh);
256  }
257 
261  protected function fetchData()
262  {
263  $offset = $this->resolveOffset();
264  $limit = $this->resolveLimit();
265  if ($this->getTotalItemCount() == 0)
266  {
267  return array();
268  }
269  return $this->runQueryAndGetResolveResultsData($offset, $limit);
270  }
271 
275  protected function resolveOffset()
276  {
277  $pagination = $this->getPagination();
278  if (isset($pagination))
279  {
280  $totalItemCount = $this->getTotalItemCount();
281  $pagination->setItemCount($totalItemCount);
282  $offset = $pagination->getOffset();
283  }
284  else
285  {
286  $offset = null;
287  }
288  if ($this->offset != null)
289  {
290  $offset = $this->offset;
291  }
292  return $offset;
293  }
294 
295  public function setOffset($offset)
296  {
297  assert('is_int($offset)');
298  $this->offset = $offset;
299  }
300 
304  protected function resolveLimit()
305  {
306  $pagination = $this->getPagination();
307  if (isset($pagination))
308  {
309  $totalItemCount = $this->getTotalItemCount();
310  $pagination->setItemCount($totalItemCount);
311  $limit = $pagination->getLimit();
312  }
313  else
314  {
315  $limit = null;
316  }
317  return $limit;
318  }
319 
325  protected function runQueryAndGetResolveResultsData($offset, $limit)
326  {
327  assert('is_int($offset) || $offset == null');
328  assert('is_int($limit) || $limit == null');
329  $selectQueryAdapter = $this->makeSelectQueryAdapter();
330  $sql = $this->makeSqlQueryForFetchingData($selectQueryAdapter, $offset, $limit);
331  $rows = $this->getRowsData($sql);
332  $resultsData = array();
333  $idByOffset = self::resolveIdByOffset($offset);
334  foreach ($rows as $key => $row)
335  {
336  $reportResultsRowData = new ReportResultsRowData($this->resolveDisplayAttributes(), $idByOffset);
337  foreach ($selectQueryAdapter->getIdTableAliasesAndModelClassNames() as $tableAlias => $modelClassName)
338  {
339  $idColumnName = $selectQueryAdapter->getIdColumNameByTableAlias($tableAlias);
340  $id = (int)$row[$idColumnName];
341  if ($id != null)
342  {
343  $reportResultsRowData->addModelAndAlias($modelClassName::getById($id), $tableAlias);
344  }
345  unset($row[$idColumnName]);
346  }
347  foreach ($row as $columnName => $value)
348  {
349  $reportResultsRowData->addSelectedColumnNameAndValue($columnName, $value);
350  }
351  $resultsData[$key] = $reportResultsRowData;
352  $idByOffset++;
353  }
354  return $resultsData;
355  }
356 
357  public function runQueryAndGrandTotalsData()
358  {
359  if (!$this->haveGrandTotals)
360  {
361  return null;
362  }
363  $rows = $this->getGrandTotalsRowsData();
364  return $rows;
365  }
366 
371  protected static function resolveIdByOffset($offset)
372  {
373  assert('is_int($offset) || $offset == null');
374  if ($offset == null)
375  {
376  return 0;
377  }
378  return $offset;
379  }
380 
385  protected function getRowsData($sql)
386  {
387  assert('is_string($sql)');
388  if ($this->_rowsData == null)
389  {
390  $this->_rowsData = ZurmoRedBean::getAll($sql);
391  }
392  return $this->_rowsData;
393  }
394 
395  protected function getGrandTotalsRowsData()
396  {
397  $sql = $this->makeSqlQueryForGrandTotals();
398  if ($sql !== null)
399  {
400  return ZurmoRedBean::getAll($sql);
401  }
402  }
403 
408  protected function fetchKeys()
409  {
410  $keys = array();
411  foreach ($this->getData() as $data)
412  {
413  $keys[] = $data->getId();
414  }
415  return $keys;
416  }
417 
424  protected function makeSqlQueryForFetchingData(RedBeanModelSelectQueryAdapter $selectQueryAdapter, $offset, $limit)
425  {
426  assert('is_int($offset) || $offset == null');
427  assert('is_int($limit) || $limit == null');
428  $moduleClassName = $this->report->getModuleClassName();
429  $modelClassName = $moduleClassName::getPrimaryModelName();
430  $joinTablesAdapter = new RedBeanModelJoinTablesQueryAdapter($modelClassName);
431  $this->makeDisplayAttributes($joinTablesAdapter, $selectQueryAdapter);
432  $where = $this->makeFiltersContent($joinTablesAdapter);
433  $orderBy = $this->makeOrderBysContent($joinTablesAdapter);
434  $groupBy = $this->makeGroupBysContent($joinTablesAdapter);
435  return SQLQueryUtil::makeQuery($modelClassName::getTableName(),
436  $selectQueryAdapter, $joinTablesAdapter, $offset, $limit, $where, $orderBy, $groupBy);
437  }
438 
444  protected function makeSqlQueryForFetchingTotalItemCount($selectQueryAdapter, $selectJustCount = false)
445  {
446  $moduleClassName = $this->report->getModuleClassName();
447  $modelClassName = $moduleClassName::getPrimaryModelName();
448  $joinTablesAdapter = new RedBeanModelJoinTablesQueryAdapter($modelClassName);
449  $this->makeDisplayAttributes($joinTablesAdapter, $selectQueryAdapter);
450  $where = $this->makeFiltersContent($joinTablesAdapter);
451  $orderBy = $this->makeOrderBysContent($joinTablesAdapter);
452  $groupBy = $this->makeGroupBysContentForCount($joinTablesAdapter);
453  //Make a fresh selectQueryAdapter that only has a count clause
454  if ($selectJustCount)
455  {
456  //Currently this is always expected as false. If it is true, we need to add support for SpecificCountClauses
457  //so we know which table/id the count is on.
458  $selectQueryAdapter = $this->resolveSqlQueryAdapterForCount($selectQueryAdapter);
459  $selectQueryAdapter->addNonSpecificCountClause();
460  }
461  return SQLQueryUtil::makeQuery($modelClassName::getTableName(),
462  $selectQueryAdapter, $joinTablesAdapter, null, null, $where, $orderBy, $groupBy);
463  }
464 
465  protected function getDisplayAttributesForGrandTotals()
466  {
467  $displayAttributes = $this->resolveDisplayAttributes();
468  foreach ($displayAttributes as $key => $displayAttribute)
469  {
470  foreach ($this->resolveGroupBys() as $groupBy)
471  {
472  if ($displayAttribute->attributeIndexOrDerivedType == $groupBy->attributeIndexOrDerivedType)
473  {
474  unset($displayAttributes[$key]);
475  }
476  }
477  }
478  return $displayAttributes;
479  }
480 
481  protected function makeSqlQueryForGrandTotals()
482  {
483  try
484  {
485  $selectQueryAdapter = $this->makeSelectQueryAdapter();
486  $moduleClassName = $this->report->getModuleClassName();
487  $modelClassName = $moduleClassName::getPrimaryModelName();
488  $joinTablesAdapter = new RedBeanModelJoinTablesQueryAdapter($modelClassName);
489  $builder = new DisplayAttributesReportQueryBuilder($joinTablesAdapter, $selectQueryAdapter,
490  $this->report->getCurrencyConversionType());
491  $builder->makeQueryContent($this->getDisplayAttributesForGrandTotals());
492  $where = $this->makeFiltersContent($joinTablesAdapter);
493  $orderBy = null;
494  $groupBy = $this->makeGroupBysContentForGrandTotals($joinTablesAdapter);
495  $offset = null;
496  $limit = null;
497  $sql = SQLQueryUtil::makeQuery($modelClassName::getTableName(),
498  $selectQueryAdapter, $joinTablesAdapter, $offset, $limit, $where, $orderBy, $groupBy);
499  }
500  catch (NotSupportedException $exception)
501  {
502  return null;
503  }
504  return $sql;
505  }
506 
511  protected function makeDisplayAttributes(RedBeanModelJoinTablesQueryAdapter $joinTablesAdapter,
512  RedBeanModelSelectQueryAdapter $selectQueryAdapter)
513  {
514  $builder = new DisplayAttributesReportQueryBuilder($joinTablesAdapter, $selectQueryAdapter,
515  $this->report->getCurrencyConversionType());
516  $builder->makeQueryContent($this->resolveDisplayAttributes());
517  }
518 
523  protected function makeFiltersContent(RedBeanModelJoinTablesQueryAdapter $joinTablesAdapter)
524  {
525  $filters = $this->report->getFilters();
526  $filtersStructure = $this->report->getFiltersStructure();
527  $resolvedFilters = $this->resolveFiltersForVariableStates($filters, $filtersStructure);
528  $resolvedFilters = $this->resolveFiltersForReadPermissions($resolvedFilters, $filtersStructure);
529  $builder = new FiltersReportQueryBuilder($joinTablesAdapter, $filtersStructure);
530  return $builder->makeQueryContent($resolvedFilters);
531  }
532 
537  protected function makeOrderBysContent(RedBeanModelJoinTablesQueryAdapter $joinTablesAdapter)
538  {
539  $builder = new OrderBysReportQueryBuilder($joinTablesAdapter, $this->report->getCurrencyConversionType());
540  return $builder->makeQueryContent($this->report->getOrderBys());
541  }
542 
547  protected function makeGroupBysContent(RedBeanModelJoinTablesQueryAdapter $joinTablesAdapter)
548  {
549  $builder = new GroupBysReportQueryBuilder($joinTablesAdapter);
550  return $builder->makeQueryContent($this->resolveGroupBys());
551  }
552 
558  {
559  return null;
560  }
561 
567  {
568  return $this->makeGroupBysContent($joinTablesAdapter);
569  }
570 
577  protected function resolveFilterForReadPermissionAttributeIndex($attributeIndexOrDerivedTypePrefix, $attributeOrDerivedAttributeType)
578  {
579  assert('is_string($attributeIndexOrDerivedTypePrefix) || $attributeIndexOrDerivedTypePrefix == null');
580  assert('is_string($attributeOrDerivedAttributeType)');
581  $moduleClassName = $this->report->getModuleClassName();
582  if ($attributeOrDerivedAttributeType == 'ReadOptimization')
583  {
584  $filter = new FilterForReportForm($moduleClassName, $moduleClassName::getPrimaryModelName(),
585  $this->report->getType());
586  $filter->attributeIndexOrDerivedType = $attributeIndexOrDerivedTypePrefix . $attributeOrDerivedAttributeType;
587  }
588  elseif ($attributeOrDerivedAttributeType == 'owner__User')
589  {
590  $filter = new FilterForReportForm($moduleClassName, $moduleClassName::getPrimaryModelName(),
591  $this->report->getType());
592  $filter->attributeIndexOrDerivedType = $attributeIndexOrDerivedTypePrefix. $attributeOrDerivedAttributeType;
593  $filter->operator = OperatorRules::TYPE_EQUALS;
594  $filter->value = Yii::app()->user->userModel->id;
595  }
596  else
597  {
598  throw new NotSupportedException();
599  }
600  return $filter;
601  }
602 
607  protected function makeReadPermissionsAttributeIndexes(array $filters)
608  {
609  $moduleClassName = $this->report->getModuleClassName();
610  $attributeIndexes = array();
612  resolveAttributeIndexes($moduleClassName::getPrimaryModelName(), $attributeIndexes);
616  resolveAttributeIndexesByComponents($attributeIndexes, $filters);
618  resolveAttributeIndexesByComponents($attributeIndexes, $this->report->getOrderBys());
620  resolveAttributeIndexesByComponents($attributeIndexes, $this->resolveGroupBys());
621  return $attributeIndexes;
622  }
623 
629  protected function resolveFilterForVariableStateAttributeIndex($attributeIndexOrDerivedTypePrefix, $variableStateData)
630  {
631  assert('is_string($attributeIndexOrDerivedTypePrefix) || $attributeIndexOrDerivedTypePrefix == null');
632  assert('is_array($variableStateData) && count($variableStateData) == 2');
633  $moduleClassName = $this->report->getModuleClassName();
634  $filter = new FilterForReportForm($moduleClassName,
635  $moduleClassName::getPrimaryModelName(),
636  $this->report->getType());
637  $filter->attributeIndexOrDerivedType = $attributeIndexOrDerivedTypePrefix. $variableStateData[0];
638  $filter->operator = OperatorRules::TYPE_ONE_OF;
639  $filter->value = $variableStateData[1];
640  return $filter;
641  }
642 
647  protected function makeVariableStatesAttributeIndexes(array $filters)
648  {
649  $moduleClassName = $this->report->getModuleClassName();
650  $attributeIndexes = array();
652  resolveAttributeIndexes($moduleClassName::getPrimaryModelName(), $attributeIndexes);
656  resolveAttributeIndexesByComponents($attributeIndexes, $filters);
658  resolveAttributeIndexesByComponents($attributeIndexes, $this->report->getOrderBys());
660  resolveAttributeIndexesByComponents($attributeIndexes, $this->resolveGroupBys());
661  return $attributeIndexes;
662  }
663 
668  protected function getDisplayAttributeByAttribute($attribute)
669  {
670  foreach ($this->resolveDisplayAttributes() as $displayAttribute)
671  {
672  if ($attribute == $displayAttribute->attributeIndexOrDerivedType)
673  {
674  return $displayAttribute;
675  }
676  }
677  }
678 
683  protected function getDisplayAttributeKeyByAttribute($attribute)
684  {
685  foreach ($this->resolveDisplayAttributes() as $key => $displayAttribute)
686  {
687  if ($attribute == $displayAttribute->attributeIndexOrDerivedType)
688  {
689  return $key;
690  }
691  }
692  }
693 
698  protected function makeSelectQueryAdapter($isDistinct = false)
699  {
700  return new RedBeanModelSelectQueryAdapter($isDistinct);
701  }
702 
709  protected function resolveSqlQueryAdapterForCount(RedBeanModelSelectQueryAdapter $selectQueryAdapter)
710  {
711  if ($selectQueryAdapter->isDistinct())
712  {
713  throw new NotSupportedException();
714  }
715  return $this->makeSelectQueryAdapter($selectQueryAdapter->isDistinct());
716  }
717  }
718 ?>
resolveFiltersForVariableStates($filters, &$filtersStructure)
resolveFilterForVariableStateAttributeIndex($attributeIndexOrDerivedTypePrefix, $variableStateData)
makeSqlQueryForFetchingData(RedBeanModelSelectQueryAdapter $selectQueryAdapter, $offset, $limit)
static resolveAttributeIndexes($modelClassName, &$attributeIndexes, $attributeIndexPrefix=null)
makeDisplayAttributes(RedBeanModelJoinTablesQueryAdapter $joinTablesAdapter, RedBeanModelSelectQueryAdapter $selectQueryAdapter)
makeGroupBysContentForGrandTotals(RedBeanModelJoinTablesQueryAdapter $joinTablesAdapter)
makeOrderBysContent(RedBeanModelJoinTablesQueryAdapter $joinTablesAdapter)
makeGroupBysContent(RedBeanModelJoinTablesQueryAdapter $joinTablesAdapter)
static resolveAttributeIndexesByComponents(array &$attributeIndexes, Array $componentForms)
getDisplayAttributeKeyByAttribute($attribute)
makeFiltersContent(RedBeanModelJoinTablesQueryAdapter $joinTablesAdapter)
runQueryAndGetResolveResultsData($offset, $limit)
getData($refresh=false)
resolveSqlQueryAdapterForCount(RedBeanModelSelectQueryAdapter $selectQueryAdapter)
__construct(Report $report, array $config=array())
resolveFiltersForReadPermissions(array $filters, &$filtersStructure)
getDisplayAttributeByAttribute($attribute)
static resolveIdByOffset($offset)
static resolveAttributeIndexes($modelClassName, &$attributeIndexes, $attributeIndexPrefix=null)
makeVariableStatesAttributeIndexes(array $filters)
makeSqlQueryForFetchingTotalItemCount($selectQueryAdapter, $selectJustCount=false)
resolveFilterForReadPermissionAttributeIndex($attributeIndexOrDerivedTypePrefix, $attributeOrDerivedAttributeType)
makeReadPermissionsAttributeIndexes(array $filters)
makeSelectQueryAdapter($isDistinct=false)
makeGroupBysContentForCount(RedBeanModelJoinTablesQueryAdapter $joinTablesAdapter)
Generated on Thu Apr 9 2020 07:10:43