CouchDB for PHP developers – CRUD

Apache CouchDB™ commonly referred to as just CouchDB is an open source NoSQL database that uses JSON for storing data in form of documents, JavaScript as its query language using MapReduce queries, and regular HTTP for an API.
There is an excellent, free book called CouchDB The Definitive Guide available online at http://guide.couchdb.org. If you haven’t read it, I strongly advise doing so.

Working with JavaScript Object Notation (aka JSON) in PHP is a breeze. The best thing about JSON in PHP is that there is no required installation of external libraries needed, as JSON support is part of the PHP core:

  • json_decode – Decodes a JSON string
  • json_encode – Returns the JSON representation of a value

When it comes to API communication through HTTP, you are also pretty well covered here. PHP supports libcurl, a library that allows you to connect and communicate to many different types of servers with many different types of protocols. Among other protocols, libcurl supports supports http and https. cURL functions is probably more familiar term for this. Although this is such a frequently used library, some servers might not have it installed, so be sure to check if cURL is installed (for example, function_exists(‘curl_version’)).

The Futon

Another great thing about CouchDB is that it comes with a native web-based interface built into it, sort of like phpMyAdmin for MySQL. This interface is called Futon. It provides a basic interface to the majority of the functionality, including the ability to create, update, delete and view documents and views. Reason why we are bringing this up, is to that you can check the results of all of the API calls we have run so far. For more information on Futon itself, check out the official documentation page http://docs.couchdb.org/en/latest/intro.html#using-futon.

You should be able to access Futon via the following URL http://127.0.0.1:5984/_utils/.

So now that we have a support JSON handling, HTTP communication and a interface to check our results, we can move on with some practical examples.

Note: In most of the following examples, I am assuming that you used the Futon to set the username/password on a database. If you haven’t, you can comment out the line of code with CURLOPT_USERPWD.

Now, lets jump on to some practical examples.

Check if CouchDB is running

You can check if CouchDB is running just by executing HTTP GET request at http://127.0.0.1:5984 URL. If all is OK, you should be able to see a “Welcome” message as a part of larger JSON object.

<?php
 
$ch = curl_init();
 
curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:5984/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-type: application/json',
'Accept: */*'
));
 
$response = curl_exec($ch);
 
curl_close($ch);
 
/* JSON string
{
"couchdb": "Welcome",
"uuid": "fd53dde57b4f2ead04267c54139358a7",
"version": "1.3.1",
"vendor": {
"version": "1.3.1",
"name": "The Apache Software Foundation"
}
}
*/

Get a list of databases

Try not to get confused with terminology “CouchDB” vs “databases”. CouchDB is a database management system (DMS), which means it can hold multiple databases. A database is a bucket that holds “related data”, such as products.

<?php
 
$ch = curl_init();
 
curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:5984/_all_dbs');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-type: application/json',
'Accept: */*'
));
 
$response = curl_exec($ch);
 
curl_close($ch);
 
/*
string(36) "["_replicator","_users","products"] "
*/

Create a database

In this example we will create an empty database called customers. For this, we need to use HTTP PUT request.

<?php
 
$table = ‘customers’;
$ch = curl_init();
 
curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:5984/'.$table);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, 'myDBusername:myDBpass');
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-type: application/json',
'Accept: */*'
));
 
$response = curl_exec($ch);
 
curl_close($ch);
 
/*
string(12) "{"ok":true} "
*/

Get an UUID from CouchDB

If you find it impractical to generate your own UUID’s, you can ask CouchDB to give you one. If you need more than one UUID, you can pass in the ?count=5 HTTP parameter to request 5 UUIDs, or any other number you need.

<?php
 
$ch = curl_init();
 
curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:5984/_uuids');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-type: application/json',
'Accept: */*'
));
 
curl_setopt($ch, CURLOPT_USERPWD, 'myDBusername:myDBpass');
 
$response = curl_exec($ch);
$_response = json_decode($response, true);
 
$UUID = $_response['uuids'];
 
curl_close($ch);
 
/*
$UUID: string(32) "c2449e8c6cb8cb938ee507e281003348"
*/

Create a document

To create new document you can either use a POST operation or a PUT operation.

In this example we are creating a document (an entry, a row, a record) within the customers database.

Each document in CouchDB has an ID. This ID is unique per database. You are free to choose any string to be the ID, although CouchDB recommends a UUID (or GUID). In the example above I showed you how to fetch the UUID from CouchDB itself. Since ID is a required parameter that needs to be passed with create a document request, we can either:
request it from CouchDB
use some other unique string for it.
For our customers table, we will use username field for ID as shown in the example below. Please note that this is not the best decision, as it is recommended to use the UUID for ID. However, for the sake of simplicity and easier overview we will use username.

<?php
 
$ch = curl_init();
 
$customer = array(
'firstname' => 'Branko',
'lastname' => 'Ajzele',
'username' => 'ajzele',
'email' => 'branko.ajzele@example.com',
'pass' => md5('myPass123')
);
 
$payload = json_encode($customer);
 
curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:5984/customers/'.$customer['username']);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); /* or PUT */
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-type: application/json',
'Accept: */*'
));
 
curl_setopt($ch, CURLOPT_USERPWD, 'myDBusername:myDBpass');
 
$response = curl_exec($ch);
 
curl_close($ch);
 
/*
string(69) "{"ok":true,"id":"ajzele","rev":"1-c3fde3a56fe3c3490448a8e34166b4ec"} "
*/

Get a document

To retrieve a document, simply perform a GET operation at the document’s URL like shown below. Here we are using $documentID = ‘ajzele’; to fetch the document we just created in previous example.

<?php
 
$ch = curl_init();
 
$documentID = 'ajzele';
 
curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:5984/customers/'.$documentID);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-type: application/json',
'Accept: */*'
));
 
curl_setopt($ch, CURLOPT_USERPWD, 'myDBusername:myDBpass');
 
$response = curl_exec($ch);
 
curl_close($ch);
 
/*
string(200) "{"_id":"ajzele","_rev":"1-c3fde3a56fe3c3490448a8e34166b4ec","firstname":"Branko","lastname":"Ajzele","username":"ajzele","email":"branko.ajzele@example.com","pass":"433484b5317340f5c28e085bfffc78be"} "
*/

Update existing document

Note the _rev value 1-c3fde3a56fe3c3490448a8e34166b4ec in Create a document example. Prefix 1 means first revision. If you try to execute the same create a document request multiple times, you would get Document update conflict. error message. This is because CouchDB sees your request as update. If you want to update or delete a document, CouchDB expects you to include the _rev field of the revision you wish to change. When CouchDB accepts the change, it will generate a new revision number.

In order to update our previously create document, we need to pass it the _rev number we got from CouchDB when we created the document. Below is an example of such request, note how it is almost identical.

<?php
 
$ch = curl_init();
 
$customer = array(
'firstname' => 'Branko_2',
'lastname' => 'Ajzele_2',
'username' => 'ajzele',
'email' => 'branko.ajzele@example.com',
'pass' => md5('myPass123')
);
 
$customer['_rev'] = '1-c3fde3a56fe3c3490448a8e34166b4ec';
 
$payload = json_encode($customer);
 
curl_setopt($ch, CURLOPT_URL, 'http://127.0.0.1:5984/customers/'.$customer['username']);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); /* or PUT */
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-type: application/json',
'Accept: */*'
));
 
curl_setopt($ch, CURLOPT_USERPWD, 'myDBusername:myDBpass');
 
$response = curl_exec($ch);
 
curl_close($ch);
 
/*
string(69) "{"ok":true,"id":"ajzele","rev":"2-6a7e68954964a35d9415eb10142d17fc"} "
*/

Creating a document with attachment

CouchDB documents can have attachments. They can be any data (pdf, image, music, video…). It is important to know that attachments are added only to an existing documents. Process of adding an attachment is considered a document update. Since each document update requires a revision number, so does the process of adding attachment.

<?php
 
$ch = curl_init();
 
$database = 'customers';
$documentID = 'ajzele';
$revision = '2-6a7e68954964a35d9415eb10142d17fc';
 
$attachment = 'inkscape-0.48.4-1-win32.exe';
$repository = 'C:/Users/branko/Downloads/';
 
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$contentType = finfo_file($finfo, $repository.$attachment);
 
$payload = file_get_contents($repository.$attachment);
 
curl_setopt($ch, CURLOPT_URL, sprintf('http://127.0.0.1:5984/%s/%s/%s?rev=%s', $database, $documentID, $attachment, $revision));
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
//curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-type: '.$contentType,
'Accept: */*'
));
 
curl_setopt($ch, CURLOPT_USERPWD, 'myDBusername:myDBpass');
 
$response = curl_exec($ch);
 
curl_close($ch);
 
/*
string(69) "{"ok":true,"id":"ajzele","rev":"3-4b53bf95181e2461cc4b417507cf1e45"} "
*/

Attachments are stored under the root _attachments key of a JSON document, in a form like shown below.

"_attachments": {
"inkscape-0.48.4-1-win32.exe": {
"content_type": "application/x-dosexec",
"revpos": 5,
"digest": "md5-4lZleB3ePMxCaF7QraQEQg==",
"length": 34702513,
"stub": true
},
"Magento_Community_1-7_User_Guide.zip": {
"content_type": "application/zip",
"revpos": 3,
"digest": "md5-j9E1m4ejrc7LtlRuKgq8cA==",
"length": 9034796,
"stub": true
}
}

Delete a document

To delete a document you need to perform a HTTP DELETE operation at the document’s location, passing the rev parameter with the document’s current revision.

<?php
 
$ch = curl_init();
 
$database = 'customers';
$documentID = 'ajzele';
$revision = '7-6f74f3f827fa594ef7bf3293490e5d79';
 
curl_setopt($ch, CURLOPT_URL, sprintf('http://127.0.0.1:5984/%s/%s?rev=%s', $database, $documentID, $revision));
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-type: application/json',
'Accept: */*'
));
 
curl_setopt($ch, CURLOPT_USERPWD, 'myDBusername:myDBpass');
 
$response = curl_exec($ch);
 
curl_close($ch);
/*
string(69) "{"ok":true,"id":"ajzele","rev":"8-80af68b3814813ebc57bb70e73b8524a"} "
*/

Interesting enough that the DELETE action, besides just deleting the document from database, also returns the increased revision number.

Summary

In this article we covered several basic but essential CRUD use cases, based on which you can start building your own application. Even doe there are already several PHP libraries out there available for working with CouchDB, we focused on doing the same with basic cURL as this is something every PHP developer should know how to do.
The next follow up article CouchDB for PHP developers – Searching/Querying will cover CouchDB Map/Reduce functions that we will use for Searching/Querying the database.