Loading…

PDODocument
Basic Usage

Overview

What is a document? For the purposes of this library, documents can be objects or associative arrays. Most of the functions are geared toward classes, but arrays can be handy for patching documents.

There are several categories of operations that can be accomplished against documents.

Insert and Save were the only two that don't mention criteria. For the others, “some criteria” can be defined a few different ways:

Finally, Find also has firstBy* implementations for all supported criteria types.

Saving Documents

The library provides three different ways to save data. The first equates to a SQL INSERT statement, and adds a single document to the repository. If automatic IDs are enabled, the document will receive one.

$room = new Room(/* ... */);
// Parameters are table name and document
Document::insert('room', $room);

The second is Document::save; and inserts the data it if does not exist and replaces the document if it does exist (what some call an “upsert”). It utilizes the ON CONFLICT syntax to ensure an atomic statement. Its parameters are the same as those for Insert. Note that this does not consider automatic IDs; using this will allow you to bypass that generation for documents you know are new.

The third equates to a SQL UPDATE statement. Update applies to a full document and is usually used by ID, while Patch is used for partial updates and may be done by field comparison, JSON containment, or JSON Path match. For a few examples, let's begin with a query that may back the “edit hotel” page. This page lets the user update nearly all the details for the hotel, so updating the entire document would be appropriate.

$hotel = Find::byId('hotel', $hotelId, Hotel::class);
if ($hotel->isDefined()) {
    // update hotel properties from the posted form
    Document::update('hotel', $hotel->id, $hotel);
}

For the next example, suppose we are upgrading our hotel, and need to take rooms 221-240 out of service*. For PostgreSQL, we can utilize a patch via JSON Path.

// PostgreSQL only
Patch::byJsonPath('room',
    "$ ? (@.hotelId == \"abc\" && (@.roomNumber >= 221 && @.roomNumber <= 240)",
    ['inService' => false]);

* - we are ignoring the current reservations, end date, etc. This is very naïve example!

For SQLite, we can utilize a Field query with a between operator. (This will also work in PostgreSQL.)

// SQLite
Patch::byFields('room', [Field::between('roomNumber', 221, 240)], ['inService' => false]); 

Finding Documents

Functions to find documents start with Find::. There are variants to find all documents in a table, find by ID, find by JSON field comparison, find by JSON containment, or find by JSON Path. The hotel update example above utilizes an ID lookup; the descriptions of JSON containment and JSON Path show examples of the criteria used to retrieve using those techniques.

Find functions take a class name, and will attempt to map the document returned to the class specified. Queries which can return zero-or-one results, like byId and the firstBy* functions, will return BitBadger\InspiredByFSharp\Option<class> representing the possibly-single result.

All functions other than byId also take an optional list of fields by which the results should be ordered. There is a Field function ::named to support creating a field with no comparison criteria. When specifying this name, one can include direction information like DESC and NULLS FIRST; additionally, there are two prefixes that will affect the sort order:

A couple of quick examples:

// Sorts "field1" ascending, using default case-sensitivity
Field::named('field1')

// Sorts "field2" descending, treating it as a number
Field::named('n:field2 DESC');

// Sorts "field3" ascending case-insensitively with NULLs last
Field::named('i:field3 NULLS LAST');

Results and the DocumentList

Find::all and Find::by* will return a DocumentList<class> instance. This is a lazy iterator over these results, and has several ways to interact with the results, none of which involve loading the entire result set into memory. It is a consumable iteration; once it is read, the results are no longer available.

All that said, the foreach on items()/items is quite straightforward.

// use ->items() for PHP 8.2/8.3
foreach ($result->items as $item) {
    // Do something amazing with $item
}

Deleting Documents

Functions to delete documents start with Delete::. Document deletion is supported by ID, JSON field comparison, JSON containment, or JSON Path match. The pattern is the same as for finding or partially updating. (There is no library method provided to delete all documents, though deleting by JSON field comparison where a non-existent field is null would accomplish this.)

Counting Documents

Functions to count documents start with Count::. Documents may be counted by a table in its entirety, by JSON field comparison, by JSON containment, or by JSON Path match. (Counting by ID is an existence check!)

Document Existence

Functions to check for existence start with Exists::. Documents may be checked for existence by ID, JSON field comparison, JSON containment, or JSON Path match.

What / How Cross-Reference

The table below shows which commands are available for each access method. (X = supported for both, P = PostgreSQL only)

Operation all byId byFields byContains byJsonPath firstByFields firstByContains firstByJsonPath
Count X X P P
Exists X X P P
Find X X X P P X P P
Patch X X P P
RemoveFields X X P P
Delete X X P P

Document::insert, Document::save, and Document::update operate on single documents.