Spaces:
No application file
No application file
| namespace MauticPlugin\MauticCrmBundle\Api; | |
| use Mautic\LeadBundle\Entity\Lead; | |
| use Mautic\PluginBundle\Exception\ApiErrorException; | |
| use Psr\Http\Message\ResponseInterface; | |
| class DynamicsApi extends CrmApi | |
| { | |
| private function getUrl(): string | |
| { | |
| $keys = $this->integration->getKeys(); | |
| return $keys['resource'].'/api/data/v8.2'; | |
| } | |
| /** | |
| * @param string $method | |
| * @param string $moduleobject | |
| * | |
| * @return array|ResponseInterface | |
| * | |
| * @throws ApiErrorException | |
| */ | |
| protected function request($operation, array $parameters = [], $method = 'GET', $moduleobject = 'contacts', $settings = []) | |
| { | |
| if ('company' === $moduleobject) { | |
| $moduleobject = 'accounts'; | |
| } | |
| if ('' === $operation) { | |
| $operation = $moduleobject; | |
| } | |
| $url = sprintf('%s/%s', $this->getUrl(), $operation); | |
| if (isset($parameters['request_settings'])) { | |
| $settings = array_merge($settings, $parameters['request_settings']); | |
| unset($parameters['request_settings']); | |
| } | |
| $settings = array_merge($settings, [ | |
| 'encode_parameters' => 'json', | |
| 'return_raw' => 'true', // needed to get the HTTP status code in the response | |
| 'request_timeout' => 300, | |
| ]); | |
| /** @var ResponseInterface $response */ | |
| $response = $this->integration->makeRequest($url, $parameters, $method, $settings); | |
| if ('POST' === $method && (!($response instanceof ResponseInterface) || !in_array($response->getStatusCode(), [200, 204], true))) { | |
| throw new ApiErrorException('Dynamics CRM API error: '.json_encode($response->getBody())); | |
| } | |
| if ('GET' === $method && $response instanceof ResponseInterface) { | |
| return json_decode($response->getBody(), true); | |
| } | |
| return $response; | |
| } | |
| /** | |
| * List types. | |
| * | |
| * @param string $object Zoho module name | |
| * | |
| * @return mixed | |
| */ | |
| public function getLeadFields($object = 'contacts') | |
| { | |
| if ('company' === $object) { | |
| $object = 'accounts'; // Dynamics object name | |
| } | |
| $logicalName = rtrim($object, 's'); // singularize object name | |
| $operation = sprintf('EntityDefinitions(LogicalName=\'%s\')/Attributes', $logicalName); | |
| $parameters = [ | |
| '$filter' => 'Microsoft.Dynamics.CRM.NotIn(PropertyName=\'AttributeTypeName\',PropertyValues=["Virtual", "Uniqueidentifier", "Picklist", "Lookup", "Owner", "Customer"]) and IsValidForUpdate eq true and AttributeOf eq null and LogicalName ne \'parentcustomerid\'', // ignore system fields | |
| '$select' => 'RequiredLevel,LogicalName,DisplayName,AttributeTypeName', // select only miningful columns | |
| ]; | |
| return $this->request($operation, $parameters, 'GET', $object); | |
| } | |
| /** | |
| * @param Lead $lead | |
| */ | |
| public function createLead($data, $lead, $object = 'contacts'): ResponseInterface | |
| { | |
| return $this->request('', $data, 'POST', $object); | |
| } | |
| public function updateLead($data, $objectId): ResponseInterface | |
| { | |
| // $settings['headers']['If-Match'] = '*'; // prevent create new contact | |
| return $this->request(sprintf('contacts(%s)', $objectId), $data, 'PATCH', 'contacts', []); | |
| } | |
| /** | |
| * gets leads. | |
| * | |
| * @return mixed | |
| */ | |
| public function getLeads(array $params) | |
| { | |
| return $this->request('', $params, 'GET', 'contacts'); | |
| } | |
| /** | |
| * gets companies. | |
| * | |
| * @param string $id | |
| * | |
| * @return mixed | |
| */ | |
| public function getCompanies(array $params, $id = null) | |
| { | |
| if ($id) { | |
| $operation = sprintf('accounts(%s)', $id); | |
| $data = $this->request($operation, $params, 'GET'); | |
| } else { | |
| $data = $this->request('', $params, 'GET', 'accounts'); | |
| } | |
| return $data; | |
| } | |
| /** | |
| * Batch create leads. | |
| * | |
| * @param array $data | |
| * @param string $object | |
| * @param bool $isUpdate | |
| */ | |
| public function createLeads($data, $object = 'contacts', $isUpdate = false): array | |
| { | |
| if (0 === count($data)) { | |
| return []; | |
| } | |
| $returnIds = []; | |
| $batchId = substr(str_shuffle(uniqid('b', false)), 0, 6); | |
| $changeId = substr(str_shuffle(uniqid('c', false)), 0, 6); | |
| $settings['headers']['Content-Type'] = 'multipart/mixed;boundary=batch_'.$batchId; | |
| $settings['headers']['Accept'] = 'application/json'; | |
| $odata = '--batch_'.$batchId.PHP_EOL; | |
| $odata .= 'Content-Type: multipart/mixed;boundary=changeset_'.$changeId.PHP_EOL.PHP_EOL; | |
| $contentId = 0; | |
| foreach ($data as $objectId => $lead) { | |
| ++$contentId; | |
| $odata .= '--changeset_'.$changeId.PHP_EOL; | |
| $odata .= 'Content-Type: application/http'.PHP_EOL; | |
| $odata .= 'Content-Transfer-Encoding:binary'.PHP_EOL; | |
| $odata .= 'Content-ID: '.$objectId.PHP_EOL.PHP_EOL; | |
| // $odata .= 'Content-ID: '.(++$contentId).PHP_EOL.PHP_EOL; | |
| $returnIds[$objectId] = $contentId; | |
| if (!$isUpdate) { | |
| $oid = $objectId; | |
| $objectId = sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535)); | |
| $returnIds[$objectId] = $oid; // save lead Id | |
| } | |
| $operation = sprintf('%s(%s)', $object, $objectId); | |
| $odata .= sprintf('PATCH %s/%s HTTP/1.1', $this->getUrl(), $operation).PHP_EOL; | |
| if ($isUpdate) { | |
| $odata .= 'If-Match: *'.PHP_EOL; | |
| } else { | |
| $odata .= 'If-None-Match: *'.PHP_EOL; | |
| } | |
| $odata .= 'Content-Type: application/json;type=entry'.PHP_EOL.PHP_EOL; | |
| $odata .= json_encode($lead).PHP_EOL; | |
| } | |
| $odata .= '--changeset_'.$changeId.'--'.PHP_EOL.PHP_EOL; | |
| $odata .= '--batch_'.$batchId.'--'.PHP_EOL; | |
| $settings['post_data'] = $odata; | |
| $settings['curl_options'][CURLOPT_CRLF] = true; | |
| $response = $this->request('$batch', [], 'POST', $object, $settings); | |
| if ($isUpdate) { | |
| return $returnIds; | |
| } | |
| return $this->parseRawHttpResponse($response); | |
| } | |
| /** | |
| * @param array $data | |
| */ | |
| public function updateLeads($data, $object = 'contacts'): array | |
| { | |
| return $this->createLeads($data, $object, true); | |
| } | |
| /** | |
| * @see https://stackoverflow.com/questions/5483851/manually-parse-raw-http-data-with-php | |
| */ | |
| public function parseRawHttpResponse(ResponseInterface $response): array | |
| { | |
| $a_data = []; | |
| $input = $response->getBody(); | |
| $contentType = $response->getHeaders()['Content-Type']; | |
| // grab multipart boundary from content type header | |
| preg_match('/boundary=(.*)$/', $contentType, $matches); | |
| $boundary = $matches[1]; | |
| // split content by boundary and get rid of last -- element | |
| $a_blocks = preg_split("/-+$boundary/", $input); | |
| array_pop($a_blocks); | |
| // there is only one batchresponse | |
| $input = array_pop($a_blocks); | |
| [$header, $input] = explode("\r\n\r\n", $input, 2); | |
| foreach (explode("\r\n", $header) as $r) { | |
| if (0 === stripos($r, 'Content-Type:')) { | |
| [$headername, $contentType] = explode(':', $r, 2); | |
| } | |
| } | |
| // grab multipart boundary from content type header | |
| preg_match('/boundary=(.*)$/', $contentType, $matches); | |
| $boundary = $matches[1]; | |
| // split content by boundary and get rid of last -- element | |
| $a_blocks = preg_split("/-+$boundary/", $input); | |
| array_pop($a_blocks); | |
| // loop data blocks | |
| foreach ($a_blocks as $block) { | |
| if (empty($block)) { | |
| continue; | |
| } | |
| if (false !== stripos($block, 'OData-EntityId:')) { | |
| preg_match('/Content-ID: (\d+)/', $block, $matches); | |
| $leadId = (count($matches) > 1) ? $matches[1] : 0; | |
| // OData-EntityId: https://virlatinus.crm.dynamics.com/api/data/v8.2/contacts(2725f27c-2058-e711-8111-c4346bac1938) | |
| preg_match('/OData-EntityId: .*\(([^\)]*)\)/', $block, $matches); | |
| $oid = (count($matches) > 1) ? $matches[1] : '00000000-0000-0000-0000-000000000000'; | |
| $a_data[$oid] = $leadId; | |
| } | |
| } | |
| return $a_data; | |
| } | |
| } | |