wtorek, 18 kwietnia 2017

Symfony 2: użycie Doctrine Cache dla zapytań SOAP

DoctrineCache zwykle kojarzy się z zastosowaniem pamięci podręcznej dla metadanych i wyników zapytań na bazie danych. Tymczasem w prosty sposób można wykorzystać go również do przechowania wyników zapytań do zewnętrznych usług. Poniżej na szybko i z przykładami opis w jaki sposób można to uzyskać.

Instalacja

Jeśli jeszcze w swoim projekcie nie wykorzystujesz DoctrineCache, najprościej zainstalować go przy pomocy composera. Poniższa komenda doda DoctrineCacheBundle do zależności projektu.
composer require doctrine/doctrine-cache-bundle
Następnym krokiem jest włączenie bundle w swoim AppKernelu.
// app/AppKernel.php
public function registerBundles()
{
    // ...
    $bundles[] = new \Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle();

    return $bundles;
}

Konfiguracja

DoctrineCache wymaga od nas konfiguracji co najmniej jednego providera. W dokumentacji możemy znaleźć sporą listę providerów. Nasz przykład oprzemy o najprostszy z nich: file_system. Osobiście zachęcam, aby w miarę możliwości korzystać z innych, bardziej zaawansowanych rozwiązań. Przykładowo Memcache może zostać skonfigurowany tak, aby wiele instancji serwera aplikacyjnego mogło korzystać z jednego wspólnego cache.

Aby skonfigurować provider wystarczy w config.yml zamieścić poniższą sekcję:
doctrine_cache:
    providers:
        acme_filecache:
            file_system:
                extension: ".cache"
                directory: "%kernel.cache_dir%/doctrine"

Elementy na poziomie gałęzi 'providers' są konfiguracją pojedynczych dostawców w naszej aplikacji. Ważne, że są dostępne w naszym ServiceContainerze, czyli możemy się do nich odwołać z kontrolera, albo przekazać któryś do serwisu wstrzykując zależność. Wszystkie providery w kontenerze są zadeklarowane w tej samej przestrzeni nazw doctrine_cache.providers.

Wykorzystanie

Zaproponowane rozwiązanie jest silnie zależne od przyjętej przez nas architektury w projekcie. Dlatego nie należy traktować go jako prawdy objawionej. Bez wątpienia istnieją inne i pewnie nawet lepsze sposoby na zastosowanie DoctrineCache w tym samym celu.

w naszym projekcie istnieje klasa robiąca za proxy, dla dowolnego CacheProvidera. Najważniejsze, że jest zgodna z interfejsem Cache, więc w razie czego może być stosowana jak każdy CacheProvider.
use Doctrine\Common\Cache\Cache;

class CacheService implements Cache;
Dla ułatwienia sobie życia i nie kopiowania w kółko tego samego rozwiązania, proxy posiada jeszcze jedną istotną metodę. Podstawową operacją z wykorzystaniem cache, jest sprawdzenie obecności danych dla zadanego identyfikatora. Jeśli dane nie są dostępne w pamięci podręcznej, należy je pobrać z oryginalnego źródła. Dlatego proxy dostarcza metodę get, która wykonuje obie operacje.

public function get($id, $callable, $args, $lifeTime = 0, $forceUpdate = false)
{
    if ($forceUpdate || !$this->cacheProvider->contains($id)) {
        $data = call_user_func_array($callable, $args);
        $this->cacheProvider->save($id, $data, $lifeTime);
        return $data;
    }
    return $this->cacheProvider->fetch($id);
}
Jak widać jeden z parametrów to typowy callable, co daje pełną swobodę zastosowania źródła danych. Może to być inny cache, repozytorium, albo klient SOAP. Druga przydatna opcja to flaga wymuszająca odświeżenie danych z oryginalnego źródła. Cache zawsze wprowadza pewne opóźnienie. Jeśli mamy kontrolę nad aktualizacją źródła danych, możemy przy okazji inwalidować cache. Przy usługach zewnętrznych nie ma możliwości stwierdzić, czy nie pojawiły się nowe dane. Użycie takiej flagi może pomóc zaciągnąć dane z oryginalnego źródła na życzenie użytkownika.

Dla każdego klienta SOAP w projekcie również tworzymy fasady, które pozwalają ukryć wykorzystanie usługi zewnętrznej. W aplikacji nie korzystamy bezpośrednio z klienta soap, więc tak samo możemy też ukryć użycie naszego CacheService przy uderzeniach do SOAP.

public function getDictionary($name)
{
    return $this->cacheService->get(
        static::$getDictionaryCacheId.$name,
        [$this->soapClient, 'getDictionary'],
        [$name],
        static::$getDictionaryLifetime
    );
}
Oczywiście ostatni przykład jest znacznie uproszczony. Brakuje przechwytywania wyjątków, czy kontroli błędów z usługi zewnętrznej, szczególnie ważnych w tym przypadku, ponieważ nie chcemy odpowiedzi z komunikatem o błędzie zapisywać w cache i serwować jej do użytkownika przez kolejne godziny. Taka forma przykładu jest jednak dostateczna dla zobrazowania wykorzystania cache.

Brak komentarzy:

Prześlij komentarz