What is the proper way to create / update a lead programmatically?

Hello,
Im working on a simple plugin that takes a scheduled appointment POST from a 3rd party webhook and depending on if the contact exists or not - either creates or updates a contact, adds a couple of notes to that contact and then adds the contact to a particular segment.

So far I have the basic plugin functional, where I am having issues is in identifying existing contacts and merging them vs creating a new lead.

I tried to follow the explanations and examples related to this in the developer documentation, but no matter what I do they dont seem to work. I can create contacts just fine, but they are duplicated.
The code below is slightly modified from the example. I removed the if ($query…) conditional, as in this case it always resulted in $inList having no data and the later conditional never executing the check. The only unique data field that matches is “email” - and that is consistent with the way our data is organized.

Can someone take a look at this portion of the code and tell me the proper way to identify and merge existing contacts?

        $lead = new Lead();
        $lead->setNewlyCreated(true);
        $leadId = "";
        
        //check if the contact exists
        $uniqueLeadFields = $this->getModel('lead.field')->getUniqueIdentiferFields();
        $uniqueLeadFieldData = array();

// Check if unique identifier fields are included
        $inList = array_intersect_key($data, $uniqueLeadFields);
        foreach ($inList as $k => $v) {
            if (array_key_exists($k, $uniqueLeadFields)) {
                $uniqueLeadFieldData[$k] = $v;
            }
        }

// If there are unique identifier fields, check for existing leads based on lead data
        if (count($inList) && count($uniqueLeadFieldData)) {
            $existingLeads = $this->getDoctrine()->getManager()->getRepository('MauticLeadBundle:Lead')->getLeadsByUniqueFields(
                    $uniqueLeadFieldData,
                    $leadId // If a currently tracked lead, ignore this ID when searching for duplicates
            );
            if (!empty($existingLeads)) {
                // Existing found so merge the two leads
                $lead = $leadModel->mergeLeads($lead, $existingLeads[0]);
            }

            // Get the lead'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 lead's data
        $leadModel->setFieldValues($lead, $data);

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

Thanks in advance for any help with this!

So after reading ALOT of the core mautic source code I came up with this solution to my issue. It seems to work and is pretty direct - however Im not sure if it’s the “proper” way of doing things. For instance, im not sure its really “merging” data the way it should, I think its probably just overwriting the data in the existing lead. Seems to work for my purposes - but I’m still curious what the proper way of merging would be.

        //initialize the lead model
        $leadModel = $this->getModel('lead');

        $lead = null;
        
        //check if the contact exists
        $existingLead = $this->getDoctrine()->getManager()->getRepository('MauticLeadBundle:Lead')->findOneBy(['email' => $data['email']]);
        
        //if we have a result then assign the lead object
        if (!empty($existingLead)) {
            $lead = $existingLead;
        }
        else
        { //otherwise create a new one
            $lead = new Lead();
            $lead->setNewlyCreated(true);
        }
        
        $leadModel->setFieldValues($lead, $data);
        $leadModel->saveEntity($lead);

Have you considered checking if contact with an email exists, that way you should be able to avoid any merging of contacts?

// stuff before...
$leads = $leadModel->getRepository()->getEntities([
    'filter' => [
        'where' => [
            [
                'column' => 'l.email',
                'expr' => 'eq',
                'val'  => $postEmail
            ]
        ],
    ],
    'ignore_paginator' => true
]);

$lead = new Lead();
if (count($leads)) {
    $lead = $leads[array_keys($leads)[0]];
}

$lead->addUpdatedField('email', $postEmail, null);
// set other fields...

$leadModel->getRepository()->saveEntity($lead);
// stuff after...

I did not test the code, but I hope this helps.

Regards, M.

Thanks for that code snippet!

I haven’t run through it in my code base yet, but its the same approach as I took with my other comment in this thread. My code is just a little different.

Definitely going to play around more with this.