Spaces:
No application file
No application file
namespace Mautic\LeadBundle\Model; | |
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; | |
use Doctrine\DBAL\Exception\UniqueConstraintViolationException; | |
use Doctrine\ORM\EntityManager; | |
use Mautic\CoreBundle\Entity\IpAddress; | |
use Mautic\CoreBundle\Entity\IpAddressRepository; | |
use Mautic\LeadBundle\Entity\Lead; | |
use Psr\Log\LoggerInterface; | |
class IpAddressModel | |
{ | |
private const DELETE_SIZE = 10000; | |
public function __construct( | |
protected EntityManager $entityManager, | |
protected LoggerInterface $logger | |
) { | |
} | |
/** | |
* Saving IP Address references sometimes throws UniqueConstraintViolationException exception on Lead entity save. | |
* Rather pre-save the IP references here and catch the exception. | |
*/ | |
public function saveIpAddressesReferencesForContact(Lead $contact): void | |
{ | |
foreach ($contact->getIpAddresses() as $ipAddress) { | |
$this->insertIpAddressReference($contact, $ipAddress); | |
} | |
} | |
/** | |
* @param string $ip | |
* | |
* @return IpAddress|null | |
*/ | |
public function findOneByIpAddress($ip) | |
{ | |
return $this->entityManager->getRepository(IpAddress::class)->findOneByIpAddress($ip); | |
} | |
/** | |
* Tries to insert the Lead/IP relation and continues even if UniqueConstraintViolationException is thrown. | |
*/ | |
private function insertIpAddressReference(Lead $contact, IpAddress $ipAddress): void | |
{ | |
$ipAddressAdded = isset($contact->getChanges()['ipAddressList'][$ipAddress->getIpAddress()]); | |
if (!$ipAddressAdded || !$ipAddress->getId() || !$contact->getId()) { | |
return; | |
} | |
$qb = $this->entityManager->getConnection()->createQueryBuilder(); | |
$values = [ | |
'lead_id' => ':leadId', | |
'ip_id' => ':ipId', | |
]; | |
$qb->insert(MAUTIC_TABLE_PREFIX.'lead_ips_xref'); | |
$qb->values($values); | |
$qb->setParameter('leadId', $contact->getId()); | |
$qb->setParameter('ipId', $ipAddress->getId()); | |
try { | |
$qb->executeStatement(); | |
} catch (UniqueConstraintViolationException) { | |
$this->logger->warning("The reference for contact {$contact->getId()} and IP address {$ipAddress->getId()} is already there. (Unique constraint)"); | |
} catch (ForeignKeyConstraintViolationException) { | |
$this->logger->warning("The reference for contact {$contact->getId()} and IP address {$ipAddress->getId()} is already there. (Foreign key constraint)"); | |
} | |
$this->entityManager->detach($ipAddress); | |
} | |
/** | |
* @throws \Doctrine\DBAL\Exception | |
*/ | |
public function deleteUnusedIpAddresses(int $limit): int | |
{ | |
/** @var IpAddressRepository $ipAddressRepo */ | |
$ipAddressRepo = $this->entityManager->getRepository(IpAddress::class); | |
$ipIds = $ipAddressRepo->getUnusedIpAddressesIds($limit); | |
$chunkedIds = array_chunk($ipIds, self::DELETE_SIZE); | |
$count = 0; | |
foreach ($chunkedIds as $ids) { | |
$count += $ipAddressRepo->deleteUnusedIpAddresses($ids); | |
// Use sleep to recover from any potential table locks. | |
usleep(50000); | |
} | |
return $count; | |
} | |
} | |