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); | |
| } | |
| } | |