custom/static-plugins/AbasConnector/src/Service/Component/Cache/AbasComponentCache.php line 87

Open in your IDE?
  1. <?php
  2. namespace Abas\AbasConnector\Service\Component\Cache;
  3. use Abas\AbasConnector\Service\Component\AbasComponent;
  4. use Abas\AbasConnector\Service\Log\AbasLogger;
  5. use Abas\AbasConnector\Service\RestApi\Client\AbasRestApiClient;
  6. use Abas\AbasConnector\Service\RestApi\Meta\MetaData;
  7. use Abas\AbasConnector\Service\RestApi\Meta\MetaDataField;
  8. use Abas\AbasConnector\Service\RestApi\Meta\MetaDataService;
  9. use Abas\AbasConnector\Service\RestApi\Request\AbasPostRequest;
  10. use Abas\AbasConnector\Service\RestApi\Request\Factories\DataRequestFactory;
  11. use Abas\AbasConnector\Service\RestApi\Response\AbasResponse;
  12. use Abas\AbasConnector\Service\RestApi\Response\Content;
  13. use Abas\AbasConnector\Service\Utils\Cache\CacheFactory;
  14. use Abas\AbasConnector\Service\Utils\Cache\CacheKeyGenerator;
  15. use Abas\AbasConnector\Service\Utils\Config\PluginConfigReader;
  16. use Abas\AbasConnector\Service\Utils\Field\FieldUtil;
  17. use Abas\AbasConnector\Service\Core\Exceptions\InvalidArgumentException;
  18. use Abas\AbasConnector\Service\Core\Enums\Duration;
  19. use Abas\AbasConnector\Service\RestApi\Response\Content\ErpDataObject;
  20. use Abas\AbasConnector\Service\Utils\Benchmark\Benchmark;
  21. use Symfony\Component\Cache\Adapter\AbstractAdapter;
  22. use Symfony\Component\Cache\Adapter\FilesystemAdapter;
  23. use Symfony\Component\Cache\Marshaller\MarshallerInterface;
  24. use Symfony\Contracts\Cache\ItemInterface;
  25. use function microtime;
  26. use function str_replace;
  27. class AbasComponentCache
  28. {
  29.     /**
  30.      * @var string
  31.      */
  32.     public const WORKSPACES_KEY 'WORKSPACES';
  33.     /**
  34.      * @var AbstractAdapter
  35.      */
  36.     private $componentAdapter;
  37.     /**
  38.      * @var AbstractAdapter
  39.      */
  40.     private $workspaceAdapter;
  41.     /**
  42.      * @var AbasRestApiClient
  43.      */
  44.     private $apiClient;
  45.     /**
  46.      * @var int
  47.      */
  48.     private $defaultLifetime;
  49.     /**
  50.      * @var AbasLogger|null
  51.      */
  52.     private $logger;
  53.     /**
  54.      * @var AbstractAdapter[]
  55.      */
  56.     private $caches = [];
  57.     /**
  58.      * @var MetaDataService
  59.      */
  60.     private $metaDataService;
  61.     /**
  62.      * @var PluginConfigReader
  63.      */
  64.     private $pluginConfigReader;
  65.     /**
  66.      * @param AbasRestApiClient $apiClient
  67.      * @param MetaDataService $metaDataService
  68.      * @param PluginConfigReader $pluginConfigReader
  69.      * @param int $defaultLifetime
  70.      * @param string $directory
  71.      * @param MarshallerInterface $marshaller
  72.      * @param AbasLogger $logger
  73.      */
  74.     public function __construct(
  75.         AbasRestApiClient $apiClient,
  76.         MetaDataService $metaDataService,
  77.         PluginConfigReader $pluginConfigReader,
  78.         int $defaultLifetime 3600,
  79.         string $directory null,
  80.         MarshallerInterface $marshaller,
  81.         AbasLogger $logger null
  82.     ) {
  83.         $this->componentAdapter = new FilesystemAdapter(
  84.             'AbasComponentCache',
  85.             $defaultLifetime,
  86.             $directory,
  87.             $marshaller
  88.         );
  89.         $this->workspaceAdapter = (new CacheFactory('AbasWorkspaceCache'$pluginConfigReader))->create();
  90.         $this->apiClient $apiClient;
  91.         $this->defaultLifetime $defaultLifetime;
  92.         $this->logger $logger;
  93.         $this->metaDataService $metaDataService;
  94.         $this->pluginConfigReader $pluginConfigReader;
  95.     }
  96.     /**
  97.      * Save a component in the cache and return the bid.
  98.      *
  99.      * @param AbasComponent $component
  100.      *
  101.      * @return string bid
  102.      * @throws InvalidArgumentException
  103.      */
  104.     public function saveComponent(AbasComponent $component): string
  105.     {
  106.         $bid $component->getBid();
  107.         $this->saveWorkspaceIdentifier($component$bid);
  108.         $item $this->componentAdapter->getItem($bid);
  109.         $item->set($component);
  110.         $item->expiresAfter($this->defaultLifetime);
  111.         $this->componentAdapter->save($item);
  112.         return $bid;
  113.     }
  114.     /**
  115.      * Get a component by the bid.
  116.      *
  117.      * @param string $bid
  118.      *
  119.      * @return AbasComponent|null
  120.      * @throws InvalidArgumentException
  121.      */
  122.     public function getComponent(string $bid): ?AbasComponent
  123.     {
  124.         $item $this->componentAdapter->getItem($bid);
  125.         if ($item->isHit()) {
  126.             return $item->get();
  127.         }
  128.         $this->closeWorkspaceForBid($bid);
  129.         return null;
  130.     }
  131.     /**
  132.      * @param string $databaseAndGroup
  133.      * @param string $criteria
  134.      * @param string|null $headFields
  135.      * @param string|null $tableFields
  136.      * @param string $identifierField
  137.      * @param int $packageSize
  138.      *
  139.      * @return void
  140.      */
  141.     public function loadViewCache(
  142.         string $databaseAndGroup,
  143.         string $criteria '',
  144.         ?string $headFields null,
  145.         ?string $tableFields null,
  146.         string $identifierField 'id',
  147.         int $packageSize 200
  148.     ): void {
  149.         $startTimeTotal microtime(true);
  150.         $this->logger->info('AbasComponentCache: load view from cache.');
  151.         $cache $this->getCache($databaseAndGroup);
  152.         $cache->clear();
  153.         if ($headFields === null || $tableFields === null) {
  154.             $metaData $this->metaDataService->getMetaData($databaseAndGroup);
  155.             $headFields $this->getFields($headFields$metaDatafalse);
  156.             $tableFields $this->getFields($tableFields$metaDatatrue);
  157.         }
  158.         $resultSize = -1;
  159.         $totalSize 0;
  160.         $offset 0;
  161.         while ($resultSize !== 0) {
  162.             $startTime microtime(true);
  163.             $request DataRequestFactory::createPostRequest(
  164.                 $databaseAndGroup,
  165.                 $criteria,
  166.                 $headFields,
  167.                 $tableFields,
  168.                 $packageSize,
  169.                 $offset
  170.             );
  171.             $abasResponse $this->apiClient->executeRequest($requesttrue);
  172.             $erpDataObjects $abasResponse->getObjects();
  173.             if ($erpDataObjects === null) {
  174.                 break;
  175.             }
  176.             $resultSize $erpDataObjects->count();
  177.             /** @var ErpDataObject $erpDataObject */
  178.             foreach ($erpDataObjects as $erpDataObject) {
  179.                 $id $erpDataObject->getFieldValue($identifierField);
  180.                 if (empty($id)) {
  181.                     continue;
  182.                 }
  183.                 $cache->get(
  184.                     CacheKeyGenerator::generateKey($id),
  185.                     function (ItemInterface $item) use ($erpDataObject) {
  186.                         $item->expiresAfter(Duration::DAY);
  187.                         $data $erpDataObject;
  188.                         $data->setType('ErpDataObject');
  189.                         $content = new Content();
  190.                         $content->setData($data);
  191.                         $dataResponse = new AbasResponse();
  192.                         $dataResponse->setContent($content);
  193.                         return $dataResponse;
  194.                     }
  195.                 );
  196.             }
  197.             $time round(microtime(true) - $startTime2);
  198.             $timePerObject $time $resultSize;
  199.             $this->logger->info('AbasComponentCache: Loading took: ' $time ' s and ' $timePerObject
  200.                 ' s per object.');
  201.             $totalSize += $resultSize;
  202.             if ($resultSize $packageSize) {
  203.                 break;
  204.             }
  205.             $offset += $packageSize;
  206.         }
  207.         $this->logger->info(
  208.             'AbasComponentCache: Loaded ' $totalSize ' objects into cache. Took: '
  209.                 Benchmark::getFormattedTime($startTimeTotal)
  210.         );
  211.     }
  212.     /**
  213.      * @param string $databaseAndGroup
  214.      *
  215.      * @return AbstractAdapter
  216.      */
  217.     protected function getCache(string $databaseAndGroup): AbstractAdapter
  218.     {
  219.         if (!isset($this->caches[$databaseAndGroup])) {
  220.             $cacheFactory = new CacheFactory(
  221.                 'AbasConnector_' str_replace(':''_'$databaseAndGroup),
  222.                 $this->pluginConfigReader
  223.             );
  224.             $this->caches[$databaseAndGroup] = $cacheFactory->create();
  225.         }
  226.         return $this->caches[$databaseAndGroup];
  227.     }
  228.     /**
  229.      * @param string $databaseAndGroup
  230.      * @param string $id
  231.      *
  232.      * @return AbasResponse|null
  233.      */
  234.     public function getFromCache(string $databaseAndGroupstring $id): ?AbasResponse
  235.     {
  236.         if (!\array_key_exists($databaseAndGroup$this->caches)) {
  237.             $this->loadViewCache($databaseAndGroup);
  238.         }
  239.         $cache $this->getCache($databaseAndGroup);
  240.         $cacheKey CacheKeyGenerator::generateKey($id);
  241.         if ($cache->hasItem($cacheKey)) {
  242.             $item $cache->getItem($cacheKey);
  243.             return $item->get();
  244.         }
  245.         return null;
  246.     }
  247.     /**
  248.      * @param string|null $fields
  249.      * @param MetaData $metaData
  250.      * @param bool $isTableColumn
  251.      *
  252.      * @return string
  253.      */
  254.     protected function getFields(?string $fieldsMetaData $metaDatabool $isTableColumn): string
  255.     {
  256.         if ($fields === null) {
  257.             $fieldArray = [];
  258.             $metaDataFields $metaData->getMetaDataFields();
  259.             /** @var MetaDataField $metaDataField */
  260.             foreach ($metaDataFields as $metaDataField) {
  261.                 if ($metaDataField->isSelectable() && $metaDataField->isTableColumn() === $isTableColumn) {
  262.                     $fieldArray[] = $metaDataField->getName();
  263.                 }
  264.             }
  265.             $fields FieldUtil::fieldArrayToString($fieldArray);
  266.         }
  267.         return $fields;
  268.     }
  269.     /**
  270.      * Delete a component from the cache and close the workspace.
  271.      *
  272.      * @param AbasComponent $component
  273.      *
  274.      * @return void
  275.      * @throws InvalidArgumentException
  276.      */
  277.     public function deleteComponent(AbasComponent $component): void
  278.     {
  279.         $bid $component->getBid();
  280.         $this->closeWorkspaceForBid($bid);
  281.         $this->componentAdapter->deleteItem($bid);
  282.     }
  283.     /**
  284.      * @param AbasComponent $component
  285.      * @param string $bid
  286.      *
  287.      * @return void
  288.      * @throws InvalidArgumentException
  289.      */
  290.     private function saveWorkspaceIdentifier(AbasComponent $componentstring $bid): void
  291.     {
  292.         /** @var ItemInterface $item */
  293.         $item $this->workspaceAdapter->getItem(self::WORKSPACES_KEY);
  294.         $workspaceIdentifiers = [];
  295.         if ($item->isHit()) {
  296.             $workspaceIdentifiers $item->get();
  297.         }
  298.         $response $component->getResponse();
  299.         if ($response === null) {
  300.             return;
  301.         }
  302.         $content $response->getContent();
  303.         if ($content === null) {
  304.             return;
  305.         }
  306.         $data $content->getData();
  307.         if ($data === null) {
  308.             return;
  309.         }
  310.         $meta $data->getMeta();
  311.         $workspaceIdentifiers[$bid] = [
  312.             'workingSetId' => $meta->getWorkingSetId(),
  313.             'workingSetEditorId' => $meta->getWorkingSetEditorId()
  314.         ];
  315.         $item->set($workspaceIdentifiers);
  316.         $this->workspaceAdapter->save($item);
  317.     }
  318.     /**
  319.      * @param string $bid
  320.      *
  321.      * @return void
  322.      */
  323.     private function closeWorkspaceForBid(string $bid): void
  324.     {
  325.         $item $this->workspaceAdapter->getItem(self::WORKSPACES_KEY);
  326.         if (!$item->isHit()) {
  327.             return;
  328.         }
  329.         $workspaceIdentifiers $item->get();
  330.         if (!\array_key_exists($bid$workspaceIdentifiers)) {
  331.             return;
  332.         }
  333.         $workingSetId $workspaceIdentifiers[$bid]['workingSetId'];
  334.         $cancelRequest = new AbasPostRequest('/workspace/' $workingSetId '/commands/CANCEL');
  335.         $this->apiClient->executeRequest($cancelRequest);
  336.         unset($workspaceIdentifiers[$bid]);
  337.         $item->set($workspaceIdentifiers);
  338.         $this->workspaceAdapter->save($item);
  339.     }
  340.     /**
  341.      * @return void
  342.      */
  343.     public function cleanupExpiredComponents(): void
  344.     {
  345.         $item $this->workspaceAdapter->getItem(self::WORKSPACES_KEY);
  346.         $workspaceIdentifiers $item->get();
  347.         if (is_iterable($workspaceIdentifiers)) {
  348.             foreach ($workspaceIdentifiers as $bid => $_workspaceIdentifier) {
  349.                 if (!$this->componentAdapter->hasItem($bid)) {
  350.                     $this->closeWorkspaceForBid($bid);
  351.                 }
  352.             }
  353.         }
  354.     }
  355. }