<?php
namespace App\EventSubscriber;
use App\Service\Prospect\ProspectHttpService;
use App\Service\TenantPerson\TenantPersonService;
use App\Util\Arr;
use Psr\Cache\CacheException;
use Psr\Cache\InvalidArgumentException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class ProspectFieldsResponseSubscriber implements EventSubscriberInterface
{
private const FIELD_TENANT = 'prospectTenantId';
private const FIELD_TENANT_PERSON = 'prospectTenantPersonId';
private const FIELD_CONTACT = 'prospectContactId';
private const FIELD_ORDER = 'prospectOrderId';
private const FIELD_WORK_SCHEDULE = 'prospectWorkScheduleId';
private const FIELDS_REPLACE_MAPPING
= [
self::FIELD_TENANT => 'prospectTenant',
self::FIELD_TENANT_PERSON => 'prospectTenantPerson',
self::FIELD_CONTACT => 'prospectContact',
self::FIELD_ORDER => 'prospectOrder',
self::FIELD_WORK_SCHEDULE => 'prospectWorkSchedule',
];
private $tenantPersonService;
private $prospectHttp;
public function __construct(TenantPersonService $tenantPersonService, ProspectHttpService $prospectHttp)
{
$this->tenantPersonService = $tenantPersonService;
$this->prospectHttp = $prospectHttp;
}
/**
* {@inheritDoc}
*/
public static function getSubscribedEvents(): array
{
return [
// The second value of the array defines priority of the subscriber.
KernelEvents::RESPONSE => ['onKernelResponse', 100],
];
}
/**
* @throws InvalidArgumentException
* @throws CacheException
*/
public function onKernelResponse(ResponseEvent $event)
{
if (!$this->tenantPersonService->isInitialized()) {
return;
}
$response = $event->getResponse();
if (!$response->isSuccessful()) {
return;
}
$contentType = $response->headers->get('Content-Type');
if (false === strpos($contentType, 'json')) {
return;
}
$content = json_decode($response->getContent(), true);
if (\JSON_ERROR_NONE !== json_last_error()) {
return;
}
if (null === $content) {
return;
}
if ('' === $content) {
return;
}
$prospectColumns = Arr::array_column_recursive($content, array_keys(self::FIELDS_REPLACE_MAPPING));
$prospectColumns = $this->fetchProspectColumns($prospectColumns);
$content = $this->findAndReplace($content, $prospectColumns);
$jsonResponse = new JsonResponse($content, $response->getStatusCode(), $response->headers->all());
$event->setResponse($jsonResponse);
}
/**
* @throws InvalidArgumentException
* @throws \Exception
*/
private function fetchProspectColumns(array $prospectColumns): array
{
$return = array_fill_keys(array_keys($prospectColumns), []);
$authProspectTenantId = $this->tenantPersonService->getTenant()->getProspectTenantId();
if (array_key_exists(self::FIELD_TENANT, $prospectColumns)) {
$key = self::FIELD_TENANT;
// Filtering an array of ids to keep only numeric values. Prevents unnecessary requests with invalid values.
$prospectColumns[$key] = array_filter($prospectColumns[$key], 'is_numeric');
if (count($prospectColumns[$key]) > 0) {
// Only one tenant can be fetched at once.
$prospectTenant = $this->prospectHttp->fetchTenant($prospectColumns[$key][0]);
$return[$key][$prospectColumns[$key][0]] = $prospectTenant;
}
}
if (array_key_exists(self::FIELD_TENANT_PERSON, $prospectColumns)) {
$key = self::FIELD_TENANT_PERSON;
$prospectColumns[$key] = array_filter($prospectColumns[$key], 'is_numeric');
if (count($prospectColumns[$key]) > 0) {
$prospectTenantPeople = $this->prospectHttp->fetchTenantPeople($authProspectTenantId, $prospectColumns[$key]);
// Replacing array indexes with corresponding ids
$return[$key] = array_combine(array_column($prospectTenantPeople, 'id'), $prospectTenantPeople);
}
}
if (array_key_exists(self::FIELD_CONTACT, $prospectColumns)) {
$key = self::FIELD_CONTACT;
$prospectColumns[$key] = array_filter($prospectColumns[$key], 'is_numeric');
if (count($prospectColumns[$key]) > 0) {
$prospectContacts = $this->prospectHttp->fetchContacts($authProspectTenantId, $prospectColumns[$key]);
$return[$key] = array_combine(array_column($prospectContacts, 'id'), $prospectContacts);
}
}
if (array_key_exists(self::FIELD_ORDER, $prospectColumns)) {
$key = self::FIELD_ORDER;
$prospectColumns[$key] = array_filter($prospectColumns[$key], 'is_numeric');
if (count($prospectColumns[$key]) > 0) {
$prospectOrders = $this->prospectHttp->fetchOrders($authProspectTenantId, $prospectColumns[$key]);
$return[$key] = array_combine(array_column($prospectOrders, 'id'), $prospectOrders);
}
}
if (array_key_exists(self::FIELD_WORK_SCHEDULE, $prospectColumns)) {
$key = self::FIELD_WORK_SCHEDULE;
$prospectColumns[$key] = array_filter($prospectColumns[$key], 'is_numeric');
if (count($prospectColumns[$key]) > 0) {
$prospectWorkSchedules = $this->prospectHttp->fetchWorkSchedules($authProspectTenantId, $prospectColumns[$key]);
$return[$key] = array_combine(array_column($prospectWorkSchedules, 'id'), $prospectWorkSchedules);
}
}
return $return;
}
/**
* Finds Prospect fields and replaces them with a new key and value from Prospect API.
*
* For example:
* ['prospectContactId' => 1] will be replaced with ['prospectContact' => ['id' => 1, 'name' => 'Foo', ...]]
*
* @param array $content An input array
* @param array $replacements Fetched Prospect data.
* Format: ['prospectContactId' => [1 => ['id' => 1, 'name' => 'Contact', ...], ...], ...]
*
* @throws InvalidArgumentException
*/
private function findAndReplace(array $content, array $replacements): array
{
$return = [];
foreach ($content as $key => $value) {
if (is_array($value)) {
$return[$key] = $this->findAndReplace($value, $replacements);
continue;
}
if (in_array($key, array_keys(self::FIELDS_REPLACE_MAPPING), true)) {
if (null !== $value) {
// Change data with corresponding replacement. If the replacement not found, keep the original value.
$value = $replacements[$key][$value] ?? $value;
}
$key = self::FIELDS_REPLACE_MAPPING[$key];
}
$return[$key] = $value;
}
return $return;
}
}