Installing Java JRE & JDK

Ubuntu 12 users should follow the guide from Digital Ocean
https://www.digitalocean.com/community/articles/how-to-install-java-on-ubuntu-with-apt-get

Mac OS X users should have Java already installed.
http://stackoverflow.com/questions/14292698/how-to-check-if-java-jdk-installed-on-mac

Windows 8 should have Java 7 pre-installed; feel free to updgrade to Java 8.

Downloading Datomic Free Edition

Get the latest version of Datomic Free Edition https://my.datomic.com/downloads/free
Once downloaded create a new directory/folder for it and unzip the file.

PHP >= 5.4

At the very minimum you will need to have PHP version 5.4.* installed on your local machine.

Ubuntu users follow this guide
http://askubuntu.com/questions/109404/how-do-i-install-latest-php-in-supported-ubuntu-versions-like-5-4-x-in-ubuntu-1

Mac OS X users need to look at the Homebrew support for PHP.
https://github.com/Homebrew/homebrew-php

Windows users who are familiar with IIS; which in my opinion is the best way to run PHP on Windows.
http://windows.php.net/download/

Get Composer

Composer is needed; the exact steps will be listed later but for now just be familiar with how it works.
https://getcomposer.org/doc/00-intro.md

Launching The Datomic REST API

The official instructions can be found on the Datomic documentation page http://docs.datomic.com/rest.html. For now open your terminal application and change into the directory where you unzipped Datomic Free and then start the REST service.

$ cd {folder_where_you_unzipped_datomic}
$ ./bin/rest -p 9998 tutorial datomic:mem://

Open your favorite text-editor and create a new project folder; name it patomic_tutorial. Create a file within the patomic_tutorial directory called composer.json.

{
    "require": {
        "taywils/patomic": "dev-master",
        "igorw/edn": "1.0.*@dev",
        "nikic/phlexy": "1.0.*@dev"
    }
}

Creating A Database

Double check that you have the Datomic REST service running by visiting http://localhost:9998/. If everything is fine and well you should be greeted with the basic page which includes a description and two links. Now click or copy paste the data url into your browser http://localhost:9998/data/. The Data page is where our storages are located; remember the "tutorial" storage we created when we launched the REST service? To use the "tutorial" storage click the link or copy paste the url into your browser http://localhost:9998/data/tutorial/. The REST API provides some useful tools and a decent interface but for the purposes of demonstrating Patomic lets get back to PHP. For more information on using the REST API provided by Datomic http://docs.datomic.com/rest.html. Reopen your terminal application and run composer.phar to install the dependencies.

$ cd {folder_where_you_unzipped_datomic}
$ curl -sS https://getcomposer.org/installer | php
$ sudo php composer.phar install
$ sudo php composer.phar update

If successful you will see confirmation that the composer packages were correctly installed; now create a new file called main.php.

<?php

require_once __DIR__ . "/vendor/autoload.php";

use \taywils\Patomic\Patomic;

try {
    $patomic = new Patomic("http://localhost", 9998, "mem", "tutorial");
    $patomic->createDatabase("seattle");
} catch(PatomicException $patomicException) {
    echo $patomicException->getMessage();
}
Verify that the database was in fact created by visiting http://localhost:9998/data/tutorial/seattle/-/

Adding A Schema

Next we're going to demonstrate how to load a schema from an existing .edn file. EDN is short for Extensible Data Notation and is the default format for Datomic schemas. Create a new file called seattle-schema.edn and copy paste the EDN provided.

[
 ;; community
 {:db/id #db/id[:db.part/db]
  :db/ident :community/name
  :db/valueType :db.type/string
  :db/cardinality :db.cardinality/one
  :db/fulltext true
  :db/doc "A community's name"
  :db.install/_attribute :db.part/db}

 {:db/id #db/id[:db.part/db]
  :db/ident :community/url
  :db/valueType :db.type/string
  :db/cardinality :db.cardinality/one
  :db/doc "A community's url"
  :db.install/_attribute :db.part/db}

 {:db/id #db/id[:db.part/db]
  :db/ident :community/neighborhood
  :db/valueType :db.type/ref
  :db/cardinality :db.cardinality/one
  :db/doc "A community's neighborhood"
  :db.install/_attribute :db.part/db}

 {:db/id #db/id[:db.part/db]
  :db/ident :community/category
  :db/valueType :db.type/string
  :db/cardinality :db.cardinality/many
  :db/fulltext true
  :db/doc "All community categories"
  :db.install/_attribute :db.part/db}

 {:db/id #db/id[:db.part/db]
  :db/ident :community/orgtype
  :db/valueType :db.type/ref
  :db/cardinality :db.cardinality/one
  :db/doc "A community orgtype enum value"
  :db.install/_attribute :db.part/db}

 {:db/id #db/id[:db.part/db]
  :db/ident :community/type
  :db/valueType :db.type/ref
  :db/cardinality :db.cardinality/one
  :db/doc "A community type enum value"
  :db.install/_attribute :db.part/db}

 ;; community/org-type enum values
 [:db/add #db/id[:db.part/user] :db/ident :community.orgtype/community]
 [:db/add #db/id[:db.part/user] :db/ident :community.orgtype/commercial]
 [:db/add #db/id[:db.part/user] :db/ident :community.orgtype/nonprofit]
 [:db/add #db/id[:db.part/user] :db/ident :community.orgtype/personal]

 ;; community/type enum values
 [:db/add #db/id[:db.part/user] :db/ident :community.type/email-list]
 [:db/add #db/id[:db.part/user] :db/ident :community.type/twitter]
 [:db/add #db/id[:db.part/user] :db/ident :community.type/facebook-page]
 [:db/add #db/id[:db.part/user] :db/ident :community.type/blog]
 [:db/add #db/id[:db.part/user] :db/ident :community.type/website]
 [:db/add #db/id[:db.part/user] :db/ident :community.type/wiki]
 [:db/add #db/id[:db.part/user] :db/ident :community.type/myspace]
 [:db/add #db/id[:db.part/user] :db/ident :community.type/ning]

 ;; neighborhood
 {:db/id #db/id[:db.part/db]
  :db/ident :neighborhood/name
  :db/valueType :db.type/string
  :db/cardinality :db.cardinality/one
  :db/unique :db.unique/identity
  :db/doc "A unique neighborhood name (upsertable)"
  :db.install/_attribute :db.part/db}

 {:db/id #db/id[:db.part/db]
  :db/ident :neighborhood/district
  :db/valueType :db.type/ref
  :db/cardinality :db.cardinality/one
  :db/doc "A neighborhood's district"
  :db.install/_attribute :db.part/db}

 ;; district
 {:db/id #db/id[:db.part/db]
  :db/ident :district/name
  :db/valueType :db.type/string
  :db/cardinality :db.cardinality/one
  :db/unique :db.unique/identity
  :db/doc "A unique district name (upsertable)"
  :db.install/_attribute :db.part/db}

 {:db/id #db/id[:db.part/db]
  :db/ident :district/region
  :db/valueType :db.type/ref
  :db/cardinality :db.cardinality/one
  :db/doc "A district region enum value"
  :db.install/_attribute :db.part/db}

 ;; district/region enum values
 [:db/add #db/id[:db.part/user] :db/ident :region/n]
 [:db/add #db/id[:db.part/user] :db/ident :region/ne]
 [:db/add #db/id[:db.part/user] :db/ident :region/e]
 [:db/add #db/id[:db.part/user] :db/ident :region/se]
 [:db/add #db/id[:db.part/user] :db/ident :region/s]
 [:db/add #db/id[:db.part/user] :db/ident :region/sw]
 [:db/add #db/id[:db.part/user] :db/ident :region/w]
 [:db/add #db/id[:db.part/user] :db/ident :region/nw]
 ]
Open main.php and create a new PatomicTransaction object, the transaction object has a method for loading .edn files.
For demonstration purposes we will be printing the transaction response from the REST API to the console.
<?php

require_once __DIR__ . "/vendor/autoload.php";

use \taywils\Patomic\Patomic;
use \taywils\Patomic\PatomicTransaction;

try {
  $patomic = new Patomic("http://localhost", 9998, "mem", "tutorial");
  $patomic->createDatabase("seattle");
  $patomic->setDatabase("seattle");

  $patomicTransaction = new PatomicTransaction();
  $patomicTransaction->loadFromFile(__DIR__ . DIRECTORY_SEPARATOR . "seattle-schema.edn");

  $patomic->commitTransaction($patomicTransaction);
  echo $patomic->getTransactionResponse();
} catch(PatomicException $patomicException) {
  echo $patomicException->getMessage();
}
				

Loading Sample Data

Similar to how we loaded the schema file lets demonstrate how to load an existing .edn file consisting of data. Create a new file called seattle-data0.edn and copy paste the following edn data into it from the file given in the link https://github.com/taywils/patomic/blob/master/tests/seattle-data0.edn.

Open the main.php file and add the code to load the data for our schema.
<?php

require_once __DIR__ . "/vendor/autoload.php";

use \taywils\Patomic\Patomic;
use \taywils\Patomic\PatomicTransaction;

try {
  $patomic = new Patomic("http://localhost", 9998, "mem", "tutorial");
  $patomic->createDatabase("se");
  $patomic->setDatabase("se");

  $patomicTransaction = new PatomicTransaction();
  $patomicTransaction->loadFromFile(__DIR__ . DIRECTORY_SEPARATOR . "seattle-schema.edn");

  $patomic->commitTransaction($patomicTransaction);
  echo $patomic->getTransactionResponse();

  $patomicTransaction2 = new PatomicTransaction();
  $patomicTransaction2->loadFromFile(__DIR__ . DIRECTORY_SEPARATOR . "seattle-data0.edn");

  $patomic->commitTransaction($patomicTransaction2);
  echo $patomic->getTransactionResponse();
} catch(PatomicException $patomicException) {
  echo $patomicException->getMessage();
}

Once again we're echoing out the PatomicTransaction response to the console just to show that the transaction sent was accepted by Datomic. In the next section we'll build some simple queries using the PatomicQuery class.

Basic Queries

The first type of basic query(basic in that no special Datomic knowledge is needed) we'll use is the ability to write raw Datomic queries interpreted from strings. If you already know how to construct Datomic queries then this is the by far the best approach; to do so see the below function as an example.

function rawQueryExample($patomic) {
  $patomicQuery = new PatomicQuery();
  $patomicQuery->newRawQuery(
    "[:find ?neighborhood :in $ :where [?entityId :neighborhood/name ?neighborhood]]"
  )->addRawQueryArgs("[{:db/alias tutorial/seattledb}]");

  $patomic->commitRawQuery($patomicQuery);

  $neighborhoodNames = $patomic->getQueryResult();

  foreach($neighborhoodNames as $row) {
    echo $row[0] . PHP_EOL;
  }
}

Raw queries benefit the programmer already familiar with Datomic query syntax however, for the PHP programmer new to Datomic there is a more user-friendly yet less powerful way to generate queries. In this next example we'll re-create the Seattle neighborhood query from a different approach using the PatomicQuery methods. The PatomicQuery class was designed to have methods similar to the symbols used within a Datomic raw query such as :find and :where.

function regularQueryExample($patomic) {
  $patomicQuery = new PatomicQuery();
  $patomicQuery->find("neighborhood")
    ->where(array("entityId" => "neighborhood/name", "neighborhood"));

  $patomic->commitRegularQuery($patomicQuery);

  $neighborhoodNames = $patomic->getQueryResult();

  foreach($neighborhoodNames as $row) {
    echo $row["neighborhood"] . PHP_EOL;
  }
}

Transactions

Transactions are created using the PatomicTransaction class and allow us to create parts of an EDN schema or add new enities to our existing schema definitions. For example if we wanted to create the following attribute for our community schema which will allow us to define a nickname.

[
 {:db/id #db/id[:db.part/db]
  :db/ident :community/nickname
  :db/valueType :db.type/string
  :db/cardinality :db.cardinality/one
  :db/fulltext true
  :db/doc "A community's nickname"
  :db.install/_attribute :db.part/db}
]

The first thing we would need is to create a new entity representing the community nickname and then add the entity as a new attribute by sending it to our database within a transaction. As part of the design of Patomic the methods available to the PatomicEntity class resemble the schema attributes defined by Datomic. In fact we could have created the entire seattle-schema.edn file using Patomic but for demonstration purposes it was much easier to showcase the ability to load existing .edn files so programmers who are currently using Datomic can import their schemas into a PHP application.

function createNickNameEntity($patomic) {
  $pe = new PatomicEntity();
  $pe->ident("community", "nickname")
    ->valueType("string")
    ->cardinality("one")
    ->fullText(true)
    ->doc("A community's nickname")
    ->install("attribute");

  $pe->prettyPrint();

  echo PHP_EOL;

  $pt = new PatomicTransaction();
  $pt->append($pe);

  $patomic->commitTransaction($pt);

  echo $patomic->getTransactionResponse();
}

So far we've given a brief outline of Patomic's functionality but how do we go about creating a small application from the ground up showing how we can use Datomic as a backend for a API written in PHP?

Collected below is the code used in all the examples shown so far this overview.

<?php

require_once __DIR__ . "/vendor/autoload.php";

use \taywils\Patomic\Patomic;
use \taywils\Patomic\PatomicTransaction;
use \taywils\Patomic\PatomicQuery;
use \taywils\Patomic\PatomicEntity;

function createSchemaLoadData($patomic) {
  $patomicTransaction = new PatomicTransaction();
  $patomicTransaction->loadFromFile(__DIR__ . DIRECTORY_SEPARATOR . "seattle-schema.edn");

  $patomic->commitTransaction($patomicTransaction);

  $patomicTransaction2 = new PatomicTransaction();
  $patomicTransaction2->loadFromFile(__DIR__ . DIRECTORY_SEPARATOR . "seattle-data0.edn");

  $patomic->commitTransaction($patomicTransaction2);
}

function rawQueryExample($patomic) {
  $patomicQuery = new PatomicQuery();
  $patomicQuery->newRawQuery(
  "[:find ?neighborhood :in $ :where [?entityId :neighborhood/name ?neighborhood]]"
  )->addRawQueryArgs("[{:db/alias tutorial/seattledb}]");

  $patomic->commitRawQuery($patomicQuery);

  $neighborhoodNames = $patomic->getQueryResult();

  foreach($neighborhoodNames as $row) {
    echo $row[0] . PHP_EOL;
  }
}

function regularQueryExample($patomic) {
  $patomicQuery = new PatomicQuery();
  $patomicQuery->find("neighborhood")
    ->where(array("entityId" => "neighborhood/name", "neighborhood"));

  $patomic->commitRegularQuery($patomicQuery);

  $neighborhoodNames = $patomic->getQueryResult();

  foreach($neighborhoodNames as $row) {
    echo $row["neighborhood"] . PHP_EOL;
  }
}

function createNickNameEntity($patomic) {
  $pe = new PatomicEntity();
  $pe->ident("community", "nickname")
    ->valueType("string")
    ->cardinality("one")
    ->fullText(true)
    ->doc("A community's nickname")
    ->install("attribute");

  $pe->prettyPrint();

  echo PHP_EOL;

  $pt = new PatomicTransaction();
  $pt->append($pe);

  $patomic->commitTransaction($pt);

  echo $patomic->getTransactionResponse();
}

try {
  $patomic = new Patomic("http://localhost", 9998, "mem", "tutorial");
  $patomic->createDatabase("seattledb");
  $patomic->setDatabase("seattledb");

  //createSchemaLoadData($patomic);
  //rawQueryExample($patomic);
  //regularQueryExample($patomic);
  //createNickNameEntity($patomic);
} catch(PatomicException $patomicException) {
  echo $patomicException->getMessage();
}