Skip to content

A simple Couchbase bucket repository and query builder in PHP.

License

Notifications You must be signed in to change notification settings

BowlOfSoup/couchbase-access-layer

Repository files navigation

Minimum PHP Version Build Status Coverage Status

Installation

composer require bowlofsoup/couchbase-access-layer

Couchbase Access Layer

A simple layer on top of the PHP Couchbase SDK. Basically you get a bucket repository class which acts as a layer between your code and Couchbase.

The repository helps you to:

  • Quickly setup a Couchbase connection.
  • A handy to use BucketRepository to quickly query a single bucket.
  • Create queries with a so called 'query builder', this helps you build maintainable and easy to read N1ql queries.
  • Processes the differences in result syntax you can get back from Couchbase into a consistent query result.
  • See below for more examples!

Usage

Do use Parameters

Important: When building a query, always try to use parameters.

Incorrect:

$queryBuilder
    ->where('data.creationDate >= ' . $creationDate);

Correct:

$queryBuilder
    ->where('data.creationDate >= $creationDate')
    ->setParameter('creationDate', '2019-01-10');

This will prevent injection.

The query builder supports the following N1QL clauses

  • SELECT with optional DISTINCT and RAW
  • FROM with optional alias, subquery also implemented (= mandatory alias)
  • USE which means: USE KEYS and USE INDEX
  • WHERE
  • GROUP BY
  • ORDER BY
  • LIMIT
  • OFFSET

Documentation for clauses can be found On the Couchbase site.

Examples

<?php

declare(strict_types=1);

namespace Some\Name\Space;

use BowlOfSoup\CouchbaseAccessLayer\Exception\CouchbaseQueryException;
use BowlOfSoup\CouchbaseAccessLayer\Factory\ClusterFactory;
use BowlOfSoup\CouchbaseAccessLayer\Model\Result;
use BowlOfSoup\CouchbaseAccessLayer\Repository\BucketRepository;

class Foo
{
    public function exampleUsingTheQueryBuilder()
    {
        $result = $this->querySomeBucket();

        foreach ($result as $item) {
            // each $item contains a Couchbase document which matched the query.
        }

        // $result implements the \JsonSerializableInterface.
        $jsonEncoded = json_encode($result);

        // These (can) only differ when query with limit/offset is done.
        $resultCount = $result->getCount();
        $resultTotalCount = $result->getTotalCount();

        // Return just the data you queried
        $justTheData = $result->get();

        // Get only one (or the first) result
        $singleResult = $result->getFirstResult();
    }

    public function exampleUsingDirectCalls()
    {
        $clusterFactory = new ClusterFactory('couchbaseHost', 'couchbaseUsername', 'couchbasePassword');
        $bucketRepository = new BucketRepository('someBucketName', $clusterFactory);

        $bucketRepository->upsert('some document id', 'the content of the document');

        $bucketRepository->remove('some document id);

        $documentContent = $bucketRepository->getByKey('some document id');
    }

    public function exampleExecutingManualQuery()
    {
        $clusterFactory = new ClusterFactory('couchbaseHost', 'couchbaseUsername', 'couchbasePassword');
        $bucketRepository = new BucketRepository('someBucketName', $clusterFactory);

        // Creating an index for a bucket
        $bucketRepository->executeQuery(
            CREATE INDEX i_foo_field_name
            ON `' . $bucketRepository->getBucketName() . '`(`field`, `data.name`)
            WHERE (`documentType` = "foo")
        );

        $result = $bucketRepository->executeQuery(
            'SELECT someField FROM `bucket name` WHERE someOtherField = $someOtherField',
            ['someOtherField' => 'some value']
        );

        // This will only return one result.
        $result = $bucketRepository->executeQueryWithOneResult();
    }

    /**
     * @return \BowlOfSoup\CouchbaseAccessLayer\Model\Result
     */
    private function querySomeBucket(): Result
    {
        $clusterFactory = new ClusterFactory('couchbaseHost', 'couchbaseUsername', 'couchbasePassword');
        $bucketRepository = new BucketRepository('someBucketName', $clusterFactory);

        $queryBuilder = $bucketRepository->createQueryBuilder();

        $queryBuilder
            ->select('data.name')
            // See that you can put in your own logic, like COUNT and such:
            ->select('COUNT(data.name) AS count')
            ->where('foo = $foo')
            ->where('someField = $someField')
            ->where('type = $type');

        $queryBuilder->setParameters([
            'foo' => 'someFooValue',
            'someKey' => 'someKeyValue',
            'type' => 'someDocumentType',
        ]);

        $queryBuilder
            ->where('data.creationDate >= $creationDate')
            ->setParameter('creationDate', '2019-01-10');

        $queryBuilder
            ->groupBy('data.name')
            ->limit(10)
            ->offset(20);

        try {
            return $bucketRepository->getResult($queryBuilder);
        } catch (CouchbaseQueryException $e) {
            // Something went wrong.
        }
    }
}

For a WHERE clause you can put in logic yourself:

$queryBuilder->where('CONTAINS(SUBSTR(name,0,1),"C")');

For a GROUP BY clause you can put in logic yourself too:

$queryBuilder->groupBy('city LETTING MinimumThingsToSee = 400 HAVING COUNT(DISTINCT name) > MinimumThingsToSee');

Using a sub query in a FROM statement

When using a sub select in a FROM statement with $queryBuilder->fromSubQuery() and you're using parameters, put the parameters in the 'master' query builder. More info on this page.

$queryBuilderForSubSelect = new QueryBuilder('bucket_name');
$queryBuilderForSubSelect->where('type = $foo');

$queryBuilder = new QueryBuilder('bucket_name');

$queryBuilder
    ->select('q1.someFieldOfTheSubSelect')
    ->fromSubQuery($queryBuilderForSubSelect, 'q1')
    ->setParameter('foo', 'value1');

Unit tests

In a previous version, the tests used a dummy couchbase instance called CouchbaseMock.jar. This is not compatible with recent Couchbase versions. Therefore, the current situation has a Docker setup with an actual Couchbase instance for the tests. Assuming you have Docker installed, you can run the tests by running the following commands:

  • docker compose up -d
  • docker exec -ti couchbase-access-layer-php-1 bash
  • vendor/bin/phpunit