In addition to being the domain I landed this blog on, Fliglio is the name of the PHP Framework we built and have been using at work for several years now. Recently we went through the effort of cleaning it up to make available on github.
In this post I will attempt to show you how to use Fliglio with a Getting Started guide for RESTful microservices.
Before we go any further, grab a copy of the demo application here.
git clone https://github.com/fliglio/rest-gs.git
This demo requires MySQL to back the todo entries (along with apache or nginx and the appropriate version of php).
If that sounds like a pain in the ass, you can also use Docker. The demo repo comes with a Makefile to easily run Fliglio services in a Docker container.
…Let’s just use Docker.
(If you’re on OS X, make sure your have docker-machine installed and running. On linux, you just need Docker installed.)
composer up
make run
This will pull down the Docker image fliglio/local-dev
and run the app in a container. It’s spitting out logs to stdout and will keep running until you hit CTRL+c
.
With that going, open a new terminal and run:
make migrate
This will apply the phinx database migrations for the app (see db/migrations).
Add a todo
$ curl -s -X POST localhost/todo -d '{"description": "take out the trash", "status": "new"}' | jq .
{
"id": "1",
"status": "new",
"description": "take out the trash"
}
query for that todo we just created
$ curl -s localhost/todo/1 | jq .
{
"id": "1",
"status": "new",
"description": "take out the trash"
}
Cool! Let’s look at the code now.
/httpdocs
directory contains a lightweight index.php
to bootstrap the application/src
contains the meat of the application.At a high level, the application comes together by having a main application class (\Demo\DemoApplication) that is configured by one or more configuration classes (\Demo\DemoConfiguration) which manages your resources (\Demo\Resources\TodoResource.)
I’m sure I could explain everything about the framework, but I’m also sure you wouldn’t care. So let’s just jump in and look at the service.
This should be pretty familiar if you’re used to REST apis. We’re defining methods on this class to handle providing basic CRUD functionality for todos with http verbs.
By hinting the parameters to these methods with classes like PathParam
, GetParam
, and Entity
, we can specify
what parameters we need to perform an action (and allow the framework to inject them rather than parsing
the Request directly.)
<?php
class TodoResource {
private $db;
private $weather;
public function __construct(TodoDbm $db, WeatherClient $weather) {
$this->db = $db;
$this->weather = $weather;
}
// GET /todo
public function getAll(GetParam $status = null) {
$todos = $this->db->findAll(is_null($status) ? null : $status->get());
return Todo::marshalCollection($todos);
}
// GET /todo/weather
public function getWeatherAppropriate(GetParam $city, GetParam $state, GetParam $status = null) {
$status = is_null($status) ? null : $status->get();
$weather = $this->weather->getWeather($city->get(), $state->get());
error_log(print_r($weather->marshal(), true));
$outdoorWeather = $weather->getDescription() == "Clear";
$todos = $this->db->findAll($status, $outdoorWeather);
return Todo::marshalCollection($todos);
}
// GET /todo/:id
public function get(PathParam $id) {
$todo = $this->db->find($id->get());
if (is_null($todo)) {
throw new NotFoundException();
}
return $todo->marshal();
}
// POST /todo
public function add(Entity $entity, ResponseWriter $resp) {
$todo = $entity->bind(Todo::getClass());
$this->db->save($todo);
$resp->setStatus(Http::STATUS_CREATED);
return $todo->marshal();
}
// PUT /todo/:id
public function update(PathParam $id, Entity $entity) {
$todo = $entity->bind(Todo::getClass());
$todo->setId($id->get());
$this->db->save($todo);
return $todo->marshal();
}
// DELETE /todo/:id
public function delete(PathParam $id) {
$todo = $this->db->find($id->get());
if (is_null($todo)) {
throw new NotFoundException();
}
$this->db->delete($todo);
}
}
(Don’t worry too much about the method getWeatherAppropriate
, this is a contrived
example to aid in showing how to test all of this.)
Those comments aren’t magical however; we still need to map this functionality to urls. We manage this in the configuration class
<?php
class DemoConfiguration extends DefaultConfiguration {
// Database Mapper
protected function getDbm() {
$dsn = "mysql:host=localhost;dbname=todo";
$db = new \PDO($dsn, 'admin', 'changeme', [\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8']);
$db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
return new TodoDbm($db);
}
// Todo Resource
protected function getTodoResource() {
return new TodoResource($this->getDbm());
}
public function getRoutes() {
$resource = $this->getTodoResource();
return [
RouteBuilder::get()
->uri('/todo')
->resource($resource, 'getAll')
->method(Http::METHOD_GET)
->build(),
RouteBuilder::get()
->uri('/todo/:id')
->resource($resource, 'get')
->method(Http::METHOD_GET)
->build(),
RouteBuilder::get()
->uri('/todo')
->resource($resource, 'add')
->method(Http::METHOD_POST)
->build(),
RouteBuilder::get()
->uri('/todo/:id')
->resource($resource, 'update')
->method(Http::METHOD_PUT)
->build(),
RouteBuilder::get()
->uri('/todo/:id')
->resource($resource, 'delete')
->method(Http::METHOD_DELETE)
->build(),
];
}
}
And there you have it! A framework for developing REST services.
That wasn’t a terribly in depth introduction to Fliglio, but I’ve gotta start somewhere, right?
Before you go off and build your own app though, take a look at Testing PHP Fliglio Microervices with Docker.
comments powered by Disqus