Category Archives: CouchDB

Book review: CouchDB and PHP Web Development

Finally the new Book “CouchDB and PHP Web Development” written by Tim Juravich is ready an in my hands. It was my first experience as technical reviewer. The author contacted me by email and the editor sent me book chapters to review. Basically I gave my opinion, I test the code and I hunt for bugs. It was a great experience. Now is really cool to see the book in my hands.

As a general rule I don’t like the Beginner’s books. They normally fill various chapters with trivial things. Things that you can easily find within the project homepage, or you also can get thousands of articles in the Internet. Maybe the first 3 chapters are trivial things about NoSQL databases, how to set up a PHP environments, and we also can read about how to set up a github account. OK this book is not one exception, but it has something “different”. The author builds one application (a twitter clone) using PHP as backend languaje, CouchDB for the storage and Twitter’s Bootstrap for the frontend. It covers all the process from the beginning (setting up the environment) to the end (deploy the application to the cloud). The book is very interactive. It’s crowed by “Time for action” sections where the reader can build the application at the same time than he is reading. And to top it all the author also encourages the user to commit each change to one git repository.

My Conclusions
In my humble opinion is a good book, even with its trivial chapters (probably they are mandatory in this kind of books). If you read the book and you follow the all the “Time for action” sections you will have great picture about one real web application development process is and it also covers very well all CouchDB related things. Another good point for the author is the usage of newest front end technologies such as Twitter’s Bootstrap. Maybe I miss a bit the usage of TDD in the development process of the application but either way if fulfills its mission quite well.

Contact with me.
As I said before it was my first experience as a technical reviewer. It was a great experience for me. I really enjoyed a lot reading and commenting chapters. If you are writing a book and you need one reviewer, feel free to contact with me.

Working with clouds. Multi-master file-system replication with CouchDB

When we want to work with a cloud/cluster one of the most common problems is the file-system. It’s mandatory to be able to scale horizontally (scale out). We need to share the same file-system between our nodes. We can do it with a file server (samba for example), but this solution inserts a huge bottleneck into our application. There’s different distributed filesystems such as Apache Hadoop (inspired by Google’s MapReduce and Google File System). In this post we’re going to build a really simple distributed storage system based on NoSql. Let’s start.

NoSql (aka one of our last hypes) databases normally allow to store large files. MongoDB for example has GridFS, But in this post we’re going to do it using CouchDB. With CouchDB we can attach documents within our database as simple attachments, just like email.

The api of CouchDB to upload an attachment is very simple. It’s a pure REST api.

In order to create the http connections directly with curl commands we can use libraries to automate this process. For example we can use a simple library shown in a previous post. If we inspect the code we will see that we’re creating a PUT request to store the file in our couchDB database.

Another cool thing we can do with PHP is to create a stream-wrapper to use standard filesystem functions for read/write/delete we can see a post about this here.

As we can see is very easy to use couchdb as filesystem. but we also can replicate our couchDB databases. Imagine that we have tho couchDB servers (host1 and host2). Each host has one database called fs. If we run the following command:

curl -X POST -H "Content-Type: application/json" http://host1:5984/_replicate -d '{"source":"cmr","target":"http://host2:5984/fs","continuous":true}'

Our database will be replicated from host1 to host2 in a continuous mode. That’s means everytime we create/delete anything in host1, couchDB will replicate it to host2. A simple master-slave replica.

Now if we execute the same command in host2:

curl -X POST -H "Content-Type: application/json" http://host2:5984/_replicate -d '{"source":"cmr","target":"http://host1:5984/fs","continuous":true}'

We have a multi-master replica system, cheap and easy to implement. As we can see we only need to install couchDB in each node, activate the replica and that’s all. Pretty straightforward, isn’t it?

Using a stream wrapper to access CouchDb attachments with PHP

I’m still working in my filesystem with CouchDb. After creating a library to enable working with PHP and CouchDB (see the post here), and after using Monkey Patching to override standard PHP’s filesystem functions. I’ve created another solution now. Thanks to a comment in my last post (many thanks Benjamin) I’ve discovered that it’s possible to create a stream wrapper in PHP (I thought it was only available with a C extension).

It’s pretty straightforward to create the wrapper. Of course it’s only an approach. We can create more functionality to our stram wrapper but at least this example meets my needs.

namespace Nov\CouchDb\Fs;

class StreamWrapper {
    var $position;
    var $varname;
    /**
     * @var \Nov\CouchDb\Fs\Resource
     */
    var $fs;

    /**
     * @param string $path
     * @return \Nov\CouchDb\Fs
     */
    private static function _getFs($path, $_url)
    {
        return \Nov\CouchDb\Fs::factory(FSCDB, $_url['host']);
    }

    function stream_open($path, $mode, $options, &$opened_path)
    {
        $_url = parse_url($path);
        $_path = $_url['path'];
        $fs = self::_getFs($path, $_url);
        $_type = strtolower(substr($mode, 0, 1));
        switch ($_type) {
            case 'r':
                $this->fs = $fs->open($_path);
                break;
            case 'w':
                $this->fs = $fs->open($_path, true, true);
                break;
        }
        return true;
    }

    function stream_read($count=null)
    {
        if (is_null($count)) {
            return $this->fs->raw();
        } else {
            return $this->fs->raw(0, $count);
        }
    }

    function stream_write($data, $lenght=null)
    {
        if (is_null($lenght)) {
            $this->fs->write($data);
        } else {
            $this->fs->write(mb_substr($data, 0, $lenght));
        }
        return strlen($data);
    }

    public function unlink($path)
    {
        $_url = parse_url($path);
        $_path = $_url['path'];

        $fs = self::_getFs($path, $_url)->open($_path);
        $fs->delete();
    }

    public function url_stat($path , $flags)
    {
        $_url = parse_url($path);
        $_path = $_url['path'];
        $fs = self::_getFs($path, $_url)->open($_path);

        //tamaño en bytes
        $size = $fs->getLenght();
        $out[7] = $size;
        $out['size'] = $size;
        return $out;
    }

That’s very simple. It’s almost the same code than the Monkey Patching example.
And now if we want to use it:

use Nov\CouchDb;
use Nov\CouchDb\Fs;
require_once ("Nov/Loader.php");
Nov\Loader::init();
// I use FSCDB to set up the connection parameters to our couchDb database
define('FSCDB', \NovConf::CDB1);

stream_wrapper_register("novCouchDb", "Nov\CouchDb\Fs\StreamWrapper") or die("Failed to register protocol");
$file = "novCouchDb://fs/home/gonzalo/new.txt";

$f = fopen($file, 'w+');
fwrite($f, "***12345dkkydd678901");
fclose($f);

$f = fopen($file, 'r');
$a = fread($f, filesize($file));
fclose($f);

unlink($file);
echo $a;

As we can see the URI is:

novCouchDb://fs/home/gonzalo/new.txt.

That’s mean [protocol]://[database]/[path]

The behaviour of this example is exactly the same than Monkey Patching one but I preffer this one. It looks like more “clean” than Monkey Patching approach.

The full source code is available here. And the example: document_root/tests/couchdb/test7.php

Using Monkey Patching to store files into CouchDb using the standard filesystem functions with PHP

Let’s go a little further in our CouchDb storage system with PHP. I will show you why. We have some files using PHP’s filesystem functions and we want to change them to the new interface with the Nov\CouchDb\Fs library. We’ve got the following code:

// Open a new file and write a string
$f = fopen($file, 'w+');
fwrite($f, "***12345dkkydd678901");
fclose($f);

// read a file into a variable
$f = fopen($file, 'r');
$a = fread($f, filesize($file));
fclose($f);

// Delete the file
unlink($file);

We can start to rewrite the code according to the new Nov\CouchDb\Fs interface. It’s not difficult but the syntax is very different so this piece of code needs almost a full rewrite. If we’ve got this kind of code split among files, we need to rewrite a lot. And rewrite always the same code is not funny so let’s do something different.

Since PHP5.3 a new design pattern is available for us: Monkey Patching. With this pattern we can override PHP’s core functions with a home-made functions in a different namespace (another example here). That’s means if I have fopen function in the above example, PHP uses the filesystem function “fopen” but if we set a namespace in our example, PHP will search first the function within the current namespace.

Using this idea I have created the following file:

<?php
namespace Nov\CouchDb\Fs\Monkeypatch;
use Nov\CouchDb\Fs;
use Nov\CouchDb;

function unlink($filename)
{
    $fs = CouchDb\Fs::factory(FSCDB);
    $fs->delete($filename);
}
/**
 * @param string $filename
 * @param string $type
 * @return \Nov\CouchDb\Fs\Resource
 */
function fopen($filename, $type='r')
{
    $out = null;
    $fs = CouchDb\Fs::factory(FSCDB);
    $_type = strtolower(substr($type, 0, 1));
    switch ($_type) {
        case 'r':
            $out = $fs->open($filename);
            break;
        case 'w':
            $out = $fs->open($filename, true, true);
            break;
    }
	return $out;
}
function filesize($filename)
{
    $fs = CouchDb\Fs::factory(FSCDB);
    $res = $fs->open($filename);
    return $res->getLenght();
}
function fwrite(\Nov\CouchDb\Fs\Resource &$f, $data, $lenght=null)
{
    if (is_null($lenght)) {
        $out = $f->write($data);
    } else {
        $out = $f->write(mb_substr($data, 0, $lenght));
    }
    $path = $f->getPath();
    $fs = CouchDb\Fs::factory(FSCDB);
    $f = $fs->open($path);
}
function fclose(\Nov\CouchDb\Fs\Resource $f)
{
}
function fread(\Nov\CouchDb\Fs\Resource $f, $lenght=null)
{
    if (is_null($lenght)) {
        return $f->raw();
    } else {
        return $f->raw(0, $lenght);
    }
}

As you can see this file redefines some function into Nov\CouchDb\Fs\Monkeypatch namespace. That’s means if we properly include this file into our project, when we’re under this namespace this namespace PHP will go to the new function set instead to the original one. It’s something like overriding main functions at runtime.

Maybe is more clear with an example. We’ll take back the first example and add the some extra lines at the beginning:

// new code
namespace Nov\CouchDb\Fs\Monkeypatch;
include ("Nov/CouchDb/Fs/Monkeypatch.php");
require_once ("Nov/Loader.php");
\Nov\Loader::init();
define('FSCDB', \NovConf::CDB1);
// end of new code

$f = fopen($file, 'w+');
fwrite($f, "***12345dkkydd678901");
fclose($f);

// read a file into a variable
$f = fopen($file, 'r');
$a = fread($f, filesize($file));
fclose($f);

// Delete the file
unlink($file);

Another possibility is to place all include code into a separate file:

index.php:

require_once ("Nov/Loader.php");
Nov\Loader::init();

define('FSCDB', \NovConf::CDB1);
include ("Nov/CouchDb/Fs/Monkeypatch.php");

include('main.php');

And main.php:

namespace Nov\CouchDb\Fs\Monkeypatch;

// Open a new file and write a string
$f = fopen($file, 'w+');
fwrite($f, "***12345dkkydd678901");
fclose($f);

// read a file into a variable
$f = fopen($file, 'r');
$a = fread($f, filesize($file));
fclose($f);

// Delete the file
unlink($file);

And that’s all. Thanks to PHP5.3’s namespaces we can change the default behaviour of our script into a new one only setting up a namespace.

The source code can be downloaded from here. The examples of this post are are: document_root/tests/couchdb/test3.php, document_root/tests/couchdb/test5.php and document_root/tests/couchdb/test6.php

Using CouchDb as filesystem with PHP

One of the problems I need to solve in my clustered PHP applications is where to store files. When I say files I’m not speaking about source code. I’m speaking about additional data files, such as download-able pdfs, logs, etc. Those files must be on every node of the cluster. One possible approach to the solution is to use a distributed filesystem, rsync or maybe use a file-server mounted on every node. Another solution may be the usage of CouchDb. CouchDb has two great features to meet or requirements with this problem. It allows us to store files as attachments and it also allows to perform a great and very easy multi-master replica system.

The usage of CouchDB is pretty straightforward. It implements a RESTfull interface to perform every operations. So the only thing we need is a REST client. Zend Framework has a great one. We dont’t really need a library. We can easily perform REST requests with the PHP’s Curl’s extension. I’ve created two libraries for working with CouchDb one is a low-level HTTP client (with curl) and another is higher level one (it uses the HTTP Client) for CouchDB operations. You can read two post about those libraries here and here.

Now I want to extend the features of my library. I want to use CouchDB as file storage in PHP. Instead of using file functions (fopen, fwrite, fread, …) I want to use another ones and store/read files in CouchDB. For doing this I’ve refactored those two libraries into another one called Nov. I also have embraced namespaces so I will use them in the library. This means it’s only available with PHP 5.3.

Here you are a summary of the library. That’s not a complete UML graph. It’s only a diagram with the main features only with educational purpose.

summary

The best to show the library is with an example:

First I’m going to start with the basic usage of Nov\CouchDb library:

// Starting up the loader
require_once("Nov/Loader.php");
Nov\Loader::init();

use Nov\CouchDb;
$cdb = new CouchDb('localhost', 5984);
$cdb->db('users');
$nombre = $cdb->db('ris_users')->select('gonzalo')->asObject()->name;
$apellido = $cdb->db('ris_users')->select('gonzalo')->asObject()->surname;
echo "Hello {$nombre} {$apellido}.
";

To allow me the use of different CouchDb Databases and to put the Database configuration in one file. I use the following configuration class:

class NovConf
{
    const CDB1 = 'CDB1';
    const PG1  = 'PG1';

    public static $_dbs = array(
    	self::PG1  => array(
            'driver'   => 'pgsql',
            'dsn'      => 'pgsql:dbname=pg1;host=localhost',
            'username' => null
            'password' => null,
        ),
        self::CDB1  => array(
            'driver'   => 'couchdb',
            'host'     => 'localhost',
            'port'     => 5984,
            'protocol' => 'http',
            'username' => null,
            'password' => null,
        ),
    );
}

As you can see I use the same configuration file for my PDO drivers and CouchDb.

Now I can use:

require_once("Nov/Loader.php");
Nov\Loader::init();

use Nov\CouchDb;
$cdb = CouchDb::factory(NovConf::CDB1)->db('users');

try {
    $cdb->insert('xxx', array('name' => 'xxx'));
} catch (CouchDb\Exception\DupValOnIndex $e) {
    echo "Already created\n";
}

$data = $cdb->select('xxx')->asObject();
$cdb->update('xxx', array('name' => 'xxx1'));
$cdb->delete('xxx')->asObject();

And now finally the file storage part:

For storing the files I’ve taken one design decision. Every files will be stored into separate CouchDb document. That’s means one file, one document. There’s another possible approach. One CouchDb document can be one folder and store every files as attachments of this folder in the same document. But I prefer the idea of not to track folders. Only files. So each CouchDb document will have only one attachment.

That’s an example of one document in CouchDb

{
   "_id": "/home/gonzalo/aasa.txt",
   "_rev": "2-48b501a81c38fd84a3e0351917e64135",
   "path": "/home/gonzalo",
   "_attachments": {
       "aasa.txt": {
           "stub": true,
           "content_type": "application/octet-stream",
           "length": 12,
           "revpos": 2
       }
   }
}

There’s another usage script. Here we can see all the features together. We create files, update and delete them. Internally Nov\CouchDb\Fs uses a predefined CouchDb database called fs.

use Nov\CouchDb\Fs;
use Nov\CouchDb\Fs\Exception;
require_once ("Nov/Loader.php");
Nov\Loader::init();

echo "<pre>";
// create an instance from a factory method
$fs = Fs::factory(NovConf::CDB1);
// Now we're going to delete a file. If it doesn't exists will throw a FileNotFound exception
try {
    $fs->delete("/home/gonzalo/aaa.txt");
} catch (Exception\FileNotFound  $e) {
    echo $e->getMessage() . "\n";
}
// Now we are going to create a file.
// the second parameter 'true' means if the file doesn't exist will be created. Similar than 'r+'
try {
    $fs->open("/home/gonzalo/aaa.txt", true)
	->write("asasasasasas", "application/octet-stream");
} catch (Exception\FileNotFound $e) {
    echo $e->getMessage() . "\n";
} catch (Exception\WriteError $e) {
    echo $e->getMessage() . "\n";
} catch (Exception $e) {
    echo $e->getMessage() . "\n";
}
// We open the file
$res = $fs->open("/home/gonzalo/aaa.txt");

// we can get the length and the content type
echo $res->getLenght() . "\n";
echo $res->getContentType(). "\n";
// We move it to another location
$to = "/another/location";
$res->move($to);

$res = $fs->open($to);
// we flush the file to the browser
echo $res->raw();

// finally we delete it
$res->delete();
echo "</pre>";

I’ve also created an extra class to allow to dump files from filesystem to CouchDb and vice-versa.

require_once ("Nov/Loader.php");
Nov\Loader::init();
echo "<pre>";
// from filesystem to couchdb
\Nov\CouchDb\Fs\Utils::fs2cdb("/path/from/", NovConf::CDB1);
// from couchdb to filesystem
\Nov\CouchDb\Fs\Utils::cdb2fs(NovConf::CDB1, "/path/to/");
echo "</pre>";

And that’s all. You can download the source code with the examples here. The examples are under document_root/tests/couchdb/ folder. Remember you will need PHP5.3.

Clustering PHP applications. Tips and hints

Sometimes a web server and a database is fair enough to meet our project requirements. But if the project scales we probably need to think in a clustered solution. This post is an attempt at being an unsorted list of ideas working with clustered PHP applications. Maybe more than a list of ideas is a list of problems that you will face when swapping from a standalone server to a clustered server.

Source code

If you’re going to spread your code among different servers, one important fact is the source code must be always the same. You cannot assume differences in the source code. If you need to mark something related to the node, use environ variables and point your code to those environ variables instead of using different source code in each node. Perhaps changing the code is the fastest way to do the things initially but you will mess up your deploy system or at least turn it into a nightmare.

Databases

Here you are the biggest problem. Clustering databases is a hard job. All Databases normally have one kind or another of replica system but replication as way to solve problems can backfire on you. The easiest way of database replication is a master slave replica system. Inserts in one node and selects in all. This scenario can be easy to implement and not very hard to maintain but maybe you need a multi-master replica system (inserts in all) and this can turn your database administration into a nightmare. One solution is use noSql databases. They have a simple and useful multi-master replica system. My recommendation is use a noSql database for those kind of information we need to have distributed in all nodes. I know it isn’t always straightforward but all the work done in this direction will be will be worthwhile in our outcomes. In fact noSQL’ main purpose is data scalability but also there are people who thinks noSql databases are some kind of hype actually. Who is right?

File-systems

Problems again. Imagine you need to write a simple log system in your application. fopen, fwrite, a few PHP lines and your log is ready. But: Where is it located?. Probably on your local filesystem. If you want to place your application on a distributed cluster, you cannot use those simple functions. You must consider using a distributed filesystem. Maybe a simple rsync would work. Maybe a single fileserver mounted on every node of your cluster. You need to balance between them and choose the solution that fits to your requirements. One smart and simple solution is benefit from CouchDB’s attachments in addition to its replication system. (can you feel I like CouchDb?)

Deploy

That’s a mandatory point. You must have an effective deploy system. You can’t rely on you to deploy the code on the among the cluster by hand. This work must be automated. PHING, rsync, pull/push actions in a distributed revision control (e.g. mercurial) or maybe a simple bash script can be enough. But never do it by hand.

Authentication and Authorization

If your application is protected with any authentication mechanism you must ensure all nodes will be able to authenticate against the same database. A good pattern is to work with an external authentication mechanism such as OAuth in a separate server. But if it’s not possible for you must consider where is located user/password database. CouchDB is good solution.

After your user is authenticated you need no check the authorization. OAuth servers can help you on authentication but your application must deal with authorization (aka user roles and role groups) anyway. My best choice here is CouchDb but you can use relational databases too. As I said in Databases section I avoid multi-master Database Replication with relational databases like the plage, but if you need to use relational replication in the authorization database you can use master-slave replica and update all role and authorizations in the master database.

Sessions, Logs and Cache files

Witch session handler do you use in your application?

File based (go to filesystem section of this post),  Database (go to database section of this post).

The same with cache and logs. With cache files you can consider keep it locally. If your cache system works unattended you can maintain the cache in local node instead to replicate it among the cluster. You can save bandwidth.

Playing with PHP, CouchDB and ORM

I’m playing with PHP and CouchDB. After writing a class to connect PHP and CouchDB I’ve done a variation of the interface to use CouchDB with PHP in a similar way to ORM. Yes. I know ORM means Object Relational Mapping and CouchDB isn’t a Relational Database but have a look to the following interface:

Imagine you have a CouchDb at localhost:5987 and you have a database called users.

Now let’s build a class called CDB_Users.

class CDB_Users extends Nov_CouchDb_Orm {
    static $_db = 'users';
}

It’s easy to automate the creation of CDB_Users with a script

CDB_Users extends Nov_CouchDb_Orm

class Nov_CouchDb_Orm
{
    static protected $_db;

    /**
     * @param string $key
     * @return Nov_CouchDb
     */
    static public function connect($key)
    {
        $class = get_called_class();
        extract(Nov_CouchDb_Conf::get($key));

        $couchDb = new Nov_CouchDb($host, $port, $protocol, $user, $password);
        return $couchDb->db($class::$_db);
    }
}

A configuration class:

class Nov_CouchDb_Conf
{
    const CDB1 = 'CDB1';

    private static $_conf = array(
        self::CDB1 => array(
            'protocol' => 'http',
            'host'     => 'localhost',
            'port'     => 5984,
            'user'     => null,
            'password' => null
            )
        );

    static function get($key)
    {
        return self::$_conf[$key];
    }
}

Putting all together with the class Nov_CouchDb
we can use:

CDB_Users::connect(Nov_CouchDb_Conf::CDB1)->insert('gonzalo', array('password' => 'g1'));
$data = CDB_Users::connect(Nov_CouchDb_Conf::CDB1)->select('gonzalo')->asArray();
print_r($data);

CDB_Users::connect(Nov_CouchDb_Conf::CDB1)->update('gonzalo', array('password' => 'g2'));
$data = CDB_Users::connect(Nov_CouchDb_Conf::CDB1)->select('gonzalo')->asArray();
print_r($data);

CDB_Users::connect(Nov_CouchDb_Conf::CDB1)->delete('gonzalo');

What do you think? That’s not exactly a ORM. I’m not sure if I will use that interface instead the classic one.

$data = CDB_Users::connect(Nov_CouchDb_Conf::CDB1)->select('gonzalo')->asArray();
// versus
$couchDb = new Nov_CouchDb('localhost', 5984);
$data = $couchDb->db('users')->select('gonzalo')->asArray()
 

Here is the full source code with the examples.

PHP and couchDB

Last days I’ve been playing with couchDB and PHP. There are several ways to use couchDB with PHP. There also are extensions but I want to build a simple library and I will show now the code.

CouchDB has a great RestFul API so I we want to use CouchDB we only need to perform http requests. I have done a small class to do the requests. You can see the code here. It’s a simple class that uses PHP’s curl functions.

I come from relational database world. NoSQL is new for me. Maybe I’m wrong but I want to use INSERT, UPDATE, DELETE and SELECT statements in CouchDB in the same way I use them in Relational database.

The class is focused in the HTTP Document API. There is a great tutorial here that explains the API. Now I’ll show the interface I’ve made to perform the statements with CouchDB.

The examples assumes there are CouchDB host at localhost:5987. Also as I’ve said before the class focused only in HTTP Document API so first of all I create a new database called users using CouchDB’s web interface (http://localhost:5984/_utils/). Now let’s start:

INSERT

$couchDb = new Nov_CouchDb('localhost', 5984);
$couchDb->db('users')->insert('gonzalo', array('password' => "g1"));

SELECT

$data = $couchDb->db('users')->select('gonzalo')->asArray();
print_r($data);

UPDATE

$couchDb->db('users')->update('gonzalo', array('password' => 'g2'));

DELETE

$out = $couchDb->db('users')->delete('gonzalo')->asArray();
print_r($out);

Basically Nov_CouchDb has a constructor that stores connection information into private member variables:

class Nov_CouchDb
{
    private $_protocol;
    private $_host;
    private $_port;
    private $_user;
    private $_password;

    public function __construct($host, $port=Nov_Http::DEF_PORT , $protocol=Nov_Http::HTTP, $user = null, $password=null)
    {
        $this->_host     = $host;
        $this->_port     = $port;
        $this->_protocol = $protocol;
        $this->_user     = $user;
        $this->_password = $password;
    }
}

There are also a public function called db. This function stores the database and returns the instance of the class (I like chainable functions)

...
    private $_db;
    /**
     * @param string $db
     * @return Nov_CouchDb
     */
    public function db($db)
    {
        $this->_db = $db;
        return $this;
    }
...

And finally the main functions:

SELECT

public function select($key)
{
    try {
        $out = Nov_Http::connect($this->_host, $this->_port, $this->_protocol)
            ->setCredentials($this->_user, $this->_password)
            ->doGet("{$this->_db}/{$key}");
    } catch (Nov_Http_Exception $e) {
        $this->_manageExceptions($e);
    }
    return new Nov_CouchDb_Resulset($out);
}

INSERT

    /**
     * @param string $key
     * @param array $values
     * @return Nov_CouchDb_Resulset
     */
    public function insert($key, $values)
    {
        try {
            $out = Nov_Http::connect($this->_host, $this->_port, $this->_protocol)
                ->setCredentials($this->_user, $this->_password)
                ->setHeaders(array('Content-Type' =>  'application/json'))
                ->doPut("{$this->_db}/{$key}", json_encode($values));
        } catch (Nov_Http_Exception $e) {
            $this->_manageExceptions($e);
        }
        return new Nov_CouchDb_Resulset($out);
    }

UPDATE

    /**
     * @param string $key
     * @param array $values
     * @return Nov_CouchDb_Resulset
     */
    public function update($key, $values)
    {
        try {
            $http = Nov_Http::connect($this->_host, $this->_port, $this->_protocol)
                ->setCredentials($this->_user, $this->_password);
            $out = $http->doGet("{$this->_db}/{$key}");
            $reg = json_decode($out);
            $out = $http->setHeaders(array('Content-Type' =>  'application/json'))
                ->doPut("{$this->_db}/{$key}", json_encode($reg));
        } catch (Nov_Http_Exception $e) {
            $this->_manageExceptions($e);
        }
        return new Nov_CouchDb_Resulset($out);
    }

DELETE

    /**
     * @param string $key
     * @return Nov_CouchDb_Resulset
     */
    public function delete($key)
    {
        try {
            $http = Nov_Http::connect($this->_host, $this->_port, $this->_protocol)
                ->setCredentials($this->_user, $this->_password);
            $out = $http->doGet("{$this->_db}/{$key}");
            $reg = json_decode($out);
            $out = $http->doDelete("{$this->_db}/{$key}", array('rev' => $reg->_rev));
        } catch (Nov_Http_Exception $e) {
            $this->_manageExceptions($e);
        }
        return new Nov_CouchDb_Resulset($out);
    }

insert, update, delete and select returns an instance of Nov_CouchDb_Resulset. CouchDb API returns a json string. But sometimes I want a PHP array or maybe an object. I use Nov_CouchDb_Resulset to perform those transformations:

// Different outputs
$data = $couchDb->db('users')->select('dummy')->asArray();
print_r($data);

$data = $couchDb->db('users')->select('dummy')->asObject();
print_r($data);

$data = $couchDb->db('users')->select('dummy')->asJson();
print_r($data);

EXCEPTIONS

CouuchDB API is a RestFul API so it uses HTTP exceptions when something wrong happens. I’ve done three exceptions. One generic and other two with the most common exception in relational databases: NoDataFound and DupValOnIndex. this is because I want do things like the following ones:

try {
    $couchDb->db('users')->update('gonzalo', array('password' => 'g2'));
} catch (Nov_CouchDb_Exception_NoDataFound $e) {
    echo "No data found \n";
}

$couchDb->db('users')->insert('gonzalo1', array('password' => "g1"));
try {
    $couchDb->db('users')->insert('gonzalo1', array('password' => "g1"));
} catch (Nov_CouchDb_Exception_DupValOnIndex $e) {
    echo "Dup Val On Index \n";
}
try {
    $couchDb->db('users')->delete('gonzalo');
} catch (Nov_CouchDb_Exception_NoDataFound $e) {
    echo "No data found \n";
}

The source code is available on google code here

Follow

Get every new post delivered to your Inbox.

Join 963 other followers