Spaces:
No application file
No application file
namespace Mautic\CampaignBundle\Model; | |
use Doctrine\DBAL\Exception; | |
use Doctrine\ORM\EntityManager; | |
use Doctrine\ORM\PersistentCollection; | |
use Mautic\CampaignBundle\CampaignEvents; | |
use Mautic\CampaignBundle\Entity\Campaign; | |
use Mautic\CampaignBundle\Entity\Event; | |
use Mautic\CampaignBundle\Entity\Lead as CampaignLead; | |
use Mautic\CampaignBundle\Entity\LeadEventLogRepository; | |
use Mautic\CampaignBundle\Event as Events; | |
use Mautic\CampaignBundle\EventCollector\EventCollector; | |
use Mautic\CampaignBundle\Executioner\ContactFinder\Limiter\ContactLimiter; | |
use Mautic\CampaignBundle\Form\Type\CampaignType; | |
use Mautic\CampaignBundle\Helper\ChannelExtractor; | |
use Mautic\CampaignBundle\Membership\MembershipBuilder; | |
use Mautic\CoreBundle\Helper\Chart\ChartQuery; | |
use Mautic\CoreBundle\Helper\Chart\LineChart; | |
use Mautic\CoreBundle\Helper\CoreParametersHelper; | |
use Mautic\CoreBundle\Helper\UserHelper; | |
use Mautic\CoreBundle\Model\FormModel as CommonFormModel; | |
use Mautic\CoreBundle\Security\Permissions\CorePermissions; | |
use Mautic\CoreBundle\Translation\Translator; | |
use Mautic\EmailBundle\Entity\Stat; | |
use Mautic\EmailBundle\Entity\StatRepository; | |
use Mautic\FormBundle\Entity\Form; | |
use Mautic\FormBundle\Model\FormModel; | |
use Mautic\LeadBundle\Entity\Lead; | |
use Mautic\LeadBundle\Model\ListModel; | |
use Mautic\LeadBundle\Tracker\ContactTracker; | |
use Psr\Log\LoggerInterface; | |
use Symfony\Component\Console\Output\OutputInterface; | |
use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
use Symfony\Component\Form\FormFactoryInterface; | |
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; | |
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | |
/** | |
* @extends CommonFormModel<Campaign> | |
*/ | |
class CampaignModel extends CommonFormModel | |
{ | |
public function __construct( | |
protected ListModel $leadListModel, | |
protected FormModel $formModel, | |
private EventCollector $eventCollector, | |
private MembershipBuilder $membershipBuilder, | |
private ContactTracker $contactTracker, | |
EntityManager $em, | |
CorePermissions $security, | |
EventDispatcherInterface $dispatcher, | |
UrlGeneratorInterface $router, | |
Translator $translator, | |
UserHelper $userHelper, | |
LoggerInterface $mauticLogger, | |
CoreParametersHelper $coreParametersHelper | |
) { | |
parent::__construct($em, $security, $dispatcher, $router, $translator, $userHelper, $mauticLogger, $coreParametersHelper); | |
} | |
/** | |
* @return \Mautic\CampaignBundle\Entity\CampaignRepository | |
*/ | |
public function getRepository() | |
{ | |
$repo = $this->em->getRepository(Campaign::class); | |
$repo->setCurrentUser($this->userHelper->getUser()); | |
return $repo; | |
} | |
/** | |
* @return \Mautic\CampaignBundle\Entity\EventRepository | |
*/ | |
public function getEventRepository() | |
{ | |
return $this->em->getRepository(Event::class); | |
} | |
/** | |
* @return \Mautic\CampaignBundle\Entity\LeadRepository | |
*/ | |
public function getCampaignLeadRepository() | |
{ | |
return $this->em->getRepository(CampaignLead::class); | |
} | |
/** | |
* @return LeadEventLogRepository | |
*/ | |
public function getCampaignLeadEventLogRepository() | |
{ | |
return $this->em->getRepository(\Mautic\CampaignBundle\Entity\LeadEventLog::class); | |
} | |
public function getPermissionBase(): string | |
{ | |
return 'campaign:campaigns'; | |
} | |
/** | |
* @param object $entity | |
* @param string|null $action | |
* @param array $options | |
* | |
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException | |
*/ | |
public function createForm($entity, FormFactoryInterface $formFactory, $action = null, $options = []): \Symfony\Component\Form\FormInterface | |
{ | |
if (!$entity instanceof Campaign) { | |
throw new MethodNotAllowedHttpException(['Campaign']); | |
} | |
if (!empty($action)) { | |
$options['action'] = $action; | |
} | |
return $formFactory->create(CampaignType::class, $entity, $options); | |
} | |
/** | |
* Get a specific entity or generate a new one if id is empty. | |
*/ | |
public function getEntity($id = null): ?Campaign | |
{ | |
if (null === $id) { | |
return new Campaign(); | |
} | |
return parent::getEntity($id); | |
} | |
/** | |
* Delete an array of campaigns. | |
* | |
* @param int[] $campaignIds | |
* | |
* @return array<int,Campaign> | |
*/ | |
public function deleteEntities($campaignIds): array | |
{ | |
$entities = []; | |
foreach ($campaignIds as $campaignId) { | |
$campaign = $this->getEntity($campaignId); | |
if ($campaign) { | |
$entities[$campaignId] = $campaign; | |
$this->deleteEntity($campaign); | |
} | |
} | |
return $entities; | |
} | |
public function deleteEntity($entity): void | |
{ | |
// Null all the event parents for this campaign to avoid database constraints | |
$this->getEventRepository()->nullEventParents($entity->getId()); | |
$this->dispatchEvent('pre_delete', $entity); | |
$this->getRepository()->setCampaignAsDeleted($entity->getId()); | |
$this->dispatcher->dispatch(new Events\DeleteCampaign($entity), CampaignEvents::ON_CAMPAIGN_DELETE); | |
} | |
public function deleteCampaign(Campaign $campaign): void | |
{ | |
$campaign->deletedId = $campaign->getId(); | |
$this->getRepository()->deleteEntity($campaign); | |
$this->dispatchEvent('post_delete', $campaign); | |
} | |
/** | |
* @throws MethodNotAllowedHttpException | |
*/ | |
protected function dispatchEvent($action, &$entity, $isNew = false, \Symfony\Contracts\EventDispatcher\Event $event = null): ?\Symfony\Contracts\EventDispatcher\Event | |
{ | |
if ($entity instanceof CampaignLead) { | |
return null; | |
} | |
if (!$entity instanceof Campaign) { | |
throw new MethodNotAllowedHttpException(['Campaign']); | |
} | |
switch ($action) { | |
case 'pre_save': | |
$name = CampaignEvents::CAMPAIGN_PRE_SAVE; | |
break; | |
case 'post_save': | |
$name = CampaignEvents::CAMPAIGN_POST_SAVE; | |
break; | |
case 'pre_delete': | |
$name = CampaignEvents::CAMPAIGN_PRE_DELETE; | |
break; | |
case 'post_delete': | |
$name = CampaignEvents::CAMPAIGN_POST_DELETE; | |
break; | |
default: | |
return null; | |
} | |
if ($this->dispatcher->hasListeners($name)) { | |
if (empty($event)) { | |
$event = new Events\CampaignEvent($entity, $isNew); | |
} | |
$this->dispatcher->dispatch($event, $name); | |
return $event; | |
} else { | |
return null; | |
} | |
} | |
/** | |
* @return array | |
*/ | |
public function setEvents(Campaign $entity, $sessionEvents, $sessionConnections, $deletedEvents) | |
{ | |
$existingEvents = $entity->getEvents()->toArray(); | |
$events = []; | |
$hierarchy = []; | |
foreach ($sessionEvents as $properties) { | |
$isNew = (!empty($properties['id']) && isset($existingEvents[$properties['id']])) ? false : true; | |
$event = !$isNew ? $existingEvents[$properties['id']] : new Event(); | |
foreach ($properties as $f => $v) { | |
if ('id' == $f && str_starts_with($v, 'new')) { | |
// set the temp ID used to be able to match up connections | |
$event->setTempId($v); | |
} | |
if (in_array($f, ['id', 'parent'])) { | |
continue; | |
} | |
$func = 'set'.ucfirst($f); | |
if (method_exists($event, $func)) { | |
$event->$func($v); | |
} | |
} | |
ChannelExtractor::setChannel($event, $event, $this->eventCollector->getEventConfig($event)); | |
$event->setCampaign($entity); | |
$events[$properties['id']] = $event; | |
} | |
foreach ($deletedEvents as $deleteMe) { | |
if (isset($existingEvents[$deleteMe])) { | |
// Remove child from parent | |
$parent = $existingEvents[$deleteMe]->getParent(); | |
if ($parent) { | |
$parent->removeChild($existingEvents[$deleteMe]); | |
$existingEvents[$deleteMe]->removeParent(); | |
} | |
$entity->removeEvent($existingEvents[$deleteMe]); | |
unset($events[$deleteMe]); | |
} | |
} | |
$relationships = []; | |
if (isset($sessionConnections['connections'])) { | |
foreach ($sessionConnections['connections'] as $connection) { | |
$source = $connection['sourceId']; | |
$target = $connection['targetId']; | |
if (in_array($source, ['lists', 'forms'])) { | |
// Only concerned with events and not sources | |
continue; | |
} | |
if (isset($connection['anchors']['source'])) { | |
$sourceDecision = $connection['anchors']['source']; | |
} else { | |
$sourceDecision = (!empty($connection['anchors'][0])) ? $connection['anchors'][0]['endpoint'] : null; | |
} | |
if ('leadsource' == $sourceDecision) { | |
// Lead source connection that does not matter | |
continue; | |
} | |
$relationships[$target] = [ | |
'parent' => $source, | |
'decision' => $sourceDecision, | |
]; | |
} | |
} | |
// Assign parent/child relationships | |
foreach ($events as $id => $e) { | |
if (isset($relationships[$id])) { | |
// Has a parent | |
$anchor = in_array($relationships[$id]['decision'], ['yes', 'no']) ? $relationships[$id]['decision'] : null; | |
$events[$id]->setDecisionPath($anchor); | |
$parentId = $relationships[$id]['parent']; | |
$events[$id]->setParent($events[$parentId]); | |
$hierarchy[$id] = $parentId; | |
} elseif ($events[$id]->getParent()) { | |
// No longer has a parent so null it out | |
// Remove decision so that it doesn't affect execution | |
$events[$id]->setDecisionPath(null); | |
// Remove child from parent | |
$parent = $events[$id]->getParent(); | |
$parent->removeChild($events[$id]); | |
// Remove parent from child | |
$events[$id]->removeParent(); | |
$hierarchy[$id] = 'null'; | |
} else { | |
// Is a parent | |
$hierarchy[$id] = 'null'; | |
// Remove decision so that it doesn't affect execution | |
$events[$id]->setDecisionPath(null); | |
} | |
} | |
$entity->addEvents($events); | |
// set event order used when querying the events | |
$this->buildOrder($hierarchy, $events, $entity); | |
uasort( | |
$events, | |
function ($a, $b): int { | |
$aOrder = $a->getOrder(); | |
$bOrder = $b->getOrder(); | |
return $aOrder <=> $bOrder; | |
} | |
); | |
// Persist events if campaign is being edited | |
if ($entity->getId()) { | |
$this->getEventRepository()->saveEntities($events); | |
} | |
return $events; | |
} | |
/** | |
* @param bool $persist | |
* | |
* @return array | |
*/ | |
public function setCanvasSettings($entity, $settings, $persist = true, $events = null) | |
{ | |
if (null === $events) { | |
$events = $entity->getEvents(); | |
} | |
$tempIds = []; | |
foreach ($events as $e) { | |
if ($e instanceof Event) { | |
$tempIds[$e->getTempId()] = $e->getId(); | |
} else { | |
$tempIds[$e['tempId']] = $e['id']; | |
} | |
} | |
if (!isset($settings['nodes'])) { | |
$settings['nodes'] = []; | |
} | |
foreach ($settings['nodes'] as &$node) { | |
if (str_contains($node['id'], 'new')) { | |
// Find the real one and update the node | |
$node['id'] = str_replace($node['id'], $tempIds[$node['id']], $node['id']); | |
} | |
} | |
if (!isset($settings['connections'])) { | |
$settings['connections'] = []; | |
} | |
foreach ($settings['connections'] as &$connection) { | |
// Check source | |
if (str_contains($connection['sourceId'], 'new')) { | |
// Find the real one and update the node | |
$connection['sourceId'] = str_replace($connection['sourceId'], $tempIds[$connection['sourceId']], $connection['sourceId']); | |
} | |
// Check target | |
if (str_contains($connection['targetId'], 'new')) { | |
// Find the real one and update the node | |
$connection['targetId'] = str_replace($connection['targetId'], $tempIds[$connection['targetId']], $connection['targetId']); | |
} | |
// Rebuild anchors | |
if (!isset($connection['anchors']['source'])) { | |
$anchors = []; | |
foreach ($connection['anchors'] as $k => $anchor) { | |
$type = (0 === $k) ? 'source' : 'target'; | |
$anchors[$type] = $anchor['endpoint']; | |
} | |
$connection['anchors'] = $anchors; | |
} | |
} | |
$entity->setCanvasSettings($settings); | |
if ($persist) { | |
$this->getRepository()->saveEntity($entity); | |
} | |
return $settings; | |
} | |
/** | |
* Get list of sources for a campaign. | |
*/ | |
public function getLeadSources($campaign): array | |
{ | |
$campaignId = ($campaign instanceof Campaign) ? $campaign->getId() : $campaign; | |
$sources = []; | |
// Lead lists | |
$sources['lists'] = $this->getRepository()->getCampaignListSources($campaignId); | |
// Forms | |
$sources['forms'] = $this->getRepository()->getCampaignFormSources($campaignId); | |
return $sources; | |
} | |
/** | |
* Add and/or delete lead sources from a campaign. | |
*/ | |
public function setLeadSources(Campaign $entity, $addedSources, $deletedSources): void | |
{ | |
foreach ($addedSources as $type => $sources) { | |
foreach ($sources as $id => $label) { | |
switch ($type) { | |
case 'lists': | |
$entity->addList($this->em->getReference(\Mautic\LeadBundle\Entity\LeadList::class, $id)); | |
break; | |
case 'forms': | |
$entity->addForm($this->em->getReference(Form::class, $id)); | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
foreach ($deletedSources as $type => $sources) { | |
foreach ($sources as $id => $label) { | |
switch ($type) { | |
case 'lists': | |
$entity->removeList($this->em->getReference(\Mautic\LeadBundle\Entity\LeadList::class, $id)); | |
break; | |
case 'forms': | |
$entity->removeForm($this->em->getReference(Form::class, $id)); | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
} | |
/** | |
* Get a list of source choices. | |
* | |
* @param string $sourceType | |
* @param bool $globalOnly | |
*/ | |
public function getSourceLists($sourceType = null, $globalOnly = false): array | |
{ | |
$choices = []; | |
switch ($sourceType) { | |
case 'lists': | |
case null: | |
$choices['lists'] = []; | |
$lists = $globalOnly ? $this->leadListModel->getGlobalLists() : $this->leadListModel->getUserLists(); | |
if ($lists) { | |
foreach ($lists as $list) { | |
$choices['lists'][$list['id']] = $list['name']; | |
} | |
} | |
// no break | |
case 'forms': | |
case null: | |
$choices['forms'] = []; | |
$viewOther = $this->security->isGranted('form:forms:viewother'); | |
$repo = $this->formModel->getRepository(); | |
$repo->setCurrentUser($this->userHelper->getUser()); | |
$forms = $repo->getFormList('', 0, 0, $viewOther, 'campaign'); | |
foreach ($forms as $form) { | |
$choices['forms'][$form['id']] = $form['name']; | |
} | |
} | |
foreach ($choices as &$typeChoices) { | |
asort($typeChoices); | |
} | |
return (null == $sourceType) ? $choices : $choices[$sourceType]; | |
} | |
/** | |
* @param mixed $form | |
* | |
* @return array | |
*/ | |
public function getCampaignsByForm($form) | |
{ | |
$formId = ($form instanceof Form) ? $form->getId() : $form; | |
return $this->getRepository()->findByFormId($formId); | |
} | |
/** | |
* Gets the campaigns a specific lead is part of. | |
* | |
* @param bool $forList | |
* | |
* @return mixed | |
*/ | |
public function getLeadCampaigns(Lead $lead = null, $forList = false) | |
{ | |
static $campaigns = []; | |
if (null === $lead) { | |
$lead = $this->contactTracker->getContact(); | |
} | |
if (!isset($campaigns[$lead->getId()])) { | |
$repo = $this->getRepository(); | |
$leadId = $lead->getId(); | |
// get the campaigns the lead is currently part of | |
$campaigns[$leadId] = $repo->getPublishedCampaigns( | |
null, | |
$lead->getId(), | |
$forList, | |
$this->security->isGranted($this->getPermissionBase().':viewother') | |
); | |
} | |
return $campaigns[$lead->getId()]; | |
} | |
/** | |
* Gets a list of published campaigns. | |
* | |
* @return array | |
*/ | |
public function getPublishedCampaigns(bool $forList = false) | |
{ | |
static $campaigns = []; | |
if (empty($campaigns)) { | |
$campaigns = $this->getRepository()->getPublishedCampaigns( | |
null, | |
null, | |
$forList, | |
$this->security->isGranted($this->getPermissionBase().':viewother') | |
); | |
} | |
return $campaigns; | |
} | |
/** | |
* Saves a campaign lead, logs the error if saving fails. | |
* | |
* @return bool | |
*/ | |
public function saveCampaignLead(CampaignLead $campaignLead) | |
{ | |
try { | |
$this->getCampaignLeadRepository()->saveEntity($campaignLead); | |
return true; | |
} catch (\Exception $exception) { | |
$this->logger->log('error', $exception->getMessage(), ['exception' => $exception]); | |
return false; | |
} | |
} | |
/** | |
* Get details of leads in a campaign. | |
* | |
* @return mixed | |
*/ | |
public function getLeadDetails($campaign, $leads = null) | |
{ | |
$campaignId = ($campaign instanceof Campaign) ? $campaign->getId() : $campaign; | |
if ($leads instanceof PersistentCollection) { | |
$leads = array_keys($leads->toArray()); | |
} | |
return $this->em->getRepository(CampaignLead::class)->getLeadDetails($campaignId, $leads); | |
} | |
/** | |
* Get leads for a campaign. If $event is passed in, only leads who have not triggered the event are returned. | |
* | |
* @param Campaign $campaign | |
* @param array $event | |
* | |
* @return mixed | |
*/ | |
public function getCampaignLeads($campaign, $event = null) | |
{ | |
$campaignId = ($campaign instanceof Campaign) ? $campaign->getId() : $campaign; | |
$eventId = (is_array($event) && isset($event['id'])) ? $event['id'] : $event; | |
return $this->em->getRepository(CampaignLead::class)->getLeads($campaignId, $eventId); | |
} | |
public function getCampaignListIds($id): array | |
{ | |
return $this->getRepository()->getCampaignListIds((int) $id); | |
} | |
/** | |
* Get line chart data of leads added to campaigns. | |
* | |
* @param string $unit {@link php.net/manual/en/function.date.php#refsect1-function.date-parameters} | |
* @param string $dateFormat | |
* @param array $filter | |
* @param bool $canViewOthers | |
*/ | |
public function getLeadsAddedLineChartData($unit, \DateTime $dateFrom, \DateTime $dateTo, $dateFormat = null, $filter = [], $canViewOthers = true): array | |
{ | |
$chart = new LineChart($unit, $dateFrom, $dateTo, $dateFormat); | |
$query = new ChartQuery($this->em->getConnection(), $dateFrom, $dateTo); | |
$q = $query->prepareTimeDataQuery('campaign_leads', 'date_added', $filter); | |
if (!$canViewOthers) { | |
$q->join('t', MAUTIC_TABLE_PREFIX.'campaigns', 'c', 'c.id = c.campaign_id') | |
->andWhere('c.created_by = :userId') | |
->setParameter('userId', $this->userHelper->getUser()->getId()); | |
} | |
$data = $query->loadAndBuildTimeData($q); | |
$chart->setDataset($this->translator->trans('mautic.campaign.campaign.leads'), $data); | |
return $chart->render(); | |
} | |
/** | |
* Get line chart data of hits. | |
* | |
* @param string|null $unit {@link php.net/manual/en/function.date.php#refsect1-function.date-parameters} | |
* @param string $dateFormat | |
* @param array $filter | |
*/ | |
public function getCampaignMetricsLineChartData($unit, \DateTime $dateFrom, \DateTime $dateTo, $dateFormat = null, $filter = []): array | |
{ | |
$events = []; | |
$chart = new LineChart($unit, $dateFrom, $dateTo, $dateFormat); | |
$query = new ChartQuery($this->em->getConnection(), $dateFrom, $dateTo); | |
$contacts = $query->fetchTimeData('campaign_leads', 'date_added', $filter); | |
$chart->setDataset($this->translator->trans('mautic.campaign.campaign.leads'), $contacts); | |
if (isset($filter['campaign_id'])) { | |
$rawEvents = $this->getEventRepository()->getCampaignEvents($filter['campaign_id']); | |
// Group events by type | |
foreach ($rawEvents as $event) { | |
if (isset($events[$event['type']])) { | |
$events[$event['type']][] = $event['id']; | |
} else { | |
$events[$event['type']] = [$event['id']]; | |
} | |
} | |
if ($events) { | |
foreach ($events as $type => $eventIds) { | |
$filter['event_id'] = $eventIds; | |
if ($this->coreParametersHelper->get('campaign_use_summary')) { | |
$q = $query->prepareTimeDataQuery('campaign_summary', 'date_triggered', $filter, 'triggered_count + non_action_path_taken_count', 'sum'); | |
$rawData = $q->executeQuery()->fetchAllAssociative(); | |
} else { | |
// Exclude failed events | |
$failedSq = $this->em->getConnection()->createQueryBuilder(); | |
$failedSq->select('null') | |
->from(MAUTIC_TABLE_PREFIX.'campaign_lead_event_failed_log', 'fe') | |
->where( | |
$failedSq->expr()->eq('fe.log_id', 't.id') | |
); | |
$filter['failed_events'] = [ | |
'subquery' => sprintf('NOT EXISTS (%s)', $failedSq->getSQL()), | |
]; | |
$q = $query->prepareTimeDataQuery('campaign_lead_event_log', 'date_triggered', $filter); | |
$rawData = $q->executeQuery()->fetchAllAssociative(); | |
} | |
if (!empty($rawData)) { | |
$triggers = $query->completeTimeData($rawData); | |
$chart->setDataset($this->translator->trans('mautic.campaign.'.$type), $triggers); | |
} | |
} | |
unset($filter['event_id']); | |
} | |
} | |
return $chart->render(); | |
} | |
/** | |
* @param Campaign $entity | |
* @param string $root | |
* @param int $order | |
*/ | |
protected function buildOrder($hierarchy, &$events, $entity, $root = 'null', $order = 1) | |
{ | |
$count = count($hierarchy); | |
if (1 === $count && 'null' === array_unique(array_values($hierarchy))[0]) { | |
// no parents so leave order as is | |
return; | |
} else { | |
foreach ($hierarchy as $eventId => $parent) { | |
if ($parent == $root || 1 === $count) { | |
$events[$eventId]->setOrder($order); | |
unset($hierarchy[$eventId]); | |
if (count($hierarchy)) { | |
$this->buildOrder($hierarchy, $events, $entity, $eventId, $order + 1); | |
} | |
} | |
} | |
} | |
} | |
/** | |
* @param int $limit | |
* @param bool $maxLeads | |
*/ | |
public function rebuildCampaignLeads(Campaign $campaign, $limit = 1000, $maxLeads = false, OutputInterface $output = null): int | |
{ | |
$contactLimiter = new ContactLimiter($limit); | |
return $this->membershipBuilder->build($campaign, $contactLimiter, $maxLeads, $output); | |
} | |
public function getCampaignIdsWithDependenciesOnSegment($segmentId): array | |
{ | |
$entities = $this->getRepository()->getEntities( | |
[ | |
'filter' => [ | |
'force' => [ | |
[ | |
'column' => 'l.id', | |
'expr' => 'eq', | |
'value' => $segmentId, | |
], | |
], | |
], | |
'joinLists' => true, | |
] | |
); | |
$ids = []; | |
foreach ($entities as $entity) { | |
$ids[] = $entity->getId(); | |
} | |
return $ids; | |
} | |
/** | |
* @return array<int, int> | |
*/ | |
public function getCampaignIdsWithDependenciesOnEmail(int $emailId): array | |
{ | |
return $this->getRepository()->getCampaignIdsWithDependenciesOnEmail($emailId); | |
} | |
/** | |
* @return array<string, array<int, array<string, int|string>>> | |
* | |
* @throws Exception | |
*/ | |
public function getCountryStats(Campaign $entity, \DateTimeImmutable $dateFrom, \DateTimeImmutable $dateTo): array | |
{ | |
/** @var StatRepository $statRepo */ | |
$statRepo = $this->em->getRepository(Stat::class); | |
$results['contacts'] = $this->getCampaignMembersGroupByCountry($entity, $dateFrom, $dateTo); | |
if ($entity->isEmailCampaign()) { | |
$eventsEmailsSend = $entity->getEmailSendEvents(); | |
$eventsIds = $eventsEmailsSend->getKeys(); | |
$emailIds = []; | |
foreach ($eventsEmailsSend as $event) { | |
$emailIds[] = $event->getChannelId(); | |
} | |
$emailStats = $statRepo->getStatsSummaryByCountry($dateFrom, $dateTo, $emailIds, 'campaign', $eventsIds); | |
$results['read_count'] = $results['clicked_through_count'] = []; | |
foreach ($emailStats as $e) { | |
$results['read_count'][] = array_intersect_key($e, array_flip(['country', 'read_count'])); | |
$results['clicked_through_count'][] = array_intersect_key($e, array_flip(['country', 'clicked_through_count'])); | |
} | |
} | |
return $results; | |
} | |
/** | |
* Get leads in a campaign grouped by country. | |
* | |
* @return array{}|array<int, array<string, string|null>> | |
*/ | |
public function getCampaignMembersGroupByCountry(Campaign $campaign, \DateTimeImmutable $dateFromObject, \DateTimeImmutable $dateToObject): array | |
{ | |
return $this->em->getRepository(CampaignLead::class)->getCampaignMembersGroupByCountry($campaign, $dateFromObject, $dateToObject); | |
} | |
} | |