Storages¶
Storage is the persistence layer for intents and this library provides two implementations out of the box. Which one you should use depends on your project and on the guarantees which you expect from the storage.
Doctrine Dbal storage¶
RunOpenCode\Component\Intent\Storage\DbalStorage stores intents within a
single database table. Intents survive a restart of your infrastructure and they
are removed only when they are fetched, invalidated, or when the storage is
maintained.
1<?php
2
3use RunOpenCode\Component\Intent\Storage\DbalStorage;
4
5$storage = new DbalStorage($connection, 'runopencode_intent');
Table which is used for storing intents has the following columns:
id— identifier of an intent, stored as ULID.intent— serialized intent object.valid_from— moment from which an intent becomes available.expires_at— moment after which an intent is no longer available.created_at— moment when an intent has been stored.
Table is indexed by expires_at as well, since that column is used when
expired intents are removed from the storage.
Registering the ULID type¶
Identifier of an intent is stored as ULID, by using the type provided by
symfony/doctrine-bridge. Doctrine has to be aware of that type, so make sure
that it is registered before the storage is used:
1<?php
2
3use Doctrine\DBAL\Types\Type;
4use Symfony\Bridge\Doctrine\Types\UlidType;
5
6if (!Type::hasType(UlidType::NAME)) {
7 Type::addType(UlidType::NAME, UlidType::class);
8}
If you are using Symfony framework, this is done for you, see the Intent Bundle documentation.
Generating the table¶
If you are using Doctrine ORM, storage is able to contribute its table to the
schema which ORM generates for your entities. Register it as a listener for the
postGenerateSchema event and the table will be created for you along with
the rest of your schema:
1<?php
2
3use Doctrine\ORM\Tools\ToolEvents;
4
5$entityManager
6 ->getEventManager()
7 ->addEventListener([ToolEvents::postGenerateSchema], $storage);
Storage will contribute its table only if the schema is generated for the very same connection which is used for storing intents, which makes it safe to use within projects having more than one entity manager.
If you are using Symfony framework, this is done for you, see the Intent Bundle documentation.
Cache storage¶
RunOpenCode\Component\Intent\Storage\CacheStorage stores intents within any
PSR-6 cache pool, which makes Redis, Memcached, filesystem, or any other cache
implementation available to you without any additional code:
1<?php
2
3use RunOpenCode\Component\Intent\Storage\CacheStorage;
4use Symfony\Component\Cache\Adapter\RedisAdapter;
5
6$storage = new CacheStorage(RedisAdapter::createConnection('redis://localhost'));
Expiration is delegated to the cache pool itself, which means that expired
intents are evicted without any effort on your side and that maintenance()
does nothing.
However, do note that a cache pool is a cache, and that it is allowed to evict an item before it expires. If losing an intent is not acceptable for your use case, use the storage which uses Doctrine Dbal instead. Also, it is advisable to use a dedicated cache pool for intents, so that clearing an application cache does not remove pending intents as well.
Implementing your own storage¶
Should neither of the provided storages suit your needs, implementing your own
is a trivial task. Implement
RunOpenCode\Component\Intent\Contract\IntentStorageInterface and make sure
that your implementation honours the following rules:
store()returns a newly generatedUlid, which must be unguessable, since it is the only thing which protects an intent from being fetched by somebody else.fetch()throwsNotExistsExceptionif an intent does not exist, if it has expired, or if it is not available yet.fetch()invalidates an intent after it has been fetched, unless it is explicitly instructed not to do so.An intent which is not available yet must be preserved, while an expired intent may be removed.
invalidate()does not throw an exception if an intent with the given identifier does not exist.maintenance()removes all expired intents, or does nothing, if a storage removes them on its own.