1. Go to this page and download the library: Download debuss-a/flora library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
debuss-a / flora example snippets
use function Flora\{Collection, Get, Ref};
$client->query(Get(Ref(Collection('users'), '9203420348023942')));
use Flora\Client;
use Flora\Driver\{MariadbDriver, MysqlDriver, PostgresDriver, SqliteDriver};
$driver = new PostgresDriver(new PDO('pgsql:host=localhost;dbname=app', 'user', 'password'));
// or
$driver = new MysqlDriver(new PDO('mysql:host=localhost;dbname=app;charset=utf8mb4', 'user', 'password'));
$driver = new MariadbDriver(new PDO('mysql:host=localhost;dbname=app;charset=utf8mb4', 'user', 'password'));
$driver = new SqliteDriver(new PDO('sqlite:/path/to/app.sqlite'));
$driver->install(); // creates the flora_collections, flora_indexes and flora_functions tables, once
$client = new Client($driver);
use function Flora\{Collection, Create, CreateCollection, Delete, Exists, Get, Ref, Replace, Update};
$client->query(CreateCollection(['name' => 'users']));
$ada = $client->query(Create(Collection('users'), ['data' => ['name' => 'Ada', 'email' => '[email protected]']]));
// ['ref' => Ref(users, "1068...") , 'ts' => 1791480000000000, 'data' => ['name' => 'Ada', ...]]
$client->query(Create(Ref(Collection('users'), '42'), ['data' => [...]])); // at a chosen id
$client->query(Get($ada['ref']));
$client->query(Update($ada['ref'], ['data' => ['age' => 36, 'email' => null]])); // deep merge, null removes a key
$client->query(Replace($ada['ref'], ['data' => ['name' => 'Ada Lovelace']])); // replaces all data
$client->query(Exists($ada['ref']));
$client->query(Delete($ada['ref'])); // returns the deleted document
use function Flora\{Abort, IfElse, NewId};
// Abort() stops the query: its writes are rolled back, and the client throws Flora\Exception\Aborted.
$client->query([
Update($account, ['data' => ['balance' => Subtract(Select(['data', 'balance'], Get($account)), 100)]]),
IfElse(LT(Select(['data', 'balance'], Get($account)), 0), Abort('Insufficient funds.'), null),
]);
$client->query(Update(Index('users_by_email'), ['name' => 'emails'])); // renames it
$client->query(Update(Index('emails'), ['unique' => false])); // or makes it unique, or not
$client->query(Delete(Index('emails'))); // the documents stay
$client->query(CreateIndex([
'name' => 'posts_by_tag',
'source' => Collection('posts'),
'terms' => [['field' => ['data', 'tags'], 'unroll' => true]],
]));
// A post tagged ['php', 'sql'] has an entry for each tag: it matches both.
$client->query(Paginate(MatchIndex(Index('posts_by_tag'), 'php')));
// Posts tagged php and sql: entries of the same index, combined like any sets.
Intersection(MatchIndex(Index('posts_by_tag'), 'php'), MatchIndex(Index('posts_by_tag'), 'sql'));
use function Flora\{Difference, Intersection, Union};
// Admins or Parisians, each once, sorted by ref.
$client->query(Paginate(Union(
MatchIndex(Index('users_by_role'), 'admin'),
MatchIndex(Index('users_by_city'), 'Paris'),
)));
// Admins living in Paris.
$client->query(Get(Intersection(
MatchIndex(Index('users_by_role'), 'admin'),
MatchIndex(Index('users_by_city'), 'Paris'),
)));
// Admins not living in Paris.
$client->query(Paginate(Difference(
MatchIndex(Index('users_by_role'), 'admin'),
MatchIndex(Index('users_by_city'), 'Paris'),
)));
// Sets nest: (admins or editors) in Paris, except the viewers.
Difference(Intersection(Union($admins, $editors), $parisians), $viewers);
// Lists too.
$client->query(Union([1, 2], [2, 3])); // [1, 2, 3]
$client->query(Intersection([1, 2], [2, 3])); // [2]
$client->query(Difference([1, 2], [2, 3])); // [1]
use function Flora\Join;
// Posts written by admins: the posts_by_author index (terms: data.author) matched with the refs of the admins.
$client->query(Paginate(Join(MatchIndex(Index('users_by_role'), 'admin'), Index('posts_by_author'))));
// Projects of the teams of the admins: teams_by_role holds a value, data.team, matched with projects_by_team.
Join(MatchIndex(Index('teams_by_role'), 'admin'), Index('projects_by_team'));
// Joins nest and combine: comments on the posts of admins, except those of the web team.
Join(Difference(Join($admins, Index('posts_by_author')), $webPosts), Index('comments_by_post'));
// A lambda receives the items of each source entry (destructured when it holds several values) and returns a set.
Join($admins, Lambda('ref', MatchIndex(Index('posts_by_author'), Variable('ref'))));
Join($ada, Lambda(['team', '_'], MatchIndex(Index('grants_by_team_and_role'), Variable('team'), 'editor')));
// Posts written and reviewed by the same admin: all the terms of the result refer to the same entry.
Join($admins, Lambda('ref', Intersection(
MatchIndex(Index('posts_by_author'), Variable('ref')),
MatchIndex(Index('posts_by_reviewer'), Variable('ref')),
)));
use function Flora\Between;
// Admins aged 40 to 65, both role'), 'admin'), 40, 65)));
// October's orders, from an index whose first value is the date: the bounds are prefixes.
Between(MatchIndex(Index('orders_by_date')), [Timestamp('2026-10-01T00:00:00Z')], [Timestamp('2026-10-31T23:59:59.999999Z')]);
// From 40 on, up to 65: an empty list leaves a side open.
Between(MatchIndex(Index('ages_by_role'), 'admin'), 40, []);
Between(MatchIndex(Index('ages_by_role'), 'admin'), [], 65);
use function Flora\{All, Any, Filter, GT, GTE, LT, LTE, Not};
// Admins living in Paris: the lambda receives each entry (a ref here) and returns whether to keep it.
$client->query(Paginate(Filter(
MatchIndex(Index('users_by_role'), 'admin'),
Lambda('ref', Equals(Select(['data', 'city'], Get(Variable('ref'))), 'Paris')),
)));
// On the values of an index: admins aged 40 or more (Between(..., 40, []) reads them from the index).
Filter(MatchIndex(Index('ages_by_role'), 'admin'), Lambda(['age', 'ref'], GTE(Variable('age'), 40)));
// Lists and pages too (a page keeps its cursors).
$client->query(Filter([1, 2, 3, 4], Lambda('n', GT(Variable('n'), 2)))); // [3, 4]
use function Flora\{Add, Append, Concat, Greatest, Reduce, Size};
$client->query(Size(MatchIndex(Index('users_by_role'), 'admin'))); // 2
$client->query(Size(Union($admins, $parisians))); // each entry once
$client->query(Size(Filter(Documents(Collection('users')), $inParis)));
$client->query(Size([1, 2, 2])); // 3
// The reducer receives the value so far and each item: the total age of the admins, and the oldest.
$ages = MatchIndex(Index('ages_by_role'), 'admin'); // values: age, ref
$client->query(Reduce(Lambda(['total', 'entry'], Add(Variable('total'), Select(0, Variable('entry')))), 0, $ages));
$client->query(Reduce(Lambda(['oldest', 'entry'], Greatest(Variable('oldest'), Select(0, Variable('entry')))), 0, $ages));
// Their names, as a list, then a string.
$names = Reduce(Lambda(['names', 'entry'], Append([Select(['data', 'name'], Get(Select(1, Variable('entry'))))], Variable('names'))), [], $ages);
$client->query(Concat($names, ', ')); // "Margaret, Ada, Grace"
use function Flora\{ContainsField, ContainsPath, ContainsValue, Distinct, Drop, IsEmpty, IsNonEmpty, Mean, Merge,
Reverse, Sum, Take, ToArray, ToObject};
$client->query(Take(2, [1, 2, 3])); // [1, 2]
$client->query(Drop(2, [1, 2, 3])); // [3]
$client->query(Paginate(Reverse(MatchIndex(Index('ages'))))); // the oldest first
$client->query(Get(Reverse(MatchIndex(Index('ages_and_refs'))))); // the oldest user
$client->query(Paginate(Distinct(MatchIndex(Index('tags'))))); // each tag once
$client->query(IsEmpty(MatchIndex(Index('users_by_role'), 'guest'))); // true
$client->query(Sum(MatchIndex(Index('ages_by_role'), 'admin'))); // their total age
$client->query(Mean([1, 2, 3, 4])); // 2.5
$client->query(ContainsValue(41, MatchIndex(Index('ages')))); // is someone 41?
$client->query(ContainsPath(['data', 'address', 'city'], Get($ref)));
$client->query(Merge(['a' => 1, 'b' => 2], ['b' => null, 'c' => 3])); // ['a' => 1, 'c' => 3]
$client->query(ToArray(['a' => 1])); // [['a', 1]], and back with ToObject()
use function Flora\{Casefold, ContainsStr, Format, LowerCase, ReplaceStrRegex, SplitStr, SubString, ToInteger,
ToString, TrimStr, IsString};
$client->query(LowerCase('ÉCOLE')); // "école"
$client->query(Casefold('Straße')); // "strasse", to compare
$client->query(SubString('Ada Lovelace', -8, 4)); // "Love"
$client->query(SplitStr('a,b,c', ',')); // ['a', 'b', 'c']
$client->query(ReplaceStrRegex('One Fish Two Fish', '(\w+) Fish', '$1 Cat')); // "One Cat Two Cat"
$client->query(Format('%s is %d years old', 'Ada', 36)); // "Ada is 36 years old"
$client->query(ToInteger('12')); // 12
$client->query(IsString(Select(['data', 'name'], Get($ref))));
use function Flora\{Collections, Functions, Indexes};
$client->query(Paginate(Collections())); // [Collection('accounts'), Collection('users')]
$client->query(Map(Paginate(Indexes()), Lambda('index', Get(Variable('index'))))); // the definitions
$client->query(Get(Index('users_by_email'))); // name, source, terms, values, unique, ts
$client->query(Exists(FunctionRef('transfer')));
use function Flora\Each;
// Delete the guests, a page at a time: each page in a query, and so in a transaction.
do {
$page = $client->query(Each(
Paginate(MatchIndex(Index('users_by_role'), 'guest'), ['size' => 100]),
Lambda('ref', Delete(Variable('ref'))),
));
} while ($page->after !== null);
// A year older each: the items of a page of ages_by_role are its values, the age and the ref.
Each(Paginate($admins), Lambda(['age', 'ref'], Update(Variable('ref'), ['data' => ['age' => Add(Variable('age'), 1)]])));
use function Flora\{Epoch, Now, TimeAdd, TimeDiff, Timestamp, TimeSubtract, ToMicros};
$client->query(Now()); // the time of the query, a DateTimeImmutable in UTC
$client->query(Timestamp('2026-10-11T14:00:00+02:00')); // 2026-10-11 12:00:00 UTC
$client->query(Create(Collection('people'), ['data' => ['born' => Timestamp('1815-12-10T00:00:00Z')]]));
$client->query(At(Timestamp('2026-10-01T00:00:00Z'), Paginate(MatchIndex(Index('users_by_role'), 'admin'))));
// Documents written in the last hour: the ts of a document counts microseconds.
$client->query(Paginate(Filter(Documents(Collection('people')), Lambda('ref',
GT(Select('ts', Get(Variable('ref'))), ToMicros(TimeAdd(Now(), -1, 'hour'))),
))));
$client->query(Epoch($document['ts'], 'microseconds')); // the ts of a document, as an instant
$client->query(TimeDiff(Epoch($document['ts'], 'microseconds'), Now(), 'days')); // its age, in whole days
use Flora\Value\Date;
use function Flora\{DayOfWeek, Month, ToDate, ToTime, Year};
$client->query(ToDate('2026-10-11')); // a Flora\Value\Date
$client->query(ToDate(Now())); // today, in UTC
$client->query(Create(Collection('people'), ['data' => ['birthday' => new Date('1815-12-10')]]));
$client->query(Paginate(Between(MatchIndex(Index('birthdays')), ToDate('1900-01-01'), ToDate('1949-12-31'))));
$client->query(TimeDiff(ToDate('2026-01-01'), ToDate('2026-10-11'), 'days')); // 283
$client->query([DayOfWeek($time), Month($time), Year($time)]); // of an instant or a date, in UTC
use function Flora\{At, Events, Insert, Remove};
$created = $client->query(Create(Collection('users'), ['data' => ['name' => 'Ada', 'role' => 'guest']]));
$client->query(Update($created['ref'], ['data' => ['role' => 'admin']]));
// Every write of a document is an event: {ts, action (create, update or delete), document, data}.
$client->query(Paginate(Events($created['ref'])));
// Every change of a set is an event: {ts, action (add or remove), document, data (the values of the entry)}.
$client->query(Paginate(Events(MatchIndex(Index('users_by_role'), 'admin'))));
// Rewrites history: an event is an action and the whole document after it. The document becomes what its last event
// says, so rewriting the last one changes it.
$client->query(Insert($created['ref'], $created['ts'] + 1, 'update', ['data' => ['name' => 'Ada Byron', 'role' => 'guest']]));
$client->query(Remove($created['ref'], $created['ts'] + 1, 'update'));
// Reads as the database was at a time, in microseconds like the ts of documents.
$client->query(At($created['ts'], Get($created['ref']))); // role: guest
$client->query(Get($created['ref'], $created['ts'])); // the same
$client->query(At($created['ts'], Paginate(MatchIndex(Index('users_by_role'), 'guest'))));
use function Flora\{SelectAll, Sequence, Singleton};
$client->query(Sequence(Update($ref, ['data' => ['seen' => true]]), Get($ref))); // the value of the last one
$client->query(SelectAll(['data', 'tags', 'name'], Get($post))); // the name of each tag
$client->query(Paginate(Difference(MatchIndex(Index('users_by_role'), 'admin'), Singleton($me))));