Advertisement
Guest User

QueueAddContactsTask

a guest
Jul 19th, 2021
57
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <?php
  2. declare(strict_types=1);
  3.  
  4. namespace App\Shell\Task;
  5.  
  6. use App\Command\Helper\DataMapHelper;
  7. use App\Command\Helper\DataMapHelper\DataMapInterface;
  8. use App\Model\Entity\Contact;
  9. use App\Model\Entity\User;
  10. use Cake\Collection\Collection;
  11. use Cake\Console\ConsoleIo;
  12. use Cake\Core\Exception\Exception;
  13. use Cake\Log\Log;
  14. use Cake\ORM\Locator\LocatorAwareTrait;
  15. use Cake\ORM\Locator\LocatorInterface;
  16. use InvalidArgumentException;
  17. use Queue\Model\QueueException;
  18. use Queue\Shell\Task\AddInterface;
  19. use Queue\Shell\Task\QueueTask;
  20. use SplFileObject;
  21. use Throwable;
  22.  
  23. /**
  24.  * Task to make sure that contacts can be added to the database without timing out the server.
  25.  *
  26.  * Especially useful for imports that have a large number of items.
  27.  * Must include a CSV file and user identifier.
  28.  *
  29.  * @package App\Shell\Task
  30.  * @property \App\Model\Table\UsersTable $Users Used to associate contacts with the current user.
  31.  * @property \App\Model\Table\ContactsTable $Contacts Used to process contact data.
  32.  * @property \App\Model\Table\CompaniesTable $Companies Used to associate company data with contacts.
  33.  * @property \App\Command\Helper\DataMapHelper $DataMap Used to create and work with data maps.
  34.  */
  35. class QueueAddContactsTask extends QueueTask implements addInterface
  36. {
  37.     use LocatorAwareTrait;
  38.  
  39.     /**
  40.      * Timeout in seconds, after which the Task is reassigned to a new worker
  41.      * if not finished successfully.
  42.      * This should be high enough that it cannot still be running on a zombie worker (>> 2x).
  43.      * Defaults to Config::defaultworkertimeout().
  44.      * Default maximum execution time on most servers is one minute.
  45.      *
  46.      * @var float|int
  47.      */
  48.     public $timeout = 60 * 5;
  49.     /**
  50.      * All company names to check for duplicates.
  51.      *
  52.      * @var array
  53.      */
  54.     public array $cNames;
  55.     /**
  56.      * Number of new contacts that have been added.
  57.      *
  58.      * @var int
  59.      */
  60.     public int $newCount;
  61.     /**
  62.      * Number of contacts that have been updated.
  63.      *
  64.      * @var int
  65.      */
  66.     public int $updateCount;
  67.     /**
  68.      * Number of contacts to be added or updated.
  69.      *
  70.      * @var int
  71.      */
  72.     public int $totalRecords;
  73.  
  74.     public array $contactEntities;
  75.  
  76.     public array $lists;
  77.  
  78.     public int $user_id;
  79.  
  80.     public string $filePath;
  81.  
  82.     public DataMapInterface $map;
  83.     public DataMapHelper $DataMap;
  84.  
  85.     /**
  86.      * @param \Cake\Console\ConsoleIo|null $io
  87.      * @param \Cake\ORM\Locator\LocatorInterface|null $locator
  88.      */
  89.     public function __construct(?ConsoleIo $io = null, ?LocatorInterface $locator = null)
  90.     {
  91.         parent::__construct($io, $locator);
  92.  
  93.         $this->Users = $this->loadModel('Users');
  94.         $this->Contacts = $this->loadModel('Contacts');
  95.         $this->Companies = $this->loadModel('Companies');
  96.         $this->cNames = [];
  97.         $this->contactEntities = [];
  98.         $this->lists = [];
  99.         $this->newCount = 0;
  100.         $this->updateCount = 0;
  101.         $this->totalRecords = 0;
  102.         $this->DataMap = $this->helper('DataMap');
  103.     }
  104.  
  105.     /**
  106.      * @param array $data Payload
  107.      * @param int $jobId The ID of the QueuedJob entity
  108.      * @return void
  109.      * @throws \Throwable
  110.      */
  111.     public function run(array $data, int $jobId): void
  112.     {
  113.         $this->newCount = 0;
  114.         $this->updateCount = 0;
  115.         $this->totalRecords = 0;
  116.         try {
  117.             // Initializing
  118.             $jobsTable = $this->getTableLocator()->get('Queue.QueuedJobs');
  119.  
  120.             // Part One
  121.             $jobsTable->updateAll(
  122.                 ['status' => 'Checking Data'],
  123.                 ['id' => $jobId]
  124.             );
  125.             $this->out('Checking Data');
  126.  
  127.             $this->filePath = $data['filePath'];
  128.             if (!isset($data['filePath'])) {
  129.                 throw new QueueException('AddContacts Task called without file path.');
  130.             }
  131.  
  132.             if (!isset($data['identifier'])) {
  133.                 throw new QueueException('AddContacts Task called without user identifier.');
  134.             }
  135.  
  136.             $this->user_id = $data['identifier'];
  137.  
  138.             if (!isset($data['listIDs'])) {
  139.                 throw new QueueException('AddContacts Task called without associated contact lists.');
  140.             }
  141.  
  142.             try {
  143.                 $fileObj = new SplFileObject($data['filePath']);
  144.             } catch (Throwable $e) {
  145.                 $error = $e->getMessage();
  146.                 $error .= ' (line ' . $e->getLine() . ' in ' . $e->getFile() . ')' . PHP_EOL . $e->getTraceAsString();
  147.                 Log::write('error', $error);
  148.                 $this->QueuedJobs->updateProgress($jobId, 100, 'Error: Contacts not imported. Contact support if this issue persists.');
  149.  
  150.                 throw $e;
  151.             }
  152.  
  153.             if (is_object($fileObj) && $fileObj instanceof SplFileObject) {
  154.                 try {
  155.                     if (!$this->isCSV($fileObj)) {
  156.                         throw new QueueException('AddContacts Task called with non csv data.');
  157.                     }
  158.  
  159.                     if (!empty($data["listIDs"])) {
  160.                         $this->lists = $data['listIDs'];
  161.                     }
  162.                     $this->map = $this->getDataMap($data['mapData']);
  163.                     $importArray = $this->toArray($fileObj, $this->map, true);
  164.                     $importArray = $this->removeDuplicates($importArray);
  165.                     $this->totalRecords = count($importArray);
  166.                     if ($this->totalRecords === 0) {
  167.                         throw new QueueException('AddContacts Task with an empty csv file');
  168.                     }
  169.                     $jobsTable->updateProgress($jobId, 50); // Passed data verification.
  170.  
  171.                     // Part Two
  172.                     $message = __("Saving Data", $this->totalRecords);
  173.                     $jobsTable->updateAll(['status' => $message], ['id' => $jobId]);
  174.                     $this->out($message);
  175.                     $user = $this->Users->get($data['identifier']);
  176.  
  177.                     $existingContacts = $this->getExistingContacts($importArray, $this->user_id);
  178.                     foreach ($existingContacts as $key => $existingContact) {
  179.                         $existingContacts[$key] = $this->associateCompany($existingContact, $user);
  180.                     }
  181.                     if (!$this->saveAllExistingContacts($existingContacts, $this->user_id, $data)) {
  182.                         $this->err("Could not save existing contacts");
  183.                     }
  184.  
  185.                     $newContacts = $this->getNewContacts($importArray, $this->user_id);
  186.                     foreach ($newContacts as $key => $newContact) {
  187.                         $newContacts[$key] = $this->associateCompany($newContact, $user);
  188.                     }
  189.                     if (!$this->saveAllNewContacts($newContacts, $this->user_id, $data)) {
  190.                         $this->err("Could not save new contacts");
  191.                     }
  192.                     $message = "All contacts successfully imported!";
  193.                     $jobsTable->updateProgress($jobId, 100);
  194.                     $jobsTable->updateAll(['status' => $message], ['id' => $jobId]);
  195.                     $this->success($message);
  196.                 } catch (Throwable $e) {
  197.                     $error = $e->getMessage();
  198.                     $error .= ' (line ' . $e->getLine() . ' in ' . $e->getFile() . ')' . PHP_EOL . $e->getTraceAsString();
  199.                     Log::write('error', $error);
  200.                     $this->QueuedJobs->updateProgress($jobId, 100, 'Error: Contacts not imported. Contact support if this issue persists.');
  201.  
  202.                     throw $e;
  203.                 }
  204.             } else {
  205.                 $this->QueuedJobs->updateProgress($jobId, 100, 'Error: Contacts not imported. Please use a valid CSV.');
  206.                 throw new QueueException('AddContacts Task called with invalid file data.');
  207.             }
  208.         } catch (throwable $e) {
  209.             $this->err('Could not finish import. Error thrown in ' . $e->getFile() . ' on line ' . $e->getLine() . '.');
  210.             throw $e;
  211.         }
  212.     }
  213.  
  214.     /**
  215.      * Verify if file is a valid CSV.
  216.      *
  217.      * @param \SplFileObject $fileObj File to be checked.
  218.      * @return bool Result of check as a true or false value.
  219.      */
  220.     protected function isCSV(SplFileObject $fileObj): bool
  221.     {
  222.         return $fileObj->isReadable();
  223.     }
  224.  
  225.     /**
  226.      * Get Data Map Object.
  227.      * @param string $data
  228.      * @return \App\Command\Helper\DataMapHelper\DataMapInterface
  229.      */
  230.     public function getDataMap(string $data): DataMapInterface
  231.     {
  232.         return $this->DataMap->getDataMap($data);
  233.     }
  234.  
  235.     /**
  236.      * Convert CSV to an array
  237.      *
  238.      * @param \SplFileObject $fileObj
  239.      * @param \App\Command\Helper\DataMapHelper\DataMapInterface $map
  240.      * @param bool $hasHeaders
  241.      * @return array|null
  242.      */
  243.     public function toArray(SplFileObject $fileObj, DataMapInterface $map, bool $hasHeaders = false): ?array
  244.     {
  245.         $fileObj->setFlags(SplFileObject::READ_CSV);
  246.         $data = new Collection($fileObj);
  247.  
  248.         if ($hasHeaders) {
  249.             $headers = $this->getHeaders($fileObj);
  250.             $rows = $data->skip(1);
  251.             $array = [];
  252.             foreach ($rows->toArray() as $row) {
  253.                 if (count($row) == count($headers)) {
  254.                     $array[] = $row;
  255.                 }
  256.             }
  257.             $result = array_map(function ($x) use ($headers) {
  258.                 return array_combine($headers, $x);
  259.             }, $array);
  260.             $result = $this->mapContacts($result, $map);
  261.         } else {
  262.             $rows = $data->toArray();
  263.             foreach ($rows as $row) {
  264.                 if (count($row) > 1) {
  265.                     $result[] = $row;
  266.                 }
  267.             }
  268.         }
  269.  
  270.         return $result ?? null;
  271.     }
  272.  
  273.     /**
  274.      * Get all headers into the array and ready to be imported.
  275.      *
  276.      * @param \SplFileObject $fileObj
  277.      * @return array
  278.      */
  279.     protected function getHeaders(SplFileObject $fileObj): array
  280.     {
  281.         $fileObj->setFlags(SplFileObject::READ_CSV);
  282.         $data = new Collection($fileObj);
  283.         $raw = $data->first();
  284.         $result = [];
  285.         foreach ($raw as $item) {
  286.             $result[] = $item;
  287.         }
  288.  
  289.         return $result;
  290.     }
  291.  
  292.     public function mapContacts(array $rawArray, DataMapInterface $map)
  293.     {
  294.         $result = [];
  295.         foreach ($rawArray as $key => $contact) {
  296.             $this->replaceValue('first_name', $map->first_name(), $contact);
  297.             $this->replaceValue('last_name', $map->last_name(), $contact);
  298.             $this->replaceValue('job_title', $map->job_title(), $contact);
  299.             $this->replaceValue('phone', $map->phone(), $contact);
  300.             $this->replaceValue('can_text', $map->can_text(), $contact);
  301.             $this->replaceValue('email', $map->email(), $contact);
  302.             $this->replaceValue('street_address', $map->street_address(), $contact);
  303.             $this->replaceValue('city', $map->city(), $contact);
  304.             $this->replaceValue('state', $map->state(), $contact);
  305.             $this->replaceValue('postal_code', $map->postal_code(), $contact);
  306.             $this->replaceValue('country', $map->country(), $contact);
  307.             $this->replaceValue('notes', $map->notes(), $contact);
  308.             $this->replaceValue('Company.name', $map->company_name(), $contact);
  309.             $result[$key] = $contact;
  310.         }
  311.  
  312.         return $result;
  313.     }
  314.  
  315.     /**
  316.      * Replace current key value pair with a new one that has the same value but an altered key.
  317.      *
  318.      * @param string $newKey
  319.      * @param string $oldKey
  320.      * @param array $array
  321.      */
  322.     public function replaceValue(string $newKey, string $oldKey, array &$array): void
  323.     {
  324.         if (empty($array)) {
  325.             throw new Exception("Array is empty.");
  326.         }
  327.         if ($oldKey !== $newKey) {
  328.             if (isset($array[$oldKey])) {
  329.                 $array[$newKey] = $array[$oldKey];
  330.                 unset($array[$oldKey]);
  331.             } else {
  332.                 $array[$newKey] = null;
  333.             }
  334.         }
  335.     }
  336.  
  337.     /**
  338.      * Get all unique values in an array.
  339.      *
  340.      * @param array $assocArray Array to be cleaned.
  341.      * @return array Cleaned associative array.
  342.      */
  343.     public function removeDuplicates(array $assocArray): array
  344.     {
  345.         $map = array_map("serialize", $assocArray);
  346.         $scrubbed = array_unique($map);
  347.  
  348.         return array_map("unserialize", $scrubbed);
  349.     }
  350.  
  351.     /**
  352.      * Get all existing contacts from submission.
  353.      *
  354.      * @param array $submissions All submissions to be evaluated and sorted.
  355.      * @param int $user_id ID of the user who submitted the contact data.
  356.      * @return array All existing contacts in submissions without any duplications.
  357.      */
  358.     public function getExistingContacts(array $submissions, int $user_id): array
  359.     {
  360.         $results = [];
  361.         $submissions = $this->removeDuplicates($submissions);
  362.         foreach ($submissions as $submission) {
  363.             if ($this->submissionExists($submission, $user_id)) {
  364.                 $results[] = $submission;
  365.             }
  366.         }
  367.  
  368.         return $results;
  369.     }
  370.  
  371.     /**
  372.      * Check to see if contact is a duplicate of submitted list of contacts.
  373.      *
  374.      * @param array $submission Contact data to be evaluated
  375.      * @param int $user_id ID of the user who submitted the contact data.
  376.      * @return bool Result of the check, if true then the contact already exists in the database.
  377.      */
  378.     public function submissionExists(array $submission, int $user_id): bool
  379.     {
  380.         $contacts = $this->Contacts->find()->where(["user_id" => $user_id]);
  381.         if ($contacts->count() > 0) {
  382.             foreach ($contacts as $contact) {
  383.                 if (
  384.                     $submission['first_name'] === $contact->first_name &&
  385.                     $submission['last_name'] === $contact->last_name &&
  386.                     $submission['email'] === $contact->email
  387.                 ) {
  388.                     return true;
  389.                 }
  390.             }
  391.         }
  392.  
  393.         return false;
  394.     }
  395.  
  396.     /**
  397.      * Associate a user with the contact and generate updated version of the row.
  398.      *
  399.      * @param array $row Row data to be modified.
  400.      * @param \App\Model\Entity\User $user User with which to associate data.
  401.      * @return array Altered row.
  402.      */
  403.     public function associateCompany(array $row, User $user): array
  404.     {
  405.         $row['user_id'] = $user->id;
  406.         if (isset($row['Company.name'])) {
  407.             $company = $this->Companies->find()->where([
  408.                 'name' => $row['Company.name'],
  409.                 'user_id' => $user->id,
  410.             ]);
  411.             if (!$company->isEmpty()) {
  412.                 if (!in_array($row['Company.name'], $this->cNames)) {
  413.                     $companyEntityArray = [
  414.                         'user_id' => $user->id,
  415.                         'name' => $row['Company.name'],
  416.                     ];
  417.                     $row['company'] = $companyEntityArray;
  418.                     $this->cNames[] = $row['Company.name'];
  419.                 } else {
  420.                     $repeat = $company->first();
  421.                     $row['company_id'] = $repeat->id ?? '';
  422.                 }
  423.             } else {
  424.                 $company = $this->createNewCompany($row['Company.name'], $user->id);
  425.                 if ($company) {
  426.                     $row['company_id'] = $company->id;
  427.                 } else {
  428.                     throw new Exception("Could not create new company");
  429.                 }
  430.             }
  431.         }
  432.  
  433.         return $row;
  434.     }
  435.  
  436.     /**
  437.      * @param array $submissions
  438.      * @param int $user_id
  439.      * @param $data
  440.      * @return bool
  441.      * @throws \Exception'
  442.      */
  443.     public function saveAllExistingContacts(array $submissions, int $user_id, $data): bool
  444.     {
  445.         $existingContacts = $this->getExistingContacts($submissions, $user_id);
  446.         if ($existingContacts) {
  447.             $contacts = [];
  448.             foreach ($existingContacts as $contact) {
  449.                 $match = $this->findExisting($contact, $user_id);
  450.                 if ($match) {
  451.                     $new = $this->Contacts->patchEntity($match, $contact);
  452.                     if (!$new->can_text) {
  453.                         $new->can_text = false;
  454.                     }
  455.                     if (!$new->notes) {
  456.                         $new->notes = null;
  457.                     }
  458.                     $contacts = $this->addToContactArray($new, $contacts);
  459.                 }
  460.             }
  461.             if ($contacts) {
  462.                 if ($this->Contacts->saveManyOrFail($contacts, ['associated' => ['Companies']])) {
  463.                     foreach ($data['listIDs'] as $list) {
  464.                         $needLinked = [];
  465.                         /** @var \App\Model\Entity\Contact $contact */
  466.                         foreach ($contacts as $contact) {
  467.                             $contactIsInList = $this->isContactInList($contact->id, $list);
  468.                             if (!$contactIsInList) {
  469.                                 $needLinked[] = $contact;
  470.                             }
  471.                         }
  472.                         if ($needLinked) {
  473.                             foreach ($needLinked as $contact) {
  474.                                 try {
  475.                                     $this->Contacts->ContactLists->Contacts->link($this->Contacts->ContactLists->get($list), [$contact]);
  476.                                 } catch (throwable $e) {
  477.                                     $message = $e->getMessage() . PHP_EOL;
  478.                                     $message .= "Contact: " . $contact->first_name . " " . $contact->last_name . PHP_EOL;
  479.                                     $contact_list = $this->Contacts->ContactLists->get($list);
  480.                                     $message .= "Contact List: " . $contact_list->name;
  481.                                     $this->log($message);
  482.                                     $this->err($message);
  483.  
  484.                                     return false;
  485.                                 }
  486.                             }
  487.                         }
  488.                     }
  489.  
  490.                     return true;
  491.                 }
  492.  
  493.                 return false;
  494.             } else {
  495.                 return true;
  496.             }
  497.         } else {
  498.             return true;
  499.         }
  500.     }
  501.  
  502.     /**
  503.      * @param $new
  504.      * @param $contacts
  505.      * @return mixed
  506.      */
  507.     protected function addToContactArray($new, $contacts)
  508.     {
  509.         if (!$new->hasErrors()) {
  510.             $contacts[] = $new;
  511.         } else {
  512.             $message = "Cannot Save Contact" . PHP_EOL;
  513.             foreach ($new->getErrors() as $field => $errorType) {
  514.                 foreach ($errorType as $errorName => $errorMsg) {
  515.                     $message .= "${errorName} Error on ${field}: $errorMsg " . PHP_EOL;
  516.                 }
  517.             }
  518.             $this->err($message);
  519.             $this->log($message, 'error');
  520.         }
  521.  
  522.         return $contacts;
  523.     }
  524.  
  525.     /**
  526.      * Get all new contacts from submission.
  527.      *
  528.      * @param array $submissions All submissions to be evaluated and sorted.
  529.      * @param int $user_id ID of the user who submitted the contact data.
  530.      * @return array All new contacts without any duplication.
  531.      */
  532.     public function getNewContacts(array $submissions, int $user_id): array
  533.     {
  534.         $results = [];
  535.         $submissions = $this->removeDuplicates($submissions);
  536.         foreach ($submissions as $submission) {
  537.             if (!$this->submissionExists($submission, $user_id)) {
  538.                 $results[] = $submission;
  539.             }
  540.         }
  541.  
  542.         return $results;
  543.     }
  544.  
  545.     /**
  546.      * @param array $submissions
  547.      * @param int $user_id
  548.      * @param $data
  549.      * @return bool
  550.      * @throws \Exception
  551.      */
  552.     public function saveAllNewContacts(array $submissions, int $user_id, $data): bool
  553.     {
  554.         $newContacts = $this->getNewContacts($submissions, $user_id);
  555.         if ($newContacts) {
  556.             $contacts = [];
  557.             foreach ($newContacts as $contact) {
  558.                 $new = $this->Contacts->newEntity($contact);
  559.                 if (!$new->can_text) {
  560.                     $new->can_text = false;
  561.                 }
  562.                 if (!$new->notes) {
  563.                     $new->notes = null;
  564.                 }
  565.                 if ($new->phone) {
  566.                     $new->phone = $this->cleanPhone($new->phone);
  567.                 }
  568.                 $contacts = $this->addToContactArray($new, $contacts);
  569.             }
  570.             if ($contacts) {
  571.                 if ($this->Contacts->saveMany($contacts, ['associated' => ['Companies']])) {
  572.                     foreach ($data['listIDs'] as $listID) {
  573.                         foreach ($contacts as $contact) {
  574.                             try {
  575.                                 $this->Contacts->ContactLists->Contacts->link($this->Contacts->ContactLists->get($listID), [$contact]);
  576.                             } catch (throwable $e) {
  577.                                 $message = $e->getMessage() . "EOL";
  578.                                 $message .= "Contact: " . $contact->first_name . " " . $contact->last_name . PHP_EOL;
  579.                                 $contact_list = $this->Contacts->ContactLists->get($listID);
  580.                                 $message .= "Contact List: " . $contact_list->name;
  581.                                 $this->log($message);
  582.                                 $this->err($message);
  583.  
  584.                                 return false;
  585.                             }
  586.                         }
  587.                     }
  588.  
  589.                     return true;
  590.                 } else {
  591.                     return false;
  592.                 }
  593.             } else {
  594.                 return true;
  595.             }
  596.         } else {
  597.             return true;
  598.         }
  599.     }
  600.  
  601.     /**
  602.      * Add contact information to the contact entities array.
  603.      *
  604.      * @param array $row Contact information to be added.
  605.      * @param \App\Model\Entity\User $user User associated with the contact.
  606.      */
  607.     public function addToEntities(array $row, User $user): void
  608.     {
  609.         $contacts = $this->Contacts->find()->where([
  610.             'AND' => [
  611.                 'first_name' => trim($row['first_name']),
  612.                 'last_name' => trim($row['last_name']),
  613.                 'user_id' => $user->id,
  614.             ],
  615.         ]);
  616.         if ($contacts->count() === 0) {
  617.             $newContact = $this->Contacts->newEntity($row);
  618.             if (!$newContact->hasErrors()) {
  619.                 $this->contactEntities[] = $newContact;
  620.             }
  621.             $this->newCount++;
  622.         } else {
  623.             foreach ($contacts as $contact) {
  624.                 if ($user->id === $contact->user_id) {
  625.                     $contact = $this->Contacts->patchEntity($contact, $row);
  626.                     if (!$contact->hasErrors()) {
  627.                         $this->contactEntities[] = $contact;
  628.                         $this->updateCount++;
  629.                     }
  630.                 }
  631.             }
  632.         }
  633.     }
  634.  
  635.     /**
  636.      * @inheritDoc
  637.      */
  638.     public function add()
  639.     {
  640.         $this->QueuedJobs->createJob('AddContacts');
  641.         $this->success('OK, job created, now run the worker');
  642.     }
  643.  
  644.     /**
  645.      * Create new company.
  646.      *
  647.      * @param string $name Name of the company.
  648.      * @param string|int $user_id ID of the user with which the company needs to be associated.
  649.      * @return \App\Model\Entity\Company|bool|\App\Shell\Task\EntityInterface
  650.      */
  651.     public function createNewCompany(string $name, $user_id)
  652.     {
  653.         $data = [
  654.             'user_id' => $user_id,
  655.             'name' => $name,
  656.         ];
  657.         $company = $this->Contacts->Companies->newEntity($data);
  658.         if ($this->Companies->save($company)) {
  659.             return $company;
  660.         }
  661.  
  662.         return false;
  663.     }
  664.  
  665.     /**
  666.      * Gets a header into a lower case underscored form.
  667.      *
  668.      * @param string $header Header to be processed.
  669.      * @return string Formatted version of header.
  670.      */
  671.     protected function formatHeader(string $header): string
  672.     {
  673.         return strtolower(str_replace(' ', '_', $header));
  674.     }
  675.  
  676.     /**
  677.      * @param array $data
  678.      * @param string|int $user_id
  679.      * @return \App\Model\Entity\Contact|bool
  680.      */
  681.     public function findExisting(array $data, $user_id)
  682.     {
  683.         if (isset($data['first_name']) && isset($data['last_name'])) {
  684.             $results = $this->Contacts->find()->where(
  685.                 [
  686.                     'first_name' => $data['first_name'],
  687.                     'last_name' => $data['last_name'],
  688.                     'user_id' => $user_id,
  689.                 ]
  690.             );
  691.             /** @var \App\Model\Entity\Contact $contact */
  692.             foreach ($results as $contact) {
  693.                 if ($this->isFullMatch($contact, $data)) {
  694.                     return $contact;
  695.                 }
  696.             }
  697.  
  698.             return false;
  699.         } else {
  700.             throw new InvalidArgumentException('First and last names must be defined.');
  701.         }
  702.     }
  703.  
  704.     /**
  705.      * Check if the data is a full match.
  706.      * @param \App\Model\Entity\Contact $contact
  707.      * @param array $data
  708.      * @return bool
  709.      */
  710.     public function isFullMatch(Contact $contact, array $data): bool
  711.     {
  712.         foreach ($data as $key => $value) {
  713.             if ($contact->$key !== $value) {
  714.                 return false;
  715.             }
  716.         }
  717.  
  718.         return true;
  719.     }
  720.  
  721.     /**
  722.      * Check if contact already exists in the contact list.
  723.      *
  724.      * @param string|int $contact_id
  725.      * @param string|int $contact_list_id
  726.      * @return bool
  727.      */
  728.     public function isContactInList($contact_id, $contact_list_id): bool
  729.     {
  730.         $reference = $this->Contacts->ContactToLists->find()->where(
  731.             [
  732.                 'contact_id' => $contact_id,
  733.                 'contact_list_id' => $contact_list_id,
  734.             ]
  735.         );
  736.         if ($reference->isEmpty()) {
  737.             return false;
  738.         } else {
  739.             return true;
  740.         }
  741.     }
  742.  
  743.     /**
  744.      * Clean out the phone number.
  745.      * Remove all symbols.
  746.      * Clear out any spaces between numbers.
  747.      * Remove preceding number 1.
  748.      *
  749.      * @param string $phone Phone number to be cleaned
  750.      * @return string Cleaned phone number.
  751.      */
  752.     public function cleanPhone(string $phone): string
  753.     {
  754.         // Expression Configuration
  755.         $pattern = "/[^0-9]+/";
  756.         $replacement = "";
  757.         $charList = "1";
  758.  
  759.         // Expression Execution
  760.         return ltrim(preg_replace($pattern, $replacement, $phone), $charList);
  761.     }
  762.  
  763.     /**
  764.      * Verify phone number is valid.
  765.      * @param string $phone
  766.      * @return bool
  767.      */
  768.     public function validatePhone(string $phone): bool
  769.     {
  770.         $numbersOnly = $this->cleanPhone($phone);
  771.         $digitCount = strlen($numbersOnly);
  772.         if ($phone !== $numbersOnly) {
  773.             return false;
  774.         }
  775.         if ($digitCount !== 7 || $digitCount !== 10) {
  776.             return false;
  777.         }
  778.  
  779.         return true;
  780.     }
  781.  
  782.     public function verifyPhone(string $phone): string
  783.     {
  784.     }
  785. }
  786.  
Advertisement
Advertisement
Advertisement
RAW Paste Data Copied
Advertisement