Contacts

Note

此页面的内容需要进行重大更新。旧页面包含过时且可能不准确的信息。您仍然可以在 Mautic Developer Documentation archived repository 中访问它。

如果您有兴趣帮助开发此页面和其他新内容的,请考虑加入文档编写工作。

请阅读 Contributing GuidelinesContributing to Mautic’s documentation 以开始您的贡献。

有几种方法可以扩展 Mautic 中的联系人功能。 其中一种方法是在联系人的事件时间轴中显示自定义事件,本文档将向您展示如何操作。

Note

在 Mautic 1.4 中,Leads 被重命名为 Contacts。但是,许多代码仍然将 Contacts 称为 Leads。

创建联系人

要创建新的联系人,请使用 \Mautic\LeadBundle\Entity\Lead 实体。查看以下代码示例。

<?php
// plugins/HelloWorldBundle/Services/ContactService.php

declare(strict_types=1);

namespace MauticPlugin\HelloWorldBundle\Services;

use Mautic\CoreBundle\Helper\IpLookupHelper;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\LeadBundle\Entity\LeadRepository;
use Mautic\LeadBundle\Model\FieldModel;
use Mautic\LeadBundle\Model\LeadModel;
use Mautic\LeadBundle\Tracker\ContactTracker;

class ContactService
{
    protected LeadModel $leadModel;
    protected ContactTracker $contactTracker;
    protected IpLookupHelper $ipLookupHelper;
    protected FieldModel $fieldModel;
    protected LeadRepository $leadRepository;

    public function __construct(
        LeadModel $leadModel,
        ContactTracker $contactTracker,
        IpLookupHelper $ipLookupHelper,
        FieldModel $fieldModel,
        LeadRepository $leadRepository
    ) {
        $this->leadModel      = $leadModel;
        $this->contactTracker = $contactTracker;
        $this->ipLookupHelper = $ipLookupHelper;
        $this->fieldModel     = $fieldModel;
        $this->leadRepository = $leadRepository;
    }

    public function createLead()
    {
        // Currently tracked Contact based on cookies
        $lead = $this->contactTracker->getContact();
        $leadId = $lead->getId();

        // OR generate a completely new Contact with
        $lead = new Lead();
        $lead->setNewlyCreated(true);
        $leadId = null;

        // IP address of the request
        $ipAddress = $this->ipLookupHelper->getIpAddress();

        // Updated/new fields
        $leadFields = array(
            'firstname' => 'Bob',
            //...
        );

        // Optionally check for identifier fields to determine if the Contact is unique
        $uniqueLeadFields    = $this->fieldModel->getUniqueIdentiferFields();
        $uniqueLeadFieldData = array();
// Check if unique identifier fields are included

$inList = array_intersect_key($leadFields, $uniqueLeadFields); foreach ($inList as $k => $v) {

if (empty($query[$k])) {

unset($inList[$k]);

}

if (array_key_exists($k, $uniqueLeadFields)) {

$uniqueLeadFieldData[$k] = $v;

}

}

// If there are unique identifier fields, check for existing Contacts based on Contact data if (count($inList) && count($uniqueLeadFieldData)) {

$existingLeads = $this->leadRepository->getLeadsByUniqueFields(

$uniqueLeadFieldData, $leadId // If a currently tracked Contact, ignore this ID when searching for duplicates

); if (!empty($existingLeads)) {

// Existing found so merge the two Contacts $lead = $this->leadModel->mergeLeads($lead, $existingLeads[0]);

}

// Get the Contact’s currently associated IPs $leadIpAddresses = $lead->getIpAddresses();

// If the IP is not already associated, do so (the addIpAddress will automatically handle ignoring // the IP if it is set to be ignored in the Configuration) if (!$leadIpAddresses->contains($ipAddress)) {

$lead->addIpAddress($ipAddress);

}

}

// Set the Contact’s data $this->leadModel->setFieldValues($lead, $leadFields);

// Save the entity $this->leadModel->saveEntity($lead);

}

}

Contact tracking

Contacts get tracked by two cookies. The first cookie registers the ID of the Contact that’s tracked by Mautic. The second is to track the Contact’s activity for the current session. This defaults to 30 minutes and resets during each Contact interaction.

mautic_session_id holds the value of the Contact’s current session ID. That value is then name of the cookie that holds the Contact’s ID.

Review the sample code on how to obtain the currently tracked Contact.

Note

As of Mautic 2.2.0, a cookie is also placed on any domain with mtc.js embedded. Ensure that Mautic’s CORS settings allow the domain. This contains the ID of the currently tracked Contact.

<?php
// plugins/HelloWorldBundle/Services/ContactTrackingService.php

declare(strict_types=1);

namespace MauticPlugin\HelloWorldBundle\Services;

use Mautic\LeadBundle\Entity\Lead;
use Mautic\LeadBundle\Tracker\ContactTracker;

class ContactTrackingService
{
    protected ContactTracker $contactTracker;

    public function __construct(ContactTracker $contactTracker) {
        $this->contactTracker = $contactTracker;
    }

    public function track() {
        $currentContact = $this->contactTracker->getContact();
// To obtain the tracking ID, use getTrackingId();

$trackingId = $this->contactTracker->getTrackingId();

// Set the currently tracked Contact and generate tracking cookies $lead = new Lead(); // … $this->contactTracker->setTrackedContact($lead);

// Set a Contact for system use purposes (i.e. events that use getCurrentLead()) but without generating tracking cookies $this->contactTracker->setSystemContact($lead);

}

}

Contact timeline/history

To inject events into a Contact’s timeline, create an event listener that listens to the LeadEvents::TIMELINE_ON_GENERATE event. Using this event, the Plugin can inject unique items into the timeline and also into the engagements graph on each page.

Note

Before using this event listener, you’ll need to ensure that you store your custom events in a custom database table. See components/contacts:Generating timeline events from your own custom events below for more details.

The event listener receives a Mautic\LeadBundle\Event\LeadTimelineEvent object. You can find the commonly used methods below the code example.

<?php
// plugins/HelloWorldBundle/EventListener/LeadSubscriber.php

declare(strict_types=1);

namespace MauticPlugin\HelloWorldBundle\EventListener;

use Doctrine\ORM\EntityManager;
use Mautic\LeadBundle\Event\LeadTimelineEvent;
use Mautic\LeadBundle\LeadEvents;
use MauticPlugin\HelloWorldBundle\Entity\WorldRepository;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

final class LeadSubscriber implements EventSubscriberInterface
{
    private TranslatorInterface $translator;
    private EntityManager $em;
    private RouterInterface $router;

    public function __construct(TranslatorInterface $translator, EntityManager $em, RouterInterface $router)
    {
        $this->translator = $translator;
        $this->em         = $em;
        $this->router     = $router;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            LeadEvents::TIMELINE_ON_GENERATE => ['onTimelineGenerate', 0]
        ];
    }

    public function onTimelineGenerate(LeadTimelineEvent $event): void
    {
        // Add this event to the list of available events which generates the event type filters
        $eventTypeKey  = 'visited.worlds';
        $eventTypeName = $this->translator->trans('mautic.hello.world.visited_worlds');
        $event->addEventType($eventTypeKey, $eventTypeName);

        // Determine if this event has been filtered out
        if (!$event->isApplicable($eventTypeKey)) {
            return;
        }
/** @var WorldRepository */

$repository = $this->em->getRepository(WorldRepository::class);

// $event->getQueryOptions() 提供时间线过滤器等。 // 此方法应使用 DBAL 获取要注入到时间线中的事件,并根据分页进行查询。 // 此外,还应查询总事件数,并返回一个包含 [‘total’ => $x, ‘results’ => []] 的数组。 // TimelineTrait 可以提供帮助。请参阅存储库示例。 $stats = $repository->getTimelineStats($event->getLead()->getId(), $event->getQueryOptions());

// 如果是 isEngagementCount(),则此事件应仅将 $stats 注入到 addToCounter() 中,以追加数据并生成 // 参与度图表。并非所有事件都是参与度相关的,如果只是信息性事件,那么该行可能只应在 !$event->isEngagementCount() 时使用。 // 如果在 stats 方法中使用 TimelineTrait,它将根据 getQueryOptions() 中包含的数据确定适当的返回值。 $event->addToCounter($eventTypeKey, $stats);

if (!$event->isEngagementCount()) {

// Add the events to the event array foreach ($stats[‘results’] as $stat) {

if ($stat[‘dateSent’]) {
$event->addEvent(
[

// Event key type ‘event’ => $eventTypeKey, // Event name/label - can be a string or an array as below to convert to a link ‘eventLabel’ => [

‘label’ => $stat[‘name’], ‘href’ => $this->router->generate(

‘mautic_dynamicContent_action’, [‘objectId’ => $stat[‘dynamic_content_id’], ‘objectAction’ => ‘view’]

)

], // Translated string displayed in the Event Type column ‘eventType’ => $eventTypeName, // DateTime object for the timestamp column ‘timestamp’ => $stat[‘dateSent’], // Optional details passed through to the contentTemplate ‘extra’ => [

‘stat’ => $stat, ‘type’ => ‘sent’

], // Optional template to customize the details of the event in the timeline ‘contentTemplate’ => ‘MauticDynamicContentBundle:SubscribedEventsTimeline:index.html.php’, // Font Awesome class to display as the icon ‘icon’ => ‘fa-envelope’

]

);

}

}

}

}

}

Method

Description

isApplicable()

Determines if this event is applicable and not filtered out.

addEventType()

Required - Add this event to the list of available events.

getLead()

Get the Contact entity

getQueryOptions()

Used to get pagination, filters, etc needed to generate an appropriate query.

addToCounter()

Used to add total number of events across all Landing Pages to the counters. This also generates the numbers for the engagements graph.

addEvent()

Required - Injects an event into the timeline. Accepts an array with the keys defined as below.

    • Key
      • Required

      • Type

      • Description

        • event

        • Required

        • string

        • The key for this event. Eg. world.visited

        • eventType

        • Required

        • string

        • The translated string representing this event type. Eg. Worlds visited

        • timestamp

        • Required

        • DateTime

        • DateTime object when this event took place

        • eventLabel

        • Optional

        • string/array

        • The translated string to display in the event name. Examples include names of items, Landing Page titles, etc. This can also be an array of [‘label’ => ‘’, ‘href’ => ‘’] to have the entry converted to a link. This defaults to eventType if not defined.

        • extra

        • Optional

        • array

        • Anything you want to pass through to the content template to generate the details view for this event

        • contentTemplate

        • Optional

        • string

        • Template you want to use to generate the details view for this event. Eg. HelloBundle:SubscribedEvents\Timeline:index.html.php

        • icon

        • Optional

        • Font Awesome class

从自定义事件生成时间线事件

您负责创建自己的事件并将它们存储在适当的数据库表中。 然后,您可以将它们转换为时间线事件,以便它们显示在联系人的详细信息屏幕上。 为了使此过程更容易,提供了 Mautic\LeadBundle\Entity\TimelineTrait trait。

<?php
// plugins/HelloWorldBundle/Entity/WorldRepository.php

declare(strict_types=1);

namespace MauticPlugin\HelloWorldBundle\Entity;

use Mautic\CoreBundle\Entity\CommonRepository;
use Mautic\LeadBundle\Entity\TimelineTrait;

/**
* @extends CommonRepository<World>
*/
class WorldRepository extends CommonRepository
{
    use TimelineTrait;

    /**
    * @param array<string,string> $options
    * @return array<string,mixed>
    */
    public function getTimelineStats(int $leadId, array $options = []): array
    {
        $query = $this->getEntityManager()->getConnection()->createQueryBuilder();

        $query->select('w.id, w.name, w.visited_count, w.date_visited, w.visit_details')
            ->from(MAUTIC_TABLE_PREFIX . 'world_visits', 'w')
            ->where($query->expr()->eq('w.lead_id', (int) $leadId));

        if (isset($options['search']) && $options['search']) {
            $query->andWhere(
                $query->expr()->like('w.name', $query->expr()->literal('%' . $options['search'] . '%'))
            );
        }

        return $this->getTimelineResults($query, $options, 'w.name', 'w.date_visited', ['visit_details'], ['date_visited']);
    }
}

为了实现这一点,在存储库方法中接受来自 ‘$event->getQueryOptions()’ 的数组。创建一个 DBAL QueryBuilder 对象 ($this->getEntityManager()->getConnection()->createQueryBuilder()) 并定义该数组的基本信息,包括按潜在客户 ID 进行过滤和搜索过滤器。然后,将 QueryBuilder 对象传递给 getTimelineResults() 方法,并附带以下参数:

Key

Required

Type

Description

$query

Required

QueryBuilder

定义查询基本信息的数据库抽象层 QueryBuilder 对象。

$options

Required

array

由事件监听器中的 ‘$event->getQueryOptions()’ 生成并传递给该方法的数组。

$eventNameColumn

Required

string

用于按事件名称进行排序时,应使用的带有表前缀的列名。

$timestampColumn

Required

string

用于按时间戳进行排序时,应使用的带有表前缀的列名。

$serializedColumns

Optional

array

当使用数据库抽象层时,数组不会由 Doctrine 自动反序列化。在此处定义这些列,这些列是查询结果返回的值,以便自动反序列化。

$dateTimeColumns

Optional

array

当使用数据库抽象层时,datetime 列不会由 Doctrine 自动转换为 DateTime 对象。在此处定义这些列,这些列是查询结果返回的值,以便自动执行此操作。

$resultsParserCallback

Optional

callback

用于自定义解析结果的回调函数。这是一个可选参数,主要用于处理当所有结果都已循环遍历以进行 ‘$serializedColumns’$dateTimeColumns 处理时的情况。