moneybird-php-client icon indicating copy to clipboard operation
moneybird-php-client copied to clipboard

Feature request: Implement PSR-6 compliant caching mechanism

Open Xesau opened this issue 1 year ago • 0 comments

Results from the Moneybird API are easily cacheable because of the synchronization API and because most (all?) entities have a version attribute. It would therefore make sense to integrate support for a PSR-6 compatible cache into this package to speed up queries and prevent users from making too many requests to the API.

Preferable, the user could specify a prefix for the cache key.

This would require the following work to be done:

  • Adding version to the attribute list of all entities that support synchronization.
  • Checking if all entities that support synchronization actally use the Synchronize trait.
  • Updating the getAll() method to load items from and save items to the cache if the entity supports synchronization.

Currently, it is possible to implement something similar using the following code. However, this requires modifying the output of the ->attributes() method, which does not return raw data that can cached and used with ->collectionFromResult(...) later.

<?php

namespace App\Service;

use Picqer\Financials\Moneybird\Moneybird;
use Psr\Cache\CacheItemPoolInterface;

class MoneybirdCache
{

    public function __construct(
        private readonly Moneybird $moneybird,
        private readonly CacheItemPoolInterface $cache
    ) {}

    public function getAll(string $type, array $filters): array
    {
        $results = [];

        $repository = $this->moneybird->$type();
        $versions = $repository->listVersions($filters);
        $idsToFetch = [];
        foreach ($versions as $version)
        {
            $versionCacheItem = $this->cache->getItem('moneybird.'. $type .'.'. $version->id);
            if ($versionCacheItem->isHit())
            {
                $cacheResult = $versionCacheItem->get();
                if ($cacheResult['version'] == $version->version)
                {
                    $results[] = $cacheResult;
                    continue;
                }
            }
            $idsToFetch[] = $version->id;
        }

        $results = $repository->collectionFromResult($results);

        for ($i = 0; $i < count($idsToFetch); $i += 100)
        {
            $fetchedObjects = $repository->getVersions(array_slice($idsToFetch, $i, 100, false));

            foreach ($fetchedObjects as $object)
            {
                $results[] = $object;
                $versionCacheItem = $this->cache->getItem('moneybird.'. $type .'.'. $object->id);

                $attributes = self::normalizeAttributes($object->attributes());

                $versionCacheItem->set($attributes);
                $this->cache->save($versionCacheItem);
            }
        }

        return $results;
    }

    private static function normalizeAttributes(array $attributes): array
    {
        $normalizedAttributes = [];

        foreach ($attributes as $key => $value)
        {
            if (is_scalar($value))
            {
                $normalizedAttributes[$key] = $value;
            }
            elseif (is_array($value))
            {
                $normalizedAttributes[$key] = self::normalizeAttributes($value);
            }
            elseif ($value instanceof Model)
            {
                $normalizedAttributes[$key] = $value->attributes();
            }
        }

        return $normalizedAttributes;
    }

}

Xesau avatar Sep 19 '23 13:09 Xesau