PHP API Tutorial
This tutorial builds on the Content Types Tutorial to illustrate how PHP scripts, models, components and apps can use the COMAND PHP API to interact with repositories.
Create a File
Before getting into code, let's add a file we can use to enter and test code as we go.
- Launch the Files App.
- In the folder tree on the left, expand and click:
publications
>staging.<account>.webcomand.com
>htdocs
- Click Add () New File in to toolbar on the right.
- Enter the Name: test.php
- As you go through each code example below:
- Copy and paste code
- Click Approve
- Open/Refresh the following web address in your web browser.
https://staging.<account>.webcomand.com/test.php
Simple Example
The PHP code below will query and display Presidents stored in the configured repository, and is a good starting point to learn COMAND PHP API basics.
<?php
// enable COMAND API auto-loader
require_once('/var/www/webcomand/comand.php');
// connect to configured repository, or exit with an error message
$repo = comand::repo() or
exit("Could not connect to repository.");
// get all presidents in repository whose name starts with "John"
$presidents = $repo->get("FROM President WHERE Name LIKE 'John%'");
// display number of contacts found and their names
header("Content-Type: text/plain");
echo(count($presidents) . " presidents\n");
foreach($presidents as $president) {
echo(" $president->Name\n");
}
Common Tasks
Applications that interact with COMAND repositories typically perform some common tasks, including those in the example above. The most common tasks are described below with additional examples.
- Enable API Auto-Loader - Load API classes automatically, as needed.
- Connect to a Repository - Connect to the configured repository.
- Query a Repository - Query for specific objects.
- Traverse a Repository - Traverse the object hierarchy and object relationships.
- Create, Read, Update and Write (CRUD) - Work with objects and field values.
Enable API Auto-Loader
To access the API classes, require_once comand.php. It will enable the API auto-loader, which will automatically load COMAND API classes as needed, so you don't need to explicitly require each one.
require_once('/var/www/webcomand/comand.php');
Connect to a Repository
When COMAND is installed, a default repository is configured. The default repository can be accessed with a simple call to the static repo
method.
$repo = comand::repo();
Pass connect options to the static repo
method to override defaults.
// connect to the repository in Working mode as the user "john"
$repo = comand::repo([
'mode'=>\io_comand\repository\storage\storage_engine::MODE_WORKING,
'auth'=>['type'=>'password', 'username'=>'john', 'password'=>'123']
]);
Query a Repository
Once connected to a repository, its objects can be queried with query languages: cPath and cQL.
- cPath - A query language similar to XPath is used to navigate and select objects in a repository.
- cQL - A query language similar to SQL, which breaks a query into
SELECT
,FROM
,GROUP BY
,ORDER BY
andLIMIT
clauses. Except, unlike SQL:- All clauses are optional.
- An additional
IN
clause is available, which uses cPath to limit query results to a filtered segment of the object hierarchy. - Relationships are handled with dot-notation fields (ie. "Contact.Friend") instead of joins (ie. "JOIN contact AS friend ON (friend.id=contact.friend_id)").
- Multiple object types are supported in a FROM clause, and each may be followed by a + symbol to include the object type and others that extend (inherit) or implement it (polymorphism).
Query Results (Collections and Row Sets)
A query can return results as a collection of objects or a set of rows.
- Collection - Like an array of objects, similar to what an object database might return. A collection can be treated exactly like a PHP array of objects, except it has additional features, such as object type counts, and functionality, such as subsequent query processing.
- Row Set - Like an array of associative arrays with key's that correspond to the selected fields and values that correspond to the selected values for a given row, similar to what a relational database might return in a result set. A row set can be treated exactly like a PHP array of associative arrays, except it has additional features, such as loading rows on-the-fly, and functionality, such as retrieving information about the selected fields.
cQL and cPath
Similar to traditional relational database APIs, cQL and cPath query strings can be used to query a repository. Query strings can be passed to the repository "get methods".
- get - Return the results as a collection of zero or more objects. NULL on error.
- get_first - Return the first result as an object, otherwise NULL.
- get_rows - Return the results as a row set of zero or more rows. NULL on error.
- get_row - Return the first result as a row, as retrieved from a row set, otherwise NULL.
// get collection of contact objects from cQL query string
$presidents = $repo->get("FROM President WHERE Name LIKE 'John%'");
// same as above, except only the first result object is returned
$president = $repo->get_first("FROM President WHERE Name LIKE 'John%'");
// similar to above, except with a cPath query string
$presidents = $repo->get("[:President AND Name LIKE 'John%']");
// same as above, except only the first result object is returned
$president = $repo->get_first("[:President AND Name LIKE 'John%']");
Relationships are queried with dot-notation in cQL and cPath, instead of using JOINs like in SQL.
// get collection of president objects from cQL query string
$president = $repo->get("WHERE Type.Identifier='President'");
Binding
There are a few ways to bind variables to queries, which helps prevent SQL injection-like issues.
// bind variables sequentially with unnamed bindings in a cQL query
$number = 12;
$name = 'Abraham Lincoln';
$presidents = $repo->get("FROM President WHERE Number>? AND Name!=?", [
'bind'=>[$number, $name]
]);
// bind multiple variables with named bindings in a cQL query
$presidents = $repo->get("FROM President WHERE Number>:n AND Name!=:name",
['bind'=>['n'=>$number, 'name'=>$name]
);
Programmatic Queries
Programmatic queries integrate with app logic well because they are assembled in distinct parts.
To assemble a programmatic query, first instantiate a new query object and then call its helper methods to add query clauses and expressions.
// instantiate new query object
$query = $repo->query();
// query President objects (from clause)
$query->from('President');
// where Presidents have a name that starts with John (where clause)
$query->where("Name LIKE 'John%'");
Once a query is assembled, it can be processed by any of the query object's "get methods", which are the same as the repository "get methods" described above, minus the first query string parameter.
// get query results as a collection of objects
$presidents = $query->get();
echo(count($presidents) . " presidents(s)\n");
foreach($presidents as $president) {
echo(" $president->Name\n");
}
// get first query result as an object
$president = $query->get_first();
if($president) {
echo("$president->Name\n");
}
// get query results as a row set
$rows = $query->get_rows();
echo(count($rows) . " presidents(s)\n");
foreach($rows as $row) {
echo(" " . $row['Name'] . "\n");
}
// get first query result as a row set
$row = $query->get_row();
if($row) {
echo($row['Name'] . "\n");
}
Query helper methods return the updated query object, so they can be chained together.
// get all contacts in repository born after December 31, 1999
$presidents = $repo->query()
->from('President')
->where("Name LIKE 'John%'")
->get();
// display the number of contacts found and their names
echo(count($presidents) . " contact(s)\n");
foreach($presidents as $president) {
echo(" $president->Name\n");
}
Traverse a Repository
In addition to querying repository objects like a database, repository objects can also be accessed, inspected and traversed using a traditional object-oriented approach.
get_root
method
Each repository has a single root object, which is typically a folder at the top of its object hierarchy. To get the root object, use the repository object's get_root
method.
$root = $repo->get_root();
object values
Like any object, the root object's field values can be accessed like properties of a standard PHP object.
// display the root object (a folder) Title field value
echo("Root Folder Title: " . $root->Title . "\n");
object relationships
Like any object, the root object's relationship fields can also be accessed like properties of a standard PHP object. Single embedded or referenced objects are accessed like an object property.
// display the root object's Content Type Title
echo("The root object is a " . $root->Type->Title . "\n");
Fields that are a list of embedded or referenced objects (aka collections) are accessed like an array property.
// display the root object (a folder) Contents
echo("Root object contains " count($root->Contents) . " object(s)\n");
$num = 1;
foreach($root->Contents as $object) {
echo("$num. $object\n"); // display each object's Summary
$num++;
}
Display repository object hierarchy
Now that we know how to access the root object and determine if the root or any other object is a Folder, we can write a recursive function to display the entire repository folder hierarchy.
/**
* Display a folder hierarchy recursively.
*
* NOTE: We need to keep track of "visited" folders to avoid infinite
* recursion because a folder can exist in more than one place.
*/
function echo_object($object, $spacing='', $visited=[]) {
echo("$spacing$object\n");
if($object->Type->Title == 'Folder' && !in_array($object->OID, $visited)) {
$visited[] = $object->OID;
foreach( $object->Contents as $child ) {
echo_object($child, " $spacing");
}
}
}
// display entire repository folder hierarchy
echo_object($repo->get_root());
Create, Read, Update and Delete (CRUD)
Basic CRUD operations are typically performed with repository, collection and object methods.
Create
Create a new object with the repository new_object
method and object approve
method.
$president = $repo->new_object('President');
$president->Number = 46;
$president->Name = 'John Smith';
$president->approve();
A cQL INSERT can also be used to create objects, similar to an SQL INSERT.
$repo->execute("INSERT INTO President SET Number=46, Name='John Smith'");
Read
Load objects from a repository with the repository get methods.
// load a single object based on it's Content Type and ID, OID or UUID
$obj = $repo->get_object_by_id(12, 'President');
$obj = $repo->get_object_by_oid(1234);
$obj = $repo->get_object_by_uuid('79ecfd48-4849-11e6-a13a-55e3718b665d');
// load a single object
$president = $repo->get_first("FROM President WHERE ID=12");
echo("$president->Number: $president->Name\n");
// load multiple objects
$presidents = $repo->get("FROM President ORDER BY Name");
foreach($presidents as $president) {
echo("$president->Name\n");
}
Refer back to the Query repository section to review other query options.
Update
To modify an object in a repository, use the repository get methods and object approve
method.
$contact = $repo->get_first("FROM Contact WHERE ID=12");
$contact->DOB = '2001-01-01';
$contact->approve();
A cQL UPDATE can also be used to update objects, similar to an SQL UPDATE.
$repo->execute("UPDATE President SET Name='Mary Smith' WHERE Number=45");
Delete
To remove objects from a repository, use the object delete
and collection delete
methods.
// delete a single object
$contact = $repo->get_first("FROM Contact WHERE ID=1");
$contact->delete();
// delete a collection of objects
$contacts = $repo->get("FROM Contact");
$contacts->delete();
A cQL DELETE can remove objects as well. This is similar to an SQL DELETE.
$repo->execute("DELETE FROM President WHERE Number=45");
Next Steps
With the COMAND API basics understood, you're ready to learn how to build Web Apps!
Or, you can continue through the subsequent tutorials to learn more about Bases first.