To run this prebuilt project, you will need:
git clone https://github.com/couchbase-examples/php-laravel-quickstart.gitcd php-laravel-quickstartcd php-laravel-quickstart
composer installNote: Composer automatically installs the required dependencies when building the project.
After installing dependencies, you need to set up the basic Laravel configuration:
cp .env.example .envphp artisan key:generateThe CouchbaseServiceProvider class is a Laravel service provider class responsible for setting up the connection to a Couchbase database in a Laravel application.
All configuration for communication with the database is read from environment variables. As a security best practice, you should set these values in your .env file:
DB_CONN_STR=<connection_string>
DB_USERNAME=<username>
DB_PASSWORD=<password>
DB_BUCKET=travel-sampleReplace the placeholder values with your actual Couchbase cluster details.
The connection string should be in the following format:
return [
'host' => env('DB_CONN_STR', 'couchbase://127.0.0.1'),
'username' => env('DB_USERNAME', 'Administrator'),
'password' => env('DB_PASSWORD', 'password'),
'bucket' => env('DB_BUCKET', 'travel-sample'),
];For more information on the Laravel connection string, see the Database Configuration documentation.
At this point the application is ready, and you can run it via the command line:
php artisan serveNote: Either the Couchbase Server must be installed and running on localhost or the connection string must be updated in the
config/couchbase.phpfile.
Tip: If Swagger UI loads without endpoints, generate the docs:
php artisan l5-swagger:generate
Build the Docker image
docker build -t php-laravel-quickstart .Run the Docker image
docker run -d --name laravel-container -p 8000:8000 \
-e DB_CONN_STR=<connection_string> \
-e DB_USERNAME=<username> \
-e DB_PASSWORD=<password> \
-e DB_BUCKET=travel-sample \
php-laravel-quickstartNote: Environment variables passed to the Docker container will override the default values in the configuration.
Once the application is running, you can access it in your browser at http://localhost:8000. Swagger UI is available at http://localhost:8000/api/documentation.
Once the application starts, you can see the details of the application on the logs.
The application will run on port 8000 of your local machine (http://localhost:8000). Open Swagger UI at http://localhost:8000/api/documentation to try the API endpoints. Swagger documentation is used in this demo to showcase the different API endpoints and how they can be invoked. More details on the Swagger documentation can be found in the appendix.
For this tutorial, we use three collections, airport, airline and route that contain sample airports, airlines and airline routes respectively. The route collection connects the airports and airlines as seen in the figure below. We use these connections in the quickstart to generate airports that are directly connected and airlines connecting to a destination airport. Note that these are just examples to highlight how you can use SQL queries to join the collections.
We will be setting up a REST API to manage some airline documents. The name field is the name of the airline. The callsign field is the callsign of the airline. The iata field is the IATA code of the airline. The icao field is the ICAO code of the airline. The country field is the country of the airline.
Our airline document will have a structure similar to the following example:
{
"name": "Couchbase Airways",
"callsign": "Couchbase",
"iata": "CB",
"icao": "CBA",
"country": "United States"
}.
├── app/
│ ├── Http/
│ │ └── Controllers/
│ │ ├── AirlineController.php
│ │ ├── AirportController.php
│ │ ├── RouteController.php
│ │ └── HotelController.php
│ ├── Models/
│ │ ├── Airline.php
│ │ ├── Airport.php
│ │ ├── Route.php
│ │ └── Hotel.php
│ └── Providers/
│ └── CouchbaseServiceProvider.php
├── config/
│ └── couchbase.php
├── routes/
│ └── api.php
└── tests/
└── Feature/
├── AirlineTest.php
├── AirportTest.php
├── RouteTest.php
└── HotelTest.phpFor this tutorial, we will focus on the airline entity to demonstrate CRUD operations and SQL++ queries.
We will be setting up a REST API to manage airline documents:
For CRUD operations, we will use the Key-Value operations that are built into the Couchbase SDK to create, read, update, and delete a document. Every document will need an ID (similar to a primary key in other databases) to save it to the database. This ID is passed in the URL. For other endpoints, we will use SQL++ to query for documents.
Open the AirlineController.php file and navigate to the store method. The request data is validated and then passed to the model to save the airline document.
// store method in AirlineController.php
public function store(Request $request, $id)
{
if ($errorResponse = $this->validateRequest($request)) {
return $errorResponse;
}
try {
$data = $request->only($this->allowedAttributes);
$airline = new Airline($data);
$airline->saveAirline($id);
return response()->json(['message' => 'Airline created successfully'], 201);
} catch (\Exception $e) {
return response()->json(['message' => 'Internal Server Error'], 500);
}
}We call the saveAirline method in the Airline model, which uses the upsert method from the Couchbase SDK. The upsert method inserts a new document or updates an existing one.
// saveAirline method in Airline model
public function saveAirline($id)
{
$data = $this->attributesToArray();
unset($data['id']);
$this->bucket->scope('inventory')->collection('airline')->upsert($id, $data);
}Navigate to the show method in the AirlineController.php file. This method retrieves a specific airline document using its ID.
// show method in AirlineController.php
public function show($id)
{
try {
$airline = Airline::findAirline($id);
if (!$airline) {
return response()->json(['message' => 'Airline not found'], 404);
}
return response()->json($airline);
} catch (\Exception $e) {
return response()->json(['message' => 'Internal Server Error'], 500);
}
}We call the findAirline method in the Airline model, which uses the get method from the Couchbase SDK for key-value retrieval.
// findAirline method in Airline model
public static function findAirline($id)
{
$instance = new static;
$document = $instance->bucket->scope('inventory')->collection('airline')->get($id);
$data = $document->content();
return new static($data);
}The PUT operation uses the same store method as POST, leveraging the upsert functionality to either update an existing document or create a new one if it doesn't exist.
// Same store method handles both POST and PUT
$airline->saveAirline($id); // Uses upsert internallyThe upsert operation in Couchbase automatically handles both insert and update scenarios, making it perfect for PUT requests.
Navigate to the destroy method in the AirlineController.php file. This method deletes an airline document by its ID.
// destroy method in AirlineController.php
public function destroy($id)
{
try {
$airline = Airline::findAirline($id);
if (!$airline) {
return response()->json(['message' => 'Airline not found'], 404);
}
Airline::destroyAirline($id);
return response()->json(['message' => 'Airline deleted successfully'], 200);
} catch (\Exception $e) {
return response()->json(['message' => 'Internal Server Error'], 500);
}
}We call the destroyAirline method in the Airline model, which uses the remove method from the Couchbase SDK.
// destroyAirline method in Airline model
public static function destroyAirline($id)
{
$instance = new static;
$instance->bucket->scope('inventory')->collection('airline')->remove($id);
}Navigate to the index method in the AirlineController.php file. This method retrieves a list of airlines with optional country filtering and pagination.
// index method in AirlineController.php
public function index(Request $request)
{
try {
$offset = $request->query('offset', 0);
$limit = $request->query('limit', 10);
$country = $request->query('country');
$airlines = Airline::getAllAirlinesByCountry($country, $offset, $limit);
return response()->json($airlines);
} catch (\Exception $e) {
return response()->json(['message' => 'Internal Server Error'], 500);
}
}We call the getAllAirlinesByCountry method in the Airline model, which uses SQL++ queries to retrieve filtered results.
// getAllAirlinesByCountry method in Airline model
public static function getAllAirlinesByCountry($country, $offset = 0, $limit = 10)
{
$query = "SELECT * FROM `travel-sample`.`inventory`.`airline`";
if ($country) {
$query .= " WHERE country = '$country'";
}
$query .= " LIMIT $limit OFFSET $offset";
$result = $instance->bucket->scope('inventory')->query($query);
return collect($result->rows());
}This endpoint demonstrates SQL++ queries with JOINs to find airlines that fly to a specific destination airport.
// getAirlinesToAirport method in Airline model
public static function getAirlinesToAirport($destinationAirportCode, $offset = 0, $limit = 10)
{
$query = "
SELECT air.callsign, air.country, air.iata, air.icao, META(air).id AS id, air.name
FROM (
SELECT DISTINCT route.airlineid AS airlineId
FROM `travel-sample`.`inventory`.`route` AS route
WHERE route.destinationairport = '$destinationAirportCode'
) AS subquery
JOIN `travel-sample`.`inventory`.`airline` AS air ON META(air).id = subquery.airlineId
LIMIT $limit OFFSET $offset";
$result = $instance->bucket->scope('inventory')->query($query);
return collect($result->rows());
}This query demonstrates advanced SQL++ features:
This command will execute all the test cases in your project.
php artisan testThis project was based on the standard Laravel project.
A full list of packages are referenced in the composer.json file.
Contributions are welcome! If you'd like to contribute to this project, please fork the repository and create a pull request.
If you would like to add another entity to the APIs, follow these steps:
Create the new entity (collection) in the Couchbase bucket: You can create the collection using the SDK or via the Couchbase Server interface.
Define the model: Create a new model in the app/Models directory, similar to the existing Airline model. Note that this quickstart already includes a Hotel model as an example. For a new entity, you would create a similar structure:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class YourEntity extends Model
{
protected $bucket;
protected $fillable = [
// Define your entity fields here
];
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->bucket = app('couchbase.bucket');
}
// Add methods for querying, saving, and deleting data
}Define the controller: Create a new controller in the app/Http/Controllers directory, similar to the existing AirlineController. For example, you can create a file HotelController.php:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Hotel;
class HotelController extends Controller
{
// Add methods for handling HTTP requests for the Hotel entity
}Define the routes: In the routes/api.php file, define the routes for the new entity similar to the existing routes for airlines:
Route::prefix('v1/hotels')->group(function () {
Route::get('list', 'HotelController@index');
Route::get('{id}', 'HotelController@show');
Route::post('{id}', 'HotelController@store');
Route::put('{id}', 'HotelController@update');
Route::delete('{id}', 'HotelController@destroy');
});Integration tests: Create a new test class in the tests/Feature directory, similar to the existing tests. For example, you can create a file HotelIntegrationTest.php:
namespace Tests\Feature;
use Tests\TestCase;
class HotelIntegrationTest extends TestCase
{
// Add test methods for the Hotel endpoints
}If you are running this quickstart with a self-managed Couchbase cluster, you need to load the travel-sample data bucket in your cluster and generate the credentials for the bucket.
You need to update the connection string and the credentials in the config/couchbase.php file:
DB_CONN_STR=couchbase://<your-couchbase-server>
DB_USERNAME=<your-username>
DB_PASSWORD=<your-password>
DB_BUCKET=travel-sampleReplace <your-couchbase-server>, <your-username>, and <your-password> with your actual Couchbase server details and credentials.
NOTE: Couchbase must be installed and running prior to running the Laravel app.
Swagger documentation provides a clear view of the API including endpoints, HTTP methods, request parameters, and response objects.
Click on an individual endpoint to expand it and see detailed information. This includes the endpoint's description, possible response status codes, and the request parameters it accepts.
You can try out an API by clicking on the "Try it out" button next to the endpoints.
Parameters: If an endpoint requires parameters, Swagger UI provides input boxes for you to fill in. This could include path parameters, query strings, headers, or the body of a POST/PUT request.
Execution: Once you've inputted all the necessary parameters, you can click the "Execute" button to make a live API call. Swagger UI will send the request to the API and display the response directly in the documentation. This includes the response code, response headers, and response body.
Swagger documents the structure of request and response bodies using models. These models define the expected data structure using JSON schema and are extremely helpful in understanding what data to send and expect.