PHP code example of debuss-a / flora

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),
]);

use function Flora\{CreateIndex, Get, Index, MatchIndex, Paginate};

$client->query(CreateIndex([
    'name' => 'users_by_email',
    'source' => Collection('users'),
    'terms' => [['field' => ['data', 'email']]],
    'unique' => true,
]));

$client->query(Get(MatchIndex(Index('users_by_email'), '[email protected]'))); // first match

$client->query(CreateIndex([
    'name' => 'users_by_role',
    'source' => Collection('users'),
    'terms' => [['field' => ['data', 'role']]],
    'values' => [['field' => ['data', 'age'], 'reverse' => true], ['field' => ['data', 'name']], ['field' => ['ref']]],
]));

$page = $client->query(Paginate(MatchIndex(Index('users_by_role'), 'admin'), ['size' => 20]));
// $page->data: [[85, 'Grace', Ref], [36, 'Ada', Ref], ...]
$next = $client->query(Paginate(MatchIndex(Index('users_by_role'), 'admin'), ['size' => 20, 'after' => $page->after]));
$prev = $client->query(Paginate(MatchIndex(Index('users_by_role'), 'admin'), ['size' => 20, 'before' => $next->before]));

$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\{Call, CreateFunction, FunctionRef, Functions, Query};

$client->query(CreateFunction([
    'name' => 'transfer',
    'body' => Query(Lambda(['from', 'to', 'amount'], IfElse(
        LT(Select(['data', 'balance'], Get(Variable('from'))), Variable('amount')),
        Abort('Insufficient funds.'),
        [
            Update(Variable('from'), ['data' => ['balance' => Subtract(Select(['data', 'balance'], Get(Variable('from'))), Variable('amount'))]]),
            Update(Variable('to'), ['data' => ['balance' => Add(Select(['data', 'balance'], Get(Variable('to'))), Variable('amount'))]]),
        ],
    ))),
]));

$client->query(Call(FunctionRef('transfer'), $from, $to, 100));            // or Call('transfer', ...)
$client->query(Update(FunctionRef('transfer'), ['body' => Lambda(...)]));   // or ['name' => ...]
$client->query(Delete(FunctionRef('transfer')));

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\{Equals, IfElse, Lambda, Let, Map, Select, Variable};

$client->query(Map(
    Paginate(MatchIndex(Index('users_by_role'), 'admin')),
    Lambda(['age', 'name', 'ref'], Select(['data', 'email'], Get(Variable('ref')), 'none')),
));

$client->query(Let(
    ['user' => Get(MatchIndex(Index('users_by_email'), '[email protected]'))],
    IfElse(
        Equals(Select(['data', 'role'], Variable('user')), 'admin'),
        Update(Select('ref', Variable('user')), ['data' => ['last_seen' => time()]]),
        null,
    ),
));

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))));

try {
    $client->query(Create(Collection('users'), ['data' => ['username' => 'grace', 'email' => '[email protected]']]));
} catch (NotUnique $e) {
    $e->index; // "users_by_email"
}