Repository navigation
Caching
The cache keeps a value for a while, so that slow work (a call to another service, a heavy count, a report) is done once and read many times. A capability reads through the cache, and the capabilities that change the data forget the value, so nobody reads an old copy for long. Since 2.18.0.
Say what is slow, how old the answer may be, and what changes it:
/caligrafy-quill:new-capability shelf_stats books:read read-only public: the numbers of books, authors and open loans, at most 10 minutes old; forget them when a book is added
Claude reads the value with Caligrafy\Cache::remember('<key>', <seconds>, fn () => ...) in the capability, and calls
Cache::forget('<key>') in the capabilities that change the data.
Check before you say OK:
- the key says what the value is (
bookshelf:stats), and holds the user's id when the value is someone's; - every capability that changes the data forgets the key;
- the value is plain data (numbers, strings, arrays), not a model object.
Run the commands in the Docker container (docker compose exec dev-box php caligrafer.php ...; without Docker, drop
docker compose exec dev-box).
-
The framework tables. The cache is kept in the
caligrafy_cachetable by default.docker compose exec dev-box php caligrafer.php platform installcreates it (the deploy step does too). -
Read through the cache. Run
docker compose exec dev-box php caligrafer.php service Bookshelf/ShelfStats --scope books:read --read-only, then makeapplication/services/Bookshelf/ShelfStats.phpread:<?php use Caligrafy\Cache; use Caligrafy\Capability; use Caligrafy\Context; use Caligrafy\Database; use Caligrafy\Service; #[Capability( name: 'shelf_stats', description: 'Returns the numbers of the Bookshelf: books, authors, and books out on loan now. Up to 10 minutes old.', scope: 'books:read', readOnly: true, public: true )] class ShelfStats extends Service { public function handle(array $input, Context $context) { return Cache::remember('bookshelf:stats', 600, function () { return array( 'books' => Database::table('books')->count(), 'authors' => Database::table('authors')->count(), 'out' => Database::table('loans')->whereNull('returned_at')->count() ); }); } }
remember()returns the kept value when there is one, otherwise runs the function, keeps its result for 600 seconds and returns it. -
Forget it where the data changes. In
application/services/Bookshelf/CreateBook.php(from Build once), adduse Caligrafy\Cache;under<?phpand makehandle()read:Do the same inpublic function handle(array $input, Context $context) { $id = Book::insert($input); Cache::forget('bookshelf:stats'); // the number of books changed return Book::with('author')->find($id); }
delete_bookandlend_book. -
Run it. With the sample rows of Models:
docker compose exec dev-box php caligrafer.php call shelf_statsThe value is now in the table:{ "data": { "books": 5, "authors": 3, "out": 2 } }docker compose exec mysql mysql -uroot -proot caligrafy -e "SELECT cache_key, value FROM caligrafy_cache"showsbookshelf:stats {"books":5,"authors":3,"out":2}. Add a book withcall create_book: the row is gone, and the nextcall shelf_statssays"books": 6.
Check:
- two
call shelf_statsin a row give the same answer, and the second one runs no count; - after
create_book,shelf_statshas the new number; -
docker compose exec dev-box php caligrafer.php cache clearprintsCache (database) emptied; <n> compiled view and class map file(s) removed.
use Caligrafy\Cache;
Cache::remember('bookshelf:stats', 600, fn () => $this->count()); // the kept value, or the function's, kept 600 s
Cache::put('report:2026-11', $rows, 3600); // for an hour (null: until forgotten; 0 or less: forgets it)
Cache::get('report:2026-11', []); // the value, or [] when it is not there or too old
Cache::has('report:2026-11'); // true or false
Cache::forever('settings', $settings); // until it is forgotten
Cache::forget('report:2026-11'); // gone
Cache::flush(); // the whole store- Values are stored as JSON: strings, numbers, booleans,
nulland arrays. An object (a model) comes back as an array, andremember()returns what a later read returns. A value that cannot be encoded is refused. - A key is any text but empty. Put whose it is in the key when it is someone's:
"loans:user:$id". - In one request, a value read once is read from memory after that.
CACHE_STORE in .env
|
Used when empty | Where |
|---|---|---|
database |
with DB_ACTIVATE=true
|
the caligrafy_cache table: one store for the web server, the queue, the scheduled jobs and every server of the app |
file |
without a database | a private folder of the temporary directory: one server, one system user |
off |
nothing is kept; remember() always runs its function |
Another value stops with CACHE_STORE=<value>: use file, database or off. Until the caligrafy_cache table exists,
the database store uses the file store and writes Caligrafy cache: the caligrafy_cache table is missing (php caligrafer.php platform install): using the file store in the PHP error log. One write in a hundred removes the
expired rows.
Why the database by default: the web server and the command line (the queue, scheduled jobs) run as different system users, and each one has its own file store. A job that forgets a value in the file store would leave the web server's copy in place. The database is one store for all of them.
php caligrafer.php cache clear empties the store, and the compiled views and the class map of the system user that
runs it. The web server keeps its own: run it as that user, or restart the container.
- Not forgetting a value where the data changes: people read the old copy until it expires.
-
CACHE_STORE=filewith a queue or scheduled jobs (G119): the job'sforget()does not reach the page's copy. Keep the database store. - Caching someone's data under a shared key: one person sees another's. Put the user's id in the key.
-
Expecting a model back: it comes back as an array (
$stats['books']).
- Build once: capabilities
- Background jobs: slow work done later
- Caligrafer: cache
Caligrafy: Describe it. Approve it. Ship it. Your idea turned into a complete app: every feature built once, for web pages, Vue apps, a REST API and AI agents, and online.
-
Quickstart with Claude: describe it, approve it, ship it
or the Quickstart: the same three steps by hand, in 5 minutes
- How Caligrafy works: the ideas in 5 minutes
- Your first capability: build a feature, page included
-
Capabilities in depth
- Scheduled jobs: capabilities on a timer
- Notifications: web push and email
- Background jobs: capabilities run later
- Migrations: database changes, in order
- The cache
- Testing your app
- Dependency injection
- Validation rules
-
Web pages
-
Vue apps
-
Mobile and tablet
-
REST API
-
AI agents
- Models
- Relationships
- Queries: filters, paging, related rows
- Sign-in, users and tokens · with an email
- Payments: Stripe and crypto
- Face detection and face login
- OpenAI: chat and images · Assistants
- Open models: Llama, Mistral...
- Chatbots with Watson
- Machine learning in the browser
- Metadata and social cards · search and analytics
-
Putting it online: the production image, the database at every start
- Caligrafer: every command
- Installation, configuration and Caligrafer
- Routing · Request · Controllers · Views
- Vue: flow of information · routes · validations
- Security and upgrading
- Release notes: 2.21 · 2.20 · 2.19 · 2.18 · 2.17 · 2.16 · 2.15 · 2.14 · 2.13 · 2.12 · 2.11 · 2.10 · 2.9 · 2.8 · 2.7 · 2.6 · 2.5 · 2.4 · 2.3 · 2.1 · October 2026 · changelog
- Testing Caligrafy (contributors)