Spaces:
No application file
No application file
namespace Mautic\ReportBundle\Model; | |
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; | |
use Doctrine\DBAL\Query\QueryBuilder; | |
use Doctrine\ORM\EntityManagerInterface; | |
use Mautic\ChannelBundle\Helper\ChannelListHelper; | |
use Mautic\CoreBundle\Helper\Chart\ChartQuery; | |
use Mautic\CoreBundle\Helper\CoreParametersHelper; | |
use Mautic\CoreBundle\Helper\DateTimeHelper; | |
use Mautic\CoreBundle\Helper\InputHelper; | |
use Mautic\CoreBundle\Helper\UserHelper; | |
use Mautic\CoreBundle\Model\FormModel; | |
use Mautic\CoreBundle\Security\Permissions\CorePermissions; | |
use Mautic\CoreBundle\Translation\Translator; | |
use Mautic\LeadBundle\Model\FieldModel; | |
use Mautic\ReportBundle\Builder\MauticReportBuilder; | |
use Mautic\ReportBundle\Crate\ReportDataResult; | |
use Mautic\ReportBundle\Entity\Report; | |
use Mautic\ReportBundle\Event\ReportBuilderEvent; | |
use Mautic\ReportBundle\Event\ReportDataEvent; | |
use Mautic\ReportBundle\Event\ReportEvent; | |
use Mautic\ReportBundle\Event\ReportGraphEvent; | |
use Mautic\ReportBundle\Event\ReportQueryEvent; | |
use Mautic\ReportBundle\Generator\ReportGenerator; | |
use Mautic\ReportBundle\Helper\ReportHelper; | |
use Mautic\ReportBundle\ReportEvents; | |
use PhpOffice\PhpSpreadsheet\Spreadsheet; | |
use Psr\Log\LoggerInterface; | |
use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
use Symfony\Component\Form\FormFactoryInterface; | |
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException; | |
use Symfony\Component\HttpFoundation\RequestStack; | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Component\HttpFoundation\Session\Session; | |
use Symfony\Component\HttpFoundation\Session\SessionInterface; | |
use Symfony\Component\HttpFoundation\StreamedResponse; | |
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; | |
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | |
use Symfony\Contracts\EventDispatcher\Event; | |
use Twig\Environment; | |
/** | |
* @extends FormModel<Report> | |
*/ | |
class ReportModel extends FormModel | |
{ | |
public const CHANNEL_FEATURE = 'reporting'; | |
/** | |
* @var array | |
*/ | |
private $reportBuilderData; | |
/** | |
* @var mixed | |
*/ | |
protected $defaultPageLimit; | |
public function __construct( | |
CoreParametersHelper $coreParametersHelper, | |
protected Environment $twig, | |
protected ChannelListHelper $channelListHelper, | |
protected FieldModel $fieldModel, | |
protected ReportHelper $reportHelper, | |
private CsvExporter $csvExporter, | |
private ExcelExporter $excelExporter, | |
EntityManagerInterface $em, | |
CorePermissions $security, | |
EventDispatcherInterface $dispatcher, | |
UrlGeneratorInterface $router, | |
Translator $translator, | |
UserHelper $userHelper, | |
LoggerInterface $mauticLogger, | |
private RequestStack $requestStack | |
) { | |
$this->defaultPageLimit = $coreParametersHelper->get('default_pagelimit'); | |
parent::__construct($em, $security, $dispatcher, $router, $translator, $userHelper, $mauticLogger, $coreParametersHelper); | |
} | |
/** | |
* @return \Mautic\ReportBundle\Entity\ReportRepository | |
*/ | |
public function getRepository() | |
{ | |
return $this->em->getRepository(Report::class); | |
} | |
public function getPermissionBase(): string | |
{ | |
return 'report:reports'; | |
} | |
protected function getSession(): SessionInterface | |
{ | |
try { | |
return $this->requestStack->getSession(); | |
} catch (SessionNotFoundException) { | |
return new Session(); // in case of CLI | |
} | |
} | |
/** | |
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException | |
*/ | |
public function createForm($entity, FormFactoryInterface $formFactory, $action = null, $options = []): \Symfony\Component\Form\FormInterface | |
{ | |
if (!$entity instanceof Report) { | |
throw new MethodNotAllowedHttpException(['Report']); | |
} | |
if (!empty($action)) { | |
$options['action'] = $action; | |
} | |
$options = array_merge($options, [ | |
'table_list' => $this->getTableData('all', $entity->getSource()), | |
'attr' => [ | |
'readonly' => false, | |
], | |
]); | |
// Fire the REPORT_ON_BUILD event off to get the table/column data | |
$reportGenerator = new ReportGenerator($this->dispatcher, $this->em->getConnection(), $entity, $this->channelListHelper, $formFactory); | |
return $reportGenerator->getForm($entity, $options); | |
} | |
public function getEntity($id = null): ?Report | |
{ | |
if (null === $id) { | |
return new Report(); | |
} | |
return parent::getEntity($id); | |
} | |
/** | |
* @throws MethodNotAllowedHttpException | |
*/ | |
protected function dispatchEvent($action, &$entity, $isNew = false, Event $event = null): ?Event | |
{ | |
if (!$entity instanceof Report) { | |
throw new MethodNotAllowedHttpException(['Report']); | |
} | |
switch ($action) { | |
case 'pre_save': | |
$name = ReportEvents::REPORT_PRE_SAVE; | |
break; | |
case 'post_save': | |
$name = ReportEvents::REPORT_POST_SAVE; | |
break; | |
case 'pre_delete': | |
$name = ReportEvents::REPORT_PRE_DELETE; | |
break; | |
case 'post_delete': | |
$name = ReportEvents::REPORT_POST_DELETE; | |
break; | |
default: | |
return null; | |
} | |
if ($this->dispatcher->hasListeners($name)) { | |
if (empty($event)) { | |
$event = new ReportEvent($entity, $isNew); | |
$event->setEntityManager($this->em); | |
} | |
$this->dispatcher->dispatch($event, $name); | |
return $event; | |
} else { | |
return null; | |
} | |
} | |
/** | |
* Build the table and graph data. | |
* | |
* @return mixed | |
*/ | |
public function buildAvailableReports($context, ?string $reportSource = null) | |
{ | |
if (empty($this->reportBuilderData[$context])) { | |
// Check to see if all has been obtained | |
if (isset($this->reportBuilderData['all'])) { | |
$this->reportBuilderData[$context]['tables'] = $this->reportBuilderData['all']['tables'][$context] ?? []; | |
$this->reportBuilderData[$context]['graphs'] = $this->reportBuilderData['all']['graphs'][$context] ?? []; | |
} else { | |
// build them | |
$eventContext = ('all' == $context) ? '' : $context; | |
$event = new ReportBuilderEvent($this->translator, $this->channelListHelper, $eventContext, $this->fieldModel->getPublishedFieldArrays(), $this->reportHelper, $reportSource); | |
$this->dispatcher->dispatch($event, ReportEvents::REPORT_ON_BUILD); | |
$tables = $event->getTables(); | |
$graphs = $event->getGraphs(); | |
if ('all' == $context) { | |
$this->reportBuilderData[$context]['tables'] = $tables; | |
$this->reportBuilderData[$context]['graphs'] = $graphs; | |
} else { | |
if (isset($tables[$context])) { | |
$this->reportBuilderData[$context]['tables'] = $tables[$context]; | |
} else { | |
$this->reportBuilderData[$context]['tables'] = $tables; | |
} | |
if (isset($graphs[$context])) { | |
$this->reportBuilderData[$context]['graphs'] = $graphs[$context]; | |
} else { | |
$this->reportBuilderData[$context]['graphs'] = $graphs; | |
} | |
} | |
} | |
} | |
return $this->reportBuilderData[$context]; | |
} | |
/** | |
* Builds the table lookup data for the report forms. | |
* | |
* @param string $context | |
* | |
* @return array | |
*/ | |
public function getTableData($context = 'all', ?string $reportSource = null) | |
{ | |
$data = $this->buildAvailableReports($context, $reportSource); | |
$data = (!isset($data['tables'])) ? [] : $data['tables']; | |
if (array_key_exists('columns', $data)) { | |
$data['columns'] = $this->preventSameAliases($data['columns']); | |
} | |
return $data; | |
} | |
/** | |
* Prevent same aliases using numeric suffixes for each alias. | |
*/ | |
private function preventSameAliases(array $columns): array | |
{ | |
$existingAliases = []; | |
foreach ($columns as $key => $column) { | |
$alias = $column['alias']; | |
// Count suffixes | |
if (!array_key_exists($alias, $existingAliases)) { | |
$existingAliases[$alias] = 0; | |
} else { | |
++$existingAliases[$alias]; | |
} | |
// Add numeric suffix | |
if ($existingAliases[$alias] > 0) { | |
$columns[$key]['alias'] = $alias.$existingAliases[$alias]; | |
} | |
} | |
return $columns; | |
} | |
/** | |
* @param string $context | |
* | |
* @return mixed | |
*/ | |
public function getGraphData($context = 'all') | |
{ | |
$data = $this->buildAvailableReports($context); | |
return (!isset($data['graphs'])) ? [] : $data['graphs']; | |
} | |
/** | |
* @param string $context | |
* | |
* @return \stdClass ['choices' => [], 'choiceHtml' => '', definitions => []] | |
*/ | |
public function getColumnList($context, $isGroupBy = false): \stdClass | |
{ | |
$tableData = $this->getTableData($context); | |
$columns = $tableData['columns'] ?? []; | |
$return = new \stdClass(); | |
$return->choices = []; | |
$return->choiceHtml = ''; | |
$return->definitions = []; | |
foreach ($columns as $column => $data) { | |
if ($isGroupBy && ('unsubscribed' == $column || 'unsubscribed_ratio' == $column || 'unique_ratio' == $column)) { | |
continue; | |
} | |
if (isset($data['label'])) { | |
$return->choiceHtml .= "<option value=\"$column\">{$data['label']}</option>\n"; | |
$return->choices[$column] = $data['label']; | |
$return->definitions[$column] = $data; | |
} | |
} | |
return $return; | |
} | |
/** | |
* @property filterList | |
* @property definitions | |
* | |
* @param string $context | |
* | |
* @return \stdClass[filterList => [], definitions => [], operatorChoices => [], operatorHtml => [], filterListHtml => ''] | |
*/ | |
public function getFilterList($context = 'all'): \stdClass | |
{ | |
$tableData = $this->getTableData($context); | |
$return = new \stdClass(); | |
$filters = $tableData['filters'] ?? $tableData['columns'] ?? []; | |
$return->choices = []; | |
$return->choiceHtml = ''; | |
$return->definitions = []; | |
$return->operatorHtml = []; | |
$return->operatorChoices = []; | |
foreach ($filters as $filter => $data) { | |
if (isset($data['label'])) { | |
$return->definitions[$filter] = $data; | |
$return->choices[$filter] = $data['label']; | |
$return->choiceHtml .= "<option value=\"$filter\">{$data['label']}</option>\n"; | |
$return->operatorChoices[$filter] = $this->getOperatorOptions($data); | |
$return->operatorHtml[$filter] = ''; | |
foreach ($return->operatorChoices[$filter] as $value => $label) { | |
$return->operatorHtml[$filter] .= "<option value=\"$value\">$label</option>\n"; | |
} | |
} | |
} | |
return $return; | |
} | |
/** | |
* @param string $context | |
* | |
* @return \stdClass ['choices' => [], choiceHtml = ''] | |
*/ | |
public function getGraphList($context = 'all'): \stdClass | |
{ | |
$graphData = $this->getGraphData($context); | |
$return = new \stdClass(); | |
$return->choices = []; | |
$return->choiceHtml = ''; | |
// First sort | |
foreach ($graphData as $key => $details) { | |
$return->choices[$key] = $this->translator->trans($key).' ('.$this->translator->trans('mautic.report.graph.'.$details['type']).')'; | |
} | |
natsort($return->choices); | |
foreach ($return->choices as $key => $value) { | |
$return->choiceHtml .= '<option value="'.$key.'">'.$value."</option>\n"; | |
} | |
return $return; | |
} | |
/** | |
* Export report. | |
* | |
* @param string $format | |
* @param int $page | |
* | |
* @return StreamedResponse|Response | |
* | |
* @throws \Exception | |
*/ | |
public function exportResults($format, Report $report, ReportDataResult $reportDataResult, $handle = null, $page = null) | |
{ | |
$date = (new DateTimeHelper())->toLocalString(); | |
$name = str_replace(' ', '_', $date).'_'.InputHelper::alphanum($report->getName(), false, '-'); | |
switch ($format) { | |
case 'csv': | |
if (!is_null($handle)) { | |
$this->csvExporter->export($reportDataResult, $handle, $page); | |
return; | |
} | |
$response = new StreamedResponse( | |
function () use ($reportDataResult): void { | |
$handle = fopen('php://output', 'r+'); | |
$this->csvExporter->export($reportDataResult, $handle); | |
fclose($handle); | |
} | |
); | |
$fileName = $name.'.csv'; | |
ExportResponse::setResponseHeaders($response, $fileName); | |
return $response; | |
case 'html': | |
$content = $this->twig->render( | |
'@MauticReport/Report/export.html.twig', | |
[ | |
'pageTitle' => $name, | |
'report' => $report, | |
'reportDataResult' => $reportDataResult, | |
] | |
); | |
return new Response($content); | |
case 'xlsx': | |
if (!class_exists(Spreadsheet::class)) { | |
throw new \Exception('PHPSpreadsheet is required to export to Excel spreadsheets'); | |
} | |
$response = new StreamedResponse( | |
function () use ($reportDataResult, $name): void { | |
$this->excelExporter->export($reportDataResult, $name); | |
} | |
); | |
$fileName = $name.'.xlsx'; | |
ExportResponse::setResponseHeaders($response, $fileName); | |
return $response; | |
default: | |
return new Response(); | |
} | |
} | |
/** | |
* Get report data for view rendering. | |
* | |
* @return mixed[] | |
*/ | |
public function getReportData(Report $entity, FormFactoryInterface $formFactory = null, array $options = []): array | |
{ | |
// Clone dateFrom/dateTo because they handled separately in charts | |
$chartDateFrom = isset($options['dateFrom']) ? clone $options['dateFrom'] : (new \DateTime('-30 days')); | |
$chartDateTo = isset($options['dateTo']) ? clone $options['dateTo'] : (new \DateTime()); | |
$debugData = []; | |
if (isset($options['dateFrom'])) { | |
// Fix date ranges if applicable | |
if (!isset($options['dateTo'])) { | |
$options['dateTo'] = new \DateTime(); | |
} | |
// Adjust dateTo to be end of day or to current hour if today | |
$now = new \DateTime(); | |
if ($now->format('Y-m-d') == $options['dateTo']->format('Y-m-d')) { | |
$options['dateTo'] = $now; | |
} else { | |
$options['dateTo']->setTime(23, 59, 59); | |
} | |
// Convert date ranges to UTC for fetching tabular data | |
$options['dateFrom']->setTimeZone(new \DateTimeZone('UTC')); | |
$options['dateTo']->setTimeZone(new \DateTimeZone('UTC')); | |
} | |
$paginate = !empty($options['paginate']); | |
$reportPage = $options['reportPage'] ?? 1; | |
$data = $graphs = []; | |
$reportGenerator = new ReportGenerator($this->dispatcher, $this->getConnection(), $entity, $this->channelListHelper, $formFactory); | |
$selectedColumns = $entity->getColumns(); | |
$totalResults = $limit = 0; | |
// Prepare the query builder | |
$tableDetails = $this->getTableData($entity->getSource()); | |
$dataColumns = $dataAggregatorColumns = []; | |
$aggregatorColumns = ($aggregators = $entity->getAggregators()) ? $aggregators : []; | |
foreach ($aggregatorColumns as $aggregatorColumn) { | |
$selectedColumns[] = $aggregatorColumn['column']; | |
// add aggregator columns to dataColumns also | |
$dataColumns[$aggregatorColumn['function'].' '.$aggregatorColumn['column']] = $aggregatorColumn['column']; | |
$dataAggregatorColumns[$aggregatorColumn['function'].' '.$aggregatorColumn['column']] = $aggregatorColumn['column']; | |
} | |
// Build a reference for column to data column (without table prefix) | |
foreach ($tableDetails['columns'] as $dbColumn => &$columnData) { | |
$dataColumns[$columnData['alias']] = $dbColumn; | |
} | |
$orderBy = $this->getSession()->get('mautic.report.'.$entity->getId().'.orderby', ''); | |
$orderByDir = $this->getSession()->get('mautic.report.'.$entity->getId().'.orderbydir', 'ASC'); | |
$dataOptions = [ | |
'order' => (!empty($orderBy)) ? [$orderBy, $orderByDir] : false, | |
'columns' => $tableDetails['columns'], | |
'filters' => $tableDetails['filters'] ?? $tableDetails['columns'], | |
'dateFrom' => $options['dateFrom'] ?? null, | |
'dateTo' => $options['dateTo'] ?? null, | |
'dynamicFilters' => $options['dynamicFilters'] ?? [], | |
]; | |
/** @var QueryBuilder $query */ | |
$query = $reportGenerator->getQuery($dataOptions); | |
$options['translator'] = $this->translator; | |
$contentTemplate = $reportGenerator->getContentTemplate(); | |
// set what page currently on so that we can return here after form submission/cancellation | |
$this->getSession()->set('mautic.report.'.$entity->getId().'.page', $reportPage); | |
// Reset the orderBy as it causes errors in graphs and the count query in table data | |
$parts = $query->getQueryParts(); | |
$order = $parts['orderBy']; | |
$query->resetQueryPart('orderBy'); | |
if (empty($options['ignoreGraphData'])) { | |
$chartQuery = new ChartQuery($this->em->getConnection(), $chartDateFrom, $chartDateTo); | |
$options['chartQuery'] = $chartQuery; | |
// Check to see if this is an update from AJAX | |
$selectedGraphs = (!empty($options['graphName'])) ? [$options['graphName']] : $entity->getGraphs(); | |
if (!empty($selectedGraphs)) { | |
$availableGraphs = $this->getGraphData($entity->getSource()); | |
if (empty($query)) { | |
$query = $reportGenerator->getQuery(); | |
} | |
$eventGraphs = []; | |
$defaultGraphOptions = $options; | |
$defaultGraphOptions['dateFrom'] = $chartDateFrom; | |
$defaultGraphOptions['dateTo'] = $chartDateTo; | |
foreach ($selectedGraphs as $g) { | |
if (isset($availableGraphs[$g])) { | |
$graphOptions = $availableGraphs[$g]['options'] ?? []; | |
$graphOptions = array_merge($defaultGraphOptions, $graphOptions); | |
$eventGraphs[$g] = [ | |
'options' => $graphOptions, | |
'type' => $availableGraphs[$g]['type'], | |
]; | |
} | |
} | |
$event = new ReportGraphEvent($entity, $eventGraphs, $query); | |
$this->dispatcher->dispatch($event, ReportEvents::REPORT_ON_GRAPH_GENERATE); | |
$graphs = $event->getGraphs(); | |
unset($defaultGraphOptions); | |
} | |
} | |
$columnsAllowed = $this->getColumnList($entity->getSource()); | |
$order = $this->getOrderBySanitized($order, $columnsAllowed); | |
if ($order['hasOrderBy']) { | |
$query->add('orderBy', $order['orderBy']); | |
} | |
// Allow plugin to manipulate the query | |
$event = new ReportQueryEvent($entity, $query, $totalResults, $dataOptions); | |
$this->dispatcher->dispatch($event, ReportEvents::REPORT_QUERY_PRE_EXECUTE); | |
$query = $event->getQuery(); | |
if (empty($options['ignoreTableData']) && !empty($selectedColumns)) { | |
if ($paginate) { | |
// Build the options array to pass into the query | |
$limit = $this->getSession()->get('mautic.report.'.$entity->getId().'.limit', $this->defaultPageLimit); | |
if (!empty($options['limit'])) { | |
$limit = $options['limit']; | |
$reportPage = $options['page']; | |
} | |
$start = (1 === $reportPage) ? 0 : (($reportPage - 1) * $limit); | |
if ($start < 0) { | |
$start = 0; | |
} | |
if (empty($options['totalResults'])) { | |
$options['totalResults'] = $totalResults = $this->getTotalCount($query, $debugData); | |
} else { | |
$totalResults = $options['totalResults']; | |
} | |
if ($limit > 0) { | |
$query->setFirstResult($start) | |
->setMaxResults($limit); | |
} | |
} | |
$queryTime = microtime(true); | |
$data = $query->execute()->fetchAllAssociative(); | |
$queryTime = round((microtime(true) - $queryTime) * 1000); | |
if ($queryTime >= 1000) { | |
$queryTime *= 1000; | |
$queryTime .= 's'; | |
} else { | |
$queryTime .= 'ms'; | |
} | |
if (!$paginate) { | |
$totalResults = count($data); | |
} | |
// Allow plugin to manipulate the data | |
$event = new ReportDataEvent($entity, $data, $totalResults, $dataOptions); | |
$this->dispatcher->dispatch($event, ReportEvents::REPORT_ON_DISPLAY); | |
$data = $event->getData(); | |
} | |
if ($this->isDebugMode()) { | |
$debugData['query'] = $query->getSQL(); | |
$params = $query->getParameters(); | |
foreach ($params as $name => $param) { | |
if (is_array($param)) { | |
$param = implode("','", $param); | |
} | |
$debugData['query'] = str_replace(":$name", "'$param'", $debugData['query']); | |
} | |
$debugData['query_time'] = $queryTime ?? 'N/A'; | |
} | |
foreach ($data as $keys => $lead) { | |
foreach ($lead as $key => $field) { | |
$data[$keys][$key] = html_entity_decode((string) $field, ENT_QUOTES); | |
} | |
} | |
return [ | |
'totalResults' => $totalResults, | |
'data' => $data, | |
'dataColumns' => $dataColumns, | |
'graphs' => $graphs, | |
'contentTemplate' => $contentTemplate, | |
'columns' => $tableDetails['columns'], | |
'limit' => ($paginate) ? $limit : 0, | |
'page' => ($paginate) ? $reportPage : 1, | |
'dateFrom' => $dataOptions['dateFrom'], | |
'dateTo' => $dataOptions['dateTo'], | |
'debug' => $debugData, | |
'aggregatorColumns' => $dataAggregatorColumns, | |
]; | |
} | |
/** | |
* Sanitize order by array comparing it to the allowed columns. | |
* | |
* @param iterable<mixed> $orderBys | |
* | |
* @return iterable<mixed> | |
*/ | |
private function getOrderBySanitized(iterable $orderBys, \stdClass $allowedColumns): iterable | |
{ | |
$hasOrderBy = false; | |
foreach ($orderBys as $key => $orderBy) { | |
if ($this->orderByIsValid($orderBy, $allowedColumns)) { | |
$hasOrderBy = true; | |
continue; | |
} | |
$orderBys[$key] = ''; | |
} | |
return [ | |
'orderBy' => $orderBys, | |
'hasOrderBy' => $hasOrderBy, | |
]; | |
} | |
/** | |
* Check if order by is valid. | |
*/ | |
private function orderByIsValid(string $order, \stdClass $allowedColumns): bool | |
{ | |
if (empty($order)) { | |
return false; | |
} | |
$orderBy = $order; | |
$oderByDirection = ''; | |
if (str_contains($order, ' ')) { | |
$orderTemp = explode(' ', $order); | |
$orderBy = $orderTemp[0]; | |
$oderByDirection = $orderTemp[1]; | |
} | |
if (!array_key_exists($orderBy, $allowedColumns->choices) || !in_array($oderByDirection, ['ASC', 'DESC', ''])) { | |
return false; | |
} | |
return true; | |
} | |
/** | |
* @return mixed[] | |
*/ | |
public function getReportsWithGraphs(): array | |
{ | |
$ownedBy = $this->security->isGranted('report:reports:viewother') ? null : $this->userHelper->getUser()->getId(); | |
return $this->getRepository()->findReportsWithGraphs($ownedBy); | |
} | |
/** | |
* Determine what operators should be used for the filter type. | |
* | |
* @return mixed|string | |
*/ | |
private function getOperatorOptions(array $data) | |
{ | |
if (isset($data['operators'])) { | |
// Custom operators | |
$options = $data['operators']; | |
} else { | |
$operator = $data['operatorGroup'] ?? $data['type']; | |
if (!array_key_exists($operator, MauticReportBuilder::OPERATORS)) { | |
$operator = 'default'; | |
} | |
$options = MauticReportBuilder::OPERATORS[$operator]; | |
} | |
foreach ($options as &$label) { | |
$label = $this->translator->trans($label); | |
} | |
return $options; | |
} | |
private function getTotalCount(QueryBuilder $qb, array &$debugData): int | |
{ | |
$countQb = clone $qb; | |
$countQb->resetQueryParts(); | |
$countQb->select('count(*)') | |
->from('('.$qb->getSQL().')', 'c'); | |
if ($this->isDebugMode()) { | |
$debugData['count_query'] = $countQb->getSQL(); | |
} | |
return (int) $countQb->executeQuery()->fetchOne(); | |
} | |
/** | |
* @param int $segmentId | |
*/ | |
public function getReportsIdsWithDependenciesOnSegment($segmentId): array | |
{ | |
$search = 'lll.leadlist_id'; | |
$filter = [ | |
'force' => [ | |
['column' => 'r.filters', 'expr' => 'LIKE', 'value'=>'%'.$search.'"%'], | |
], | |
]; | |
$entities = $this->getEntities( | |
[ | |
'filter' => $filter, | |
] | |
); | |
$dependents = []; | |
foreach ($entities as $entity) { | |
$retrFilters = $entity->getFilters(); | |
foreach ($retrFilters as $eachFilter) { | |
if ($eachFilter['column'] == $search && $eachFilter['value'] == $segmentId) { | |
$dependents[] = $entity->getId(); | |
} | |
} | |
} | |
return $dependents; | |
} | |
/** | |
* @return array<int, int> | |
*/ | |
public function getReportsIdsWithDependenciesOnEmail(int $emailId): array | |
{ | |
$search = 'e.id'; | |
$filter = [ | |
'force' => [ | |
['column' => 'r.source', 'expr' => 'IN', 'value'=> ['emails', 'email.stats']], | |
['column' => 'r.filters', 'expr' => 'LIKE', 'value'=>'%'.$search.'"%'], | |
], | |
]; | |
$entities = $this->getEntities( | |
[ | |
'filter' => $filter, | |
] | |
); | |
$dependents = []; | |
foreach ($entities as $entity) { | |
foreach ($entity->getFilters() as $entityFilter) { | |
if ($entityFilter['column'] == $search && $entityFilter['value'] == $emailId) { | |
$dependents[] = $entity->getId(); | |
} | |
} | |
} | |
return array_unique($dependents); | |
} | |
/** | |
* @return \Doctrine\DBAL\Connection | |
*/ | |
private function getConnection() | |
{ | |
$connection = $this->em->getConnection(); | |
if ($connection instanceof PrimaryReadReplicaConnection) { | |
$connection->ensureConnectedToReplica(); | |
} | |
return $connection; | |
} | |
protected function isDebugMode(): bool | |
{ | |
return MAUTIC_ENV == 'dev' || $this->coreParametersHelper->get('debug'); | |
} | |
} | |