A newer version of this software is available
You are viewing the documentation for an older version of this software. To find the documentation for the current version, visit the Couchbase documentation home page.
This guide provides information for developers who want to use the Couchbase PHP SDK to build applications that use Couchbase Server.
We will use PHP and access the Couchbase server as part of this exercise; the Couchbase Server can be part of a cluster or it can be stand-alone. This exercise will show you how to:
Download, install, and configure the Couchbase PHP SDK.
Use Couchbase and PHP to generate a general page access counter and last-accessed date indicator script.
This section assumes that you have the following items set up for your development environment:
PHP 5.3 or later installed. For more information, see the PHP Manual.
Web server installed and configured to serve PHP pages.
Couchbase Server 2.0 installed and available from your development environment. For more information, see Couchbase Downloads.
This section also assumes you have downloaded and set up a compatible version of Couchbase Server and have at least one instance of Couchbase Server and one data bucket established. If you need to set up these items, you can do that by using the Couchbase Web Console, the Couchbase Command-Line Interface (CLI), or the Couchbase REST API. For information and instructions, see:
Using the Couchbase Web Console for information about using the Couchbase Administrative Console.
Couchbase CLI for information about the command line interface.
Couchbase REST API for information about creating and managing Couchbase resources.
The TCP/IP port allocation on Windows by default includes a restricted number of ports available for client communication. For more information about adjusting the configuration and increasing the number of available ports, see MSDN: Avoiding TCP/IP Port Exhaustion. After you have your Couchbase Server set up and you have installed the Couchbase SDK, you can compile and run the following basic program.
Follow these steps to install and set up the PHP SDK:
Get, install and start Couchbase Server. Come back when you are done.
Get and install the C Library. The C SDK is a requirement for the PHP library.
Download the PHP SDK for your system at Develop with Couchbase.
Unpack the archive containing the PHP SDK in the directory of your choice:
shell> tar xzf php-ext-couchbase-$system-$arch.tar.gz
This creates the directory php-ext-couchbase
. This directory includes a file
couchbase.so
which is the PHP extension for the SDK. Note the path to this
file as you will use it to update the PHP configuration file with this path.
Open the php.ini
file for your PHP interpreter. This is the initializer for
PHP which you will edit. You can get the path with this command:
shell> php -i | grep ini
The output provided will have the path to the configuration for PHP, for instance:
Loaded Configuration File => /private/etc/php.ini
Open the php.ini
file and add the path to the couchbase.so
file:
extension=/path/to/couchbase.so
Depending on the platform you are using, you may also need to reference the JSON library in your PHP configuration file.
If you are using the Couchbase PHP SDK on Red Hat/CentOS or their derivatives,
be aware that JSON encoding for PHP is by default not available to other
extensions. As a result you will receive an error resolving the
php_json_encode
symbol. The solution is to edit the php.ini
file to load the
JSON library and also load the Couchbase library. For instance, if your
extensions are at /etc/php.ini
, add the following two lines, in this order, to
the file:
extension=/path/to/json.so
extension=/path/to/couchbase.so
The reference to the two extensions must be in this specific order.
Now you are ready to verify your install by creating a small sample script using the PHP SDK.
To verify you have correctly set up the PHP SDK and the underlying C SDK follow these steps:
Create a new file named test.php and place the following test code it:
<?php
$cb = new Couchbase("127.0.0.1:8091", "username", "password", "default");
$cb->set("a", 101);
var_dump($cb->get("a"));
?>
Start your Couchbase Server. Note the ip address and port for Couchbase Server and any username and password.
If you need to, update the IP address, username and password in test.php
with
your Couchbase Server information.
Save the new script as test.php.
Run your test script:
shell> php test.php
Couchbase Server returns 101.
If you are using the PHP SDK on a Linux distribution such as Red Hat/CentOS, be
aware that JSON encoding for PHP is by default not available to other
extensions. As a result you will receive an error resolving the
php_json_encode
symbol. The solution is to edit the PHP configuration file as
we described earlier to include the JSON extension and then the Couchbase
extension. For instance, if your PHP config file is /etc/php.ini
, add the
following two lines to the file:
extension=/path/to/json.so
extension=/path/to/couchbase.so
The references to your JSON library and the PHP SDK need to be provided in this order.
Now you are ready to ready to develop with the PHP SDK and Couchbase Server 2.0. For more information about methods available in this SDK, see Couchbase Client Library: PHP 1.1. For learning more in general about developing application on Couchbase Server, see Couchbase Developer’s Guide 2.0.
To demonstrate how to use Couchbase PHP SDK with Couchbase Server we will built a web page counter. The counter will record the last access date for a web page. we’ll implement those features in PHP using the Couchbase library.
To store data in Couchbase Server, you provide information in a way that is similar to a PHP Array. You can store a value with a key, then retrieve the value by the key. In this exercise, we will store and retrieve integer and string data.
In the first part of our PHP script, we create a connection with Couchbase Server:
<?php
#Create the Couchbase object
$cb_obj = new Couchbase("127.0.0.1:8091", "user", "pass", "default");
In the next section of our script, we find out the name of the script currently running on the web server. Typically the name would be for the counter PHP script itself:
#determine the name of the script currently running
$script_name=$_SERVER["SCRIPT_NAME"];
In this case we set the $script_name
variable to be the script name that is
stored in a PHP environment variable, SCRIPT_NAME
. Then we try to retrieve an
existing record for the key $script_name
from Couchbase Server. This will tell
us if the key already exists or not and wheter we need to create a new record
with the key:
#if the script name doesn't exist as a Couchbase key then add
$script_access_count=$cb_obj->get($script_name);
if($cb_obj->getResultCode() == COUCHBASE_KEY_ENOENT){
#the add will fail if it has already been added
$cb_obj->add($script_name,0);
}
Then we use one of the Couchbase SDKs increment methods to add one to the page
access count. We provide the key, which is the $script_name
, to increase the
counter by one. We also output the access count in a string:
#increment the integer associated with the script name
$access_count = $cb_obj->increment($script_name);
#print the current access count
echo "this page ($script_name) accessed $access_count times<br>";
In this final part of our access counter script, we retrieve the last date and time that the script had been accessed. Again, we use the key as a parameter to get the value from Couchbase Server:
#retrieve the last access date/time of the script.
#the key name is the script name prefixed with DATE::
$last_access_date=$cb_obj->get("DATE::" . $script_name);
#handle the case where this is the first access to the script
#and that key doesn't yet exist
if($last_access_date == NULL){
$last_access_date = "never";
}
echo "this page last accessed: " . $last_access_date;
#save the current access date/time in a script
$cb_obj->set("DATE::" . $script_name,date("F j, Y, g:i:s a "));
?>
You can save this sample can be saved into a PHP file in a directory on your web server, such as test.php
When you load test.php page you’ll see something like this on the first load:
this page (/test.php) accessed 1 times
this page last accessed: never
And like this on subsequent loads, with the access count incrementing and the date/time increasing:
this page (/test.php) accessed 2 times
this page last accessed: June 1, 2011, 10:06:04 am
Reload the script a few times in rapid succession and you’ll see a spike in
traffic in the Couchbase Web UI. The first line of the script creates a
Couchbase client instance, ( $cb_obj
). The parameters define a Couchbase
server hostname and port as well as user credentials and the default bucket name
to the server pool that we’ll be accessing.
If you are running Couchbase on a single node, you specify its hostname and port. If you are running a multi-node cluster, you only need to point to a single node in a cluster as the PHP extension will transparently determine any cluster topology and route requests to the right servers as well as react on topolgy changes.
$cb_obj = new Couchbase("localhost", "user", "pass", "default");
We then get the "SCRIPT_NAME"
value from the $_SERVER
Array, which tells us
in which script this PHP code is currently running.
$script_name=$_SERVER["SCRIPT_NAME"];
Now we connect to Couchbase to retrieve the integer access count associated with this script using the script name (determined above) as the key.
$script_access_count=$cb_obj->get($script_name);
If there is no such key in Couchbase (because the page has not previously been
accessed), the request returns a NULL
value, and the result code of the call
to the get method is the Couchbase constant COUCHBASE_KEY_ENOENT
(resource not
found). In this case we use the add
method to set the value to zero. If,
between the failed get
and add
method calls, the key has already been added
by another process, the add
will fail and we will not overwrite the added
value. This sort of attention to concurrency issues is overkill for the current
application, as a missed counting of a single script access is unlikely to be
critical; however, it illustrates how to write Couchbase code to avoid
unintentionally overwriting keys:
if($cb_obj->getResultCode() == COUCHBASE_KEY_ENOENT){
$cb_obj->add($script_name,0);
}
The key associated with the script name count is then incremented by one for the current script access, and that incremented value is returned and printed out. The increment method is atomic, consisting of both an increment and retrieval of the resultant value. If the Couchbase server receives two such requests, they will be queued for action in the order they were received and return the correct count to each requesting process.
$access_count = $cb_obj->increment($script_name);
echo "this page ($script_name) accessed $access_count times<br>";
To retrieve and update the last access date of the script we’re doing something similar to what we did for the script access count, however, instead of adding and incrementing an integer, we’re adding and updating a string.
As a key for the date any given script was accessed, we’ve prepended the string
"DATE::"
to the beginning of the script name; thus the key for the last access
date of our test.php script is "DATE::test.php"
. We first try to get the date
of last access and assign it to the $last_access_date
variable. If this fails
(the get
method returns a Boolean NULL
value and the subsequent
getResultCode
call returns COUCHBASE_KEY_ENOENT
) that means it hasn’t yet
been set, so the script has not previously been accessed. In that case we’ll set
$last_access_date
to “never”. We then print out the value of
$last_access_date
.
$last_access_date=$cb_obj->get("DATE::" . $script_name);
if($cb_obj->getResultCode() == COUCHBASE_KEY_ENOENT){
$last_access_date = "never";
}
echo "this page last accessed: " . $last_access_date;
We now set the access date for this access of the script for retrieval on the next access.
$cb_obj->set("DATE::" . $script_name,date("F j, Y, g:i:s a "));
And that’s the end of the script. There is no method in the Couchbase library to close the connection to the server, however it will automatically be closed at the end of script execution.
This has been a brief outline of some basic features of the Couchbase API in
PHP. We’ve used the add
, get
, set
, increment
and getResultCode
methods
of the Couchbase class to store and retrieve integers and strings. There are a
number of other methods that allow for scalable development in a multiprocess
and multi-server environment.
Building on the foundations of the Getting Started guide, this tutorial will show you how to build a full-blown web application on top of Couchbase Server 2.0. We’ll make use of a very powerful toolchain, consisting of Composer, Silex and Twig to build a solid web application fast while not losing the focus on showing how to work with the PHP SDK.
We’ll make use of the beer-sample
dataset to display and manage beers and
breweries. Along the way we’ll pick up concepts on querying Couchbase Server 2.0
by keys or through views.
The full code sample is available on GitHub. You can clone the repository and work from there if you would simply like to see the finished application.
While we’ll cover the installation process in detail, make sure to have the following dependencies in place:
PHP 5.3 or later: The PHP SDK itself requires 5.3 or later, nearly all of the dependencies used here require 5.3.
Couchbase PHP SDK 1.1 : Install the appropriate .so or .dll file, depending on your platform. Earlier versions are not capable enough, because we make use of the brand new view-related functionality.
Composer : An excellent dependency manager for PHP and supported by major libraries and frameworks like Doctrine or Symfony.
Before we can start coding our application logic, we need to prepare both the
database and the application. We’ll import the beer-sample
bucket, prepare
some views and make sure everything works correctly. We’ll also get our
application skeleton in line so we are ready to code.
If you haven’t already, download and install Couchbase Server 2.0. While you’re
at it, make sure to install the beer-sample
sample bucket on the fly. If you
already have the server installed and the beer-sample
bucket is not in place,
head over to Settings->Sample Buckets
and install it. Give it a few seconds
until the notification box disappears. You may need to shrink the size of some of
your existing buckets to make room for the beer-sample database.
The beer-sample
bucket comes with a small set of views already predefined, but
to make our application function correctly we need some more. This is also a
very good chance to explore the view management possibilities inside the Web UI.
Since we want to list beers and breweries by name, we need to define one view
for each. Head over to the Web UI and click on the Views
menu. Select
beer-sample
from the drop-down list to switch to the correct bucket. Now click
on Development Views
and then Create Development View
to define your first
view. You need to give it the name of both the design document and the actual
view. Insert the following names:
Design Document Name: _design/dev_beer
View Name: by_name
The next step is to define the map
and (optional) reduce
functions. In our
examples, we won’t use have a reduce
function, but you can play around if you
would like to. Insert the following JavaScript map
function and click Save
.
function (doc, meta) {
if(doc.type && doc.type == "beer") {
emit(doc.name, null);
}
}
Every map
function takes the full document ( doc
) and (optionally) its
associated metadata ( meta
) as the arguments. You are then free to inspect
this data and emit
a result when you want to have it in your view. Views are
always sorted by key, so by emitting information, we are in effect creating an
index. In our case we emit the name of the beer ( doc.name
) when the document
both has a type field and the type is beer
. We don’t need to emit a value -
that’s because we are using null
here. It’s always advisable to keep the view
entry as small as possible. Resist the urge to include the full document through
emit(meta.id, doc)
, because it will increase the size of your views. If you
need to access the full document (or large parts), then retrieve the document
via the returned id in the view result. Note: at this time PHP does not have a
way to include docs, though some other Couchbase SDKs do.
Now we need to do (nearly) the same for our breweries. Since you already know how to do this, here is all the information you need to create it:
Design Document Name: _design/dev_brewery
View Name: by_name
Map Function:
function (doc, meta) {
if(doc.type && doc.type == "brewery") {
emit(doc.name, null);
}
}
The final step that you need to do is to push the design documents in
production. While the design documents are in development
, the index is only
applied to a subset of the data. Since we want to have the index on the whole
dataset, click the Publish
button on both design documents (and accept any
info popup that warns you from overriding the old one). See the view section of
the Couchbase Server manual for more information on how you may use this
development and production workflow to your advantage when developing a large
application.
Now that Couchbase Server is ready to use, we need to set up the skeleton of our
application. Since we’re using composer, all we need to get the dependencies is
to create a composer.json
file with the following content:
{
"require": {
"silex/silex": "1.0.x-dev",
"twig/twig": ">=1.8,<2.0-dev"
}
}
Place that file inside the /beersample-php
directory of your web root
(depending on your setup, it is often located under /var/www/
). We also need
to create a few more directories to keep the application organized.
Create directories with the following structure:
/beersample-php
/templates
/beers
/breweries
/assets
/css
/js
We’ll fill the template directories later, but the assets can be added immediately. Please locate and download the following JavaScript and CSS files so they are in place. We make use of the fabulous Twitter Bootstrap library to make the application look good without much effort.
css/bootstrap.min.css (the minified twitter bootstrap library)
css/bootstrap-responsive.min.css (the minified responsive layout classes from bootstrap)
Also, we’re using pretty URLs in our application. Here is a .htaccess
file you
can place inside your root directory to make it work properly when using Apache
HTTPD. Please refer to the Silex
documentation on how to add
one for different web servers.
<IfModule mod_rewrite.c>
Options -MultiViews
RewriteEngine On
RewriteBase /beersample-php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
To install the dependencies, we’re now able to run php composer.phar install
(or update
). It should install all needed dependencies and we’re ready to go
afterwards:
Loading composer repositories with package information
Installing dependencies
- Installing twig/twig (v1.11.1)
Downloading: 100%
...
- Installing silex/silex (dev-master 0e69dc2)
Cloning 0e69dc22400293f9364f8b918d008f3f6b634a47
symfony/routing suggests installing symfony/config (2.1.*)
...
Writing lock file
Generating autoload files
With all the dependencies installed and ready to go, we can start out by adding
our index.php
and doing the basic bootstrap.
Create a index.php
file inside your /beersample-php
directory and add the
following. We’ll discuss it afterwards:
<?php
use Symfony\Component\HttpFoundation\Request;
use Silex\Application;
use Silex\Provider\TwigServiceProvider;
// Config Settings
define("SILEX_DEBUG", true);
define("COUCHBASE_HOSTS", "127.0.0.1");
define("COUCHBASE_BUCKET", "beer-sample");
define("COUCHBASE_PASSWORD", "");
define("COUCHBASE_CONN_PERSIST", true);
define("INDEX_DISPLAY_LIMIT", 20);
// Autoloader
require_once __DIR__.'/vendor/autoload.php';
// Silex-Application Bootstrap
$app = new Application();
$app['debug'] = SILEX_DEBUG;
// Connecting to Couchbase
$cb = new Couchbase(COUCHBASE_HOSTS, "beer-sample", COUCHBASE_PASSWORD, COUCHBASE_BUCKET, COUCHBASE_CONN_PERSIST);
// Register the Template Engine
$app->register(new TwigServiceProvider(), array('twig.path' => __DIR__.'/templates'));
// Run the Application
$app->run();
?>
The first part defines some constants to make config settings easy to change. Of
course this is not needed, but makes it easy for you to change the configuration
later in different environments. Afterwards, the composer autoloader is
included. This is needed to make sure the use
statements are available to us
without having to require all PHP files by hand.
The new Application();
initializes a new Silex application. To make sure that
we see all errors during development, we can set debug
to true
.
Now it gets interesting. We connect to our Couchbase cluster by constructing a new Couchbase object. We pass in all required information (easy to grasp with the name of the constants). This object will then be passed into all controller actions and used from there.
Because we’re using the Twig template engine, we can register a TwigServiceProvider which helps us to automatically locate and load them. You’ll see later how these are rendered and how we can pass data to them.
Finally, we run the application through $app->run();
. The actual actions are
implemented between the Twig registration call ( $app->register(new
TwigServiceProvider()...
) and the final run method ( $app->run()
), so
remember to put them in there.
If you now run the application in your browser, you should see the following
Exception showing up: "Sorry, the page you are looking for could not be
found."
. This is actually great, because Silex
is at work, but can’t find the route for the /
URL. Let’s fix this now. Add
the following snippet between the TwigServiceProvider
and the run()
call:
$app->get('/', function() use ($app) {
return $app['twig']->render('welcome.twig.html');
});
This action is called when a GET
request comes in for the /
URL. We don’t
need to fetch any data from Couchbase here, so we just instruct
Silex to render a
Twig template named welcome.twig.html
. Since we
haven’t created it yet, go ahead and place the file inside the “templates”
directory:
{% extends "layout.twig.html" %}
{% block content %}
<div class="span6">
<div class="span12">
<h4>Browse all Beers</h4>
<a href="/beersample-php/beers" class="btn btn-warning">Show me all beers</a>
<hr />
</div>
<div class="span12">
<h4>Browse all Breweries</h4>
<a href="/beersample-php/breweries" class="btn btn-info">Take me to the breweries</a>
</div>
</div>
<div class="span6">
<div class="span12">
<h4>About this App</h4>
<p>Welcome to Couchbase!</p>
<p>This application helps you to get started on application
development with Couchbase. It shows how to create, update and
delete documents and how to work with JSON documents.</p>
<p>The official tutorial can be found
<a href="http://www.couchbase.com/docs/couchbase-sdk-php-1.1/tutorial.html">here</a>!</p>
</div>
</div>
{% endblock %}
There is nothing fancy here, we’re just showing some basic information to the
user and guiding them to the real application functionality. Also, note the {%
extends "layout.twig.html" %}
and {% block content %}
twig elements on top of
the page.
Since we don’t want to repeat the HTML layout part on every template, we can
define a layout and each template is a block that is loaded into that template.
Since we haven’t created the layout.twig.html
, do that in the same directory
as the welcome.twig.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Couchbase PHP Beer-Sample</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="The Couchbase PHP Beer-Sample App">
<meta name="author" content="Couchbase, Inc. 2012">
<link href="/beersample-php/assets/css/bootstrap.min.css" rel="stylesheet">
<link href="/beersample-php/assets/css/beersample.css" rel="stylesheet">
<link href="/beersample-php/assets/css/bootstrap-responsive.min.css" rel="stylesheet">
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<div class="container-narrow">
<div class="masthead">
<ul class="nav nav-pills pull-right">
<li><a href="/beersample-php">Home</a></li>
<li><a href="/beersample-php/beers">Beers</a></li>
<li><a href="/beersample-php/breweries">Breweries</a></li>
</ul>
<h2 class="muted">Couchbase Beer-Sample</h2>
</div>
<hr>
<div class="row-fluid">
<div class="span12">
{% block content %}{% endblock %}
</div>
</div>
<hr>
<div class="footer">
<p>© Couchbase, Inc. 2012</p>
</div>
</div>
<script src="/beersample-php/assets/js/jquery.min.js"></script>
<script src="/beersample-php/assets/js/bootstrap.min.js"></script>
<script src="/beersample-php/assets/js/beersample.js"></script>
</body>
</html>
The {% block content %}{% endblock %}
is responsible for loading the
appropriate block later (the other markup is again just HTML boilerplate to help
with a nice layout for Twitter Bootstrap
).
If you load the page, you should see the welcome page loading! If not, you may need to look at your web server logs to see what kind of error messages have been generated when running the scripts. Assuming all is working well, we’re now ready to implement the actual functionality.
The first thing we’re going to implement is to show a list of beers in a table.
The table itself will contain the name of the beer and links to the brewery as
well as buttons to edit or delete the beer. We’ll implement interactive
filtering on the table later as well. The following code should be inserted
after the /
action to keep everything in order.
$app->get('/beers', function() use ($app, $cb) {
// Load all beers from the beer/by_name view
$results = $cb->view("beer", "by_name", array(
'limit' => INDEX_DISPLAY_LIMIT
));
$beers = array();
// Iterate over the returned rows
foreach($results['rows'] as $row) {
// Load the full document by the ID
$doc = $cb->get($row['id']);
if($doc) {
// Decode the JSON string into a PHP array
$doc = json_decode($doc, true);
$beers[] = array(
'name' => $doc['name'],
'brewery' => $doc['brewery_id'],
'id' => $row['id']
);
}
}
// Render the template and pass on the beers array
return $app['twig']->render('beers/index.twig.html', compact('beers'));
});
We’re making use of our previously defined view beer/by_name
. We also pass in
a limit
option to make sure we don’t load all documents returned by the view.
The results
variable stores the view response and contains the actual data
inside the rows
element. We can then iterate over the dataset, but since the
view only returns the document ID and we need more information, we fetch the
full document through the get()
method. If it actually finds a document by the
given ID, we convert the JSON string to a PHP array and add it to the list of
beers. The list is then passed on to the template to display it.
The corresponding template beers/index.twig.html
looks like this:
{% extends "layout.twig.html" %}
{% block content %}
<h3>Browse Beers</h3>
<form class="navbar-search pull-left">
<input id="beer-search" type="text" class="search-query" placeholder="Search for Beers">
</form>
<table id="beer-table" class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Brewery</th>
<th></th>
</tr>
</thead>
<tbody>
{% for beer in beers %}
<tr>
<td><a href="/beersample-php/beers/show/{{beer.id}}">{{beer.name}}</a></td>
<td><a href="/beersample-php/breweries/show/{{beer.brewery}}">To Brewery</a></td>
<td>
<a class="btn btn-small btn-warning" href="/beersample-php/beers/edit/{{beer.id}}">Edit</a>
<a class="btn btn-small btn-danger" href="/beersample-php/beers/delete/{{beer.id}}">Delete</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
Aside from normal HTML table markup, we make use of the {% for beer in beers
%}
block to loop over the beers array. We then print out each row and show the
name of the beer, the link to the brewery and also buttons to edit and delete
the beer. We’ll implement these methods in a minute.
The next action we’re going to implement is the show
action. When you click on
a beer, it should display all attributes from the JSON document in a table so we
can inspect them properly. Since everything is stored in one document, we just
need to fetch it by the given ID, decode it from the JSON string and pass it on
to the view. Very straightforward and performant:
$app->get('/beers/show/{id}', function($id) use ($app, $cb) {
// Get the beer by its ID
$beer = $cb->get($id);
if($beer) {
// If a document was found, decode it
$beer = json_decode($beer, true);
$beer['id'] = $id;
} else {
// Redirect if no document was found
return $app->redirect('/beers');
}
// Render the template and pass the beer to it
return $app['twig']->render(
'beers/show.twig.html',
compact('beer')
);
});
The template iterates over the JSON attributes and prints their name and value accordingly. Note that some documents can contain nested values which is not covered here.
{% extends "layout.twig.html" %}
{% block content %}
<h3>Show Details for Beer "{{beer.name}}"</h3>
<table class="table table-striped">
<tbody>
{% for key,attribute in beer %}
<c:forEach items="${beer}" var="item">
<tr>
<td><strong>{{key}}</strong></td>
<td>{{attribute}}</td>
</tr>
</c:forEach>
{% endfor %}
</tbody>
</table>
{% endblock %}
The next action we’re going to implement is the delete
action.
$app->get('/beers/delete/{id}', function($id) use ($app, $cb) {
// Delete the Document by its ID
$cb->delete($id);
// Redirect to the Index action
return $app->redirect('/beersample-php/beers');
});
As you can see, the delete
call is very similar to the previous get
method.
After the document has been deleted, we redirect to the index action. If we’d
like to, we could get more sophisticated in here. For example, good practice
would be to fetch the document first and check if the document type is beer
to
make sure only beers are deleted here. Also, it would be appropriate to return a
error message if the document didn’t exist previously. Note that there is no
template needed because we redirect immediately after deleting the document.
Since we can now show and delete beers, its about time to make them editable as
well. We now need to implement two different actions here. One to load the
dataset and one to actually handle the POST
response. Take note that this demo
code is not really suited for production. You really want to add validation here
to make sure only valid data is stored - but it should give you a solid idea on
how to implement the basics with Couchbase.
// Show the beer form
$app->get('/beers/edit/{id}', function($id) use ($app, $cb) {
// Fetch the document
$beer = $cb->get($id);
if($beer) {
// Decode the document
$beer = json_decode($beer, true);
$beer['id'] = $id;
} else {
// Redirect if no document was found
return $app->redirect('/beers');
}
// Pass the document on to the template
return $app['twig']->render(
'beers/edit.twig.html',
compact('beer')
);
});
// Store submitted Beer Data (POST /beers/edit/<ID>)
$app->post('/beers/edit/{id}', function(Request $request, $id) use ($app, $cb) {
// Extract the POST form data out of the request
$data = $request->request;
$newbeer = array();
// Iterate over the POSTed fields and extract their content.
foreach($data as $name => $value) {
$name = str_replace('beer_', '', $name);
$newbeer[$name] = $value;
}
// Add the type field
$newbeer['type'] = 'beer';
// Encode it to a JSON string and save it back
$cb->set($id, json_encode($newbeer));
// Redirect to show the beers details
return $app->redirect('/beersample-php/beers/show/' . $id);
});
The missing link between the GET
and POST
handlers is the form itself. The
template is called edit.twig.html
and looks like this:
{% extends "layout.twig.html" %}
{% block content %}
<h3>Edit Beer</h3>
<form method="post" action="/beersample-php/beers/edit/{{beer.id}}">
<fieldset>
<legend>General Info</legend>
<div class="span12">
<div class="span6">
<label>Name</label>
<input type="text" name="beer_name" placeholder="The name of the beer." value="{{beer.name}}">
<label>Description</label>
<input type="text" name="beer_description" placeholder="A short description." value="{{beer.description}}">
</div>
<div class="span6">
<label>Style</label>
<input type="text" name="beer_style" placeholder="Bitter? Sweet? Hoppy?" value="{{beer.style}}">
<label>Category</label>
<input type="text" name="beer_category" placeholder="Ale? Stout? Lager?" value="{{beer.category}}">
</div>
</div>
</fieldset>
<fieldset>
<legend>Details</legend>
<div class="span12">
<div class="span6">
<label>Alcohol (ABV)</label>
<input type="text" name="beer_abv" placeholder="The beer's ABV" value="{{beer.abv}}">
<label>Biterness (IBU)</label>
<input type="text" name="beer_ibu" placeholder="The beer's IBU" value="{{beer.ibu}}">
</div>
<div class="span6">
<label>Beer Color (SRM)</label>
<input type="text" name="beer_srm" placeholder="The beer's SRM" value="{{beer.srm}}">
<label>Universal Product Code (UPC)</label>
<input type="text" name="beer_upc" placeholder="The beer's UPC" value="{{beer.upc}}">
</div>
</div>
</fieldset>
<fieldset>
<legend>Brewery</legend>
<div class="span12">
<div class="span6">
<label>Brewery</label>
<input type="text" name="beer_brewery_id" placeholder="The brewery" value="{{beer.brewery_id}}">
</div>
</div>
</fieldset>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save changes</button>
</div>
</form>
{% endblock %}
The only special part in the form are the Twig blocks like
{{beer.brewery_id}}
. They allow us to easily include the actual value from the
field (when there is one). You can now change the values in the input fields,
hit Save changes
and see the updated document either in the web application or
through the Couchbase Admin UI.
There is one last thing we want to implement here. You may have noticed that the
index
page lists all beers but also has a search box on the top. Currently, it
won’t work because the backend is not yet implemented. The JavaScript is already
in place in the assets/js/beersample.js
file, so look through it if you are
interested. It just does an AJAX request against the server with the given
search value, expects a JSON response and iterates over it while replacing the
original table rows with the new ones.
We need to implement nearly the same view code as in the index
action, but
this time we make use of two more view query params that allow us to only return
the range of documents we need:
$app->get('/beers/search', function(Request $request) use ($app, $cb) {
// Extract the search value
$input = strtolower($request->query->get('value'));
// Define the Query options
$options = array(
'limit' => INDEX_DISPLAY_LIMIT, // Limit the number of returned documents
'startkey' => $input, // Start the search at the given search input
'endkey' => $input . '\uefff' // End the search with a special character (see explanation below)
);
// Query the view
$results = $cb->view("beer", "by_name", $options);
$beers = array();
// Iterate over the resulting rows
foreach($results['rows'] as $row) {
// Load the corresponding document
$doc = $cb->get($row['id']);
if($doc) {
// If the doc is found, decode it.
$doc = json_decode($doc, true);
$beers[] = array(
'name' => $doc['name'],
'brewery' => $doc['brewery_id'],
'id' => $row['id']
);
}
}
// Return a JSON formatted response of all beers for the JavaScript code.
return $app->json($beers, 200);
});
The two new query parameters we make use of are: startkey
and endkey
. They
allow us to define the key where the search should begin and the key where the
search should end. We use the special character '\uefff'
here which means
“end”. That way, we only get results which correctly begin with the given search
string. This is a little trick that comes in very handy from time to time.
The rest is very similar to the index
action so we’ll skip the discussion for
that. Also, we don’t need a template here because we can return the JSON
response directly. Very straightforward.
For more information about using views for indexing and querying from Couchbase Server, here are some useful resources:
For technical details on views, how they operate, and how to write effective map/reduce queries, see Couchbase Server 2.0: Views and Couchbase Sever 2.0: Writing Views.
Sample Patterns: to see examples and patterns you can use for views, see Couchbase Views, Sample Patterns.
Timestamp Pattern: many developers frequently ask about extracting information based on date or time. To find out more, see Couchbase Views, Sample Patterns.
If you look through the actual code on GitHub, you will notice that there is much more to it than described here. For example, some is the same but just for breweries. There is also some additional code not covered here. We recommend you look through it to become more familiar with it. Then move on to the API documentation and further examples.
If you wish to view further samples using the PHP client, please take a look at some of our DeveloperDay samples!
Couchbase Server and the PHP SDK provides a boatload of useful methods that you can use in your day-to-day work. You should now be ready to explore those on your own, so have fun coding with Couchbase!
For convenience, all distributions include a stub API reference file that
contains the definitions for all the different methods and constants supported.
The API information is provided in the couchbase-api.php
file. Additionally,
a link to the current API reference is available here.
In Couchbase 2.0 you can index and query JSON documents using views. Views are functions written in JavaScript that can serve several purposes in your application. You can use them to:
Find all the documents in your database that you need for a particular process,
Create a copy of data in a document and present it in a specific order,
Create an index to efficiently find documents by a particular value or by a particular structure in the document,
Represent relationships between documents, and
Perform calculations on data contained in documents.
For more information on views, see Couchbase Developer Guide, Finding Data with Views, and Couchbase Sever 2.0: Views and Indexes.
Sample Patterns: to see examples and patterns you can use for views, see Couchbase Views, Sample Patterns.
Timestamp Pattern: many developers frequently ask about extracting information based on date or time. To find out more, see Couchbase Views, Sample Patterns.
Error handling with the Couchbase PHP interface is currently handled using a
combination of return values, exceptions and error codes. Return values are used
for operations such as get()
, which returns the corresponding document value
if successful, or false
if the document could not be found.
An explicit error code system is available when you want to identify specific errors, but the methods to obtain these must be called immediately after each operation to identify a specific failure. These methods are deprecated in place of the PHP exception mechanism and may be removed in a future release.
All the interface calls support exceptions and will raise a specific or generic
Couchbase exception if an operation fails. For more information on the
exceptions supported, see Exceptions.
Operations also raise a result code which can be obtained by calling the
getResultCode()
method immediately after an operation. Constants are provided
to identify specific error conditions. See Error Codes and
Constants for more information.
Exceptions are used within the PHP Couchbase client library to raise an error
within an operation. The exceptions are all inherited from the base
CouchbaseException
class, or you can trap for specific exceptions. The error
string associated with the exception will provide more information about the
error.
For example:
try {
$oo->touch("spoon", 1);
} catch (CouchbaseException $e) {
echo "Error: " + $e;
}
The supported exceptions are listed below:
CouchbaseException
This is the base class of all of the exceptions thrown from the extension.
CouchbaseIllegalKeyException
The key provided to the operation is not legal (i.e. empty).
CouchbaseAuthenticationException
Authentication to the Couchbase cluster failed.
CouchbaseLibcouchbaseException
An error occurred within libcouchbase which is used by the PHP extension to communicate with the cluster.
CouchbaseServerException
An error occurred somewhere in the Couchbase Cluster.
The PHP client library defines the following error codes and constants. These can be used against the return value for most operations to determine whether they succeeded, or failed, and in the event of a failure, what the underlying cause was. The constants are distributed between both generic interface errors, and operation specific errors. Check the corresponding API reference for information on which errors are raised by each operation.
You can use the getResultCode()
and getResultMessage()
methods to obtain the
error code and message for each operation. You can use these in combination with
the error constants to determine a specific course of action when an operation
fails, for example:
$cb->replace("fork", "Hello World!", 10);
if ($cb->getResultCode() === COUCHBASE_KEY_ENOENT) {
// Doesn't exist, add it first
$cb->add("fork", "Hello World!", 10);
echo "Key added (didn't exist)\n";
} elseif ($cb->getResultCode() === SUCCESS) {
echo "Key updated\n";
} else {
echo "Error occurred: ", $cb->getResultMessage(), "<br/>";
}
In the above example, an update operation is attempted on a key which must
already exist. If Couchbase Server returns an error that the key didn’t exist,
add()
is used to create the key. If the operation was successful, we output a
success message. For all other errors, the error message is generated.
Note that the getResultCode()
and getResultMessage()
methods are now
deprecated and will be removed in a future release. The PHP exception system
will replace the functionality of these two methods.
The PHP SDK currently implements a timeout for Couchbase operations
which is defaulted to 2.5 seconds. This timeout can be adjusted via
the getTimeout
and setTimeout
methods. The following is an example =
of this:
<?php
// Connect to our cluster
$cb = new Couchbase("192.168.1.200:8091");
// Adjust our timeout to 100000usec rather than the default of 2500000usec
$cb->setTimeout(100000);
// The following operation will timeout if not completed within 100000usec
$cb->add('test_key', 'test_value');
Note, this feature is currently experimental!
Due to the highly maleable nature of a Couchbase cluster, the configuration information which holds a list of all cluster nodes along with various other important pieces of cluster meta-data needs to be downloaded when a connection to the cluster is established. This is a ‘heavy’ call to make to a Couchbase cluster, and because of this, we have implemented a system that allows temporary cacheing of this data such that requests that are executed simultaneously, or close together will not incur the configuration lookup penalty.
In order to enable this feature, you must specify the
couchbase.config_cache
option somewhere in your php configuration.
The value of this option should be a path to a directory where you
would like to store the cached data.
See the example couchbase ini for further information.
As of version 1.2.0 of the PHP client library, we now support an operation known as ‘replica read’. This functionality allows you to read data from your cluster when the node holding the active copy of your document is inaccessible. This is not a replacement for node failover, but instead provides a stop-gap for gracefully handling situations where a particular document is inaccessible but available through one of its replicas. It is worth noting that this feature does not guarantee consistency as any pending writes on the primary node may not have been replicated yet.
Using this feature is similar to executing a get request, however there is no Multi variant of replica reads. Here is an simple example of reading a document from a replica:
<?php
// Connect to our cluster
$cb = new Couchbase("192.168.1.200:8091”);
// Add a document and ensure replication to another node
$cb->add("replica_read", "this_is_a_test", 0, 1, 1);
// Read from our replica
$replica_cas = "";
$cb->getReplica("replica_read", NULL, $replica_cas);
The API reference for the PHP client is available at http://www.couchbase.com/autodocs/couchbase-php-client-1.1.5/index.html
The following sections provide release notes for individual release versions of Couchbase Client Library PHP. To browse or submit new issues, see Couchbase Client Library PHP Issues Tracker.
New Features and Behavior Changes in 1.2.1
New Features and Behavior Changes in 1.2.0
This is the second release of the Couchbase PHP SDK which is compatible with Couchbase Server 1.8 and Couchbase Server 2.0. It is intended to be compatible with version 1.0 of the SDK. New features for this SDK include:
New functions for writing and accessing data using pessimistic locking, which are also known as get-and-lock functions.
Specify durability. You can now indicate how many replica nodes an item will be written to; you can also request a return value if Couchbase Server has successfully persisted an item to disk.
Indexing and querying. Ability to query a view and handle result sets returned by Couchbase Server.
To browse existing issues and fixes in this release, see PHP SDK Issues Tracking.
New Features and Behavior Changes in 1.1.0
Warn for incorrect view parameters.
Issues : PCBC-13
Add support for unlock command.
Issues : PCBC-52
Fixes in 1.1.0
Completely document constants used as options and result codes for the PHP SDK. A list of constants and result codes are available at Error Codes and Constants.
PHP Client also now catches and processes ‘object too large’ errors coming from underlying C library. For a complete list of errors, including this one, see Error Codes and Constants.
Issues : PCBC-92
Depending on the platform you are using, you may also need to reference the JSON library in your PHP configuration file:
If you are using the Couchbase PHP SDK on Red Hat/CentOS or their derivatives, be aware that JSON encoding for PHP is by default not available to other extensions. As a result you will receive an error resolving the php_json_encode symbol. The solution is to edit the php.ini file to load the JSON library and also load the Couchbase library. Please note that you should provide these two extensions in the order shown below:
extension=/path/to/json.so
extension=/path/to/couchbase.so
Issues : PCBC-141
Improve method signature for get()
: move the $cas_token parameter to the
second parameter, and improve handling of callback function provided as
parameter.
Issues : PCBC-73
Documentation incorrectly stated that version returns the version of the server; this in fact returns the version of the library. This has been fixed.
Issues : PCBC-108
Provide tarball releases for PHP SDKs.
Issues : PCBC-79
When doing a query against a view, the php application segfaulted. This is fixed.
Issues : PCBC-147
Known Issues in 1.1.0
PHP SDK 1.1.0 is not yet available on Windows as a supported library. There is however an experimental build of the PHP SDK 1.1.0 which you can download and preview on a development system.
Issues : PCBC-53
This is a preview release of the Couchbase PHP SDK with preliminary support for Views and Couchbase Server 2.0. Version “-dp1” was a faulty release and is thus not mentioned here.
This versions includes all fixes of the 1.0.x branch, up to the 1.0.4 release.
New Features and Behavior Changes in 1.1.0-dp2