<commit_before>Sandstone Edition
===================

Build a real-time RestApi !

It uses Silex and websockets.


## Technical stack

This edition integrates:

 - [Sandstone](https://eole-io.github.io/sandstone/) (Silex with websockets)
 - **Docker** environment to mount the whole application (RestApi, websocket server, MariaDB, PHPMyAdmin)
 - **Doctrine ORM** and Doctrine commands
 - **Symfony web profiler** for debugging RestApi requests and Push events
 - [Silex annotations](https://github.com/danadesrosiers/silex-annotation-provider) for controllers and routing annotations


## Installation

Sandstone requires PHP 5.5+, ZMQ and php-zmq extension.

But the edition also has a Docker installation,
so you don't need to install PHP, ZMQ, php-zmq, mysql... using Docker.


### Docker installation

This installation requires **make**, **Docker** and **docker-compose**.

``` bash
# Install a new Sandstone project
curl -L https://github.com/eole-io/sandstone-edition/archive/dev.tar.gz | tar xz
cd sandstone-edition-dev/

# Install and mount environment
make
```

> **Note**: Sometimes you'll need to do either a
> `chown -R {your_user}:{your_group} .`
> or a
> `chmod -R 777 var/*`
> to make it work.

Check your installation by going to the diagnostic page: http://0.0.0.0:8480/hello/world.html

Docker runs the whole environment, the RestApi, the websocket server and PHPMyAdmin. You now have access to:

 - http://0.0.0.0:8480/index-dev.php/api/hello *hello world* route in **dev** mode.
 - http://0.0.0.0:8480/api/hello *hello world* route in **prod** mode.
 - http://0.0.0.0:8480/index-dev.php/_profiler/ Symfony web profiler (only dev mode).
 - http://0.0.0.0:8481/ PHPMyAdmin.
 - `ws://0.0.0.0:8482/` Websocket server.

You can now start to create your RestApi endpoints and websocket topics.

Access to the **Silex console**:

``` bash
docker exec -ti sandstone-php /bin/bash -c "php bin/console"
```

Open a bash session to PHP Docker container:

``` bash
make bash
```

#### Docker default ports

Once the environment mounted, Docker exposes by default these ports:

 - `8480:http` Web server for the RestApi (nginx)
 - `8481:http` PHPMyAdmin instance
 - `8482:ws` Websocket server


### Normal installation

This requires PHP 5.5+, ZMQ, php-zmq extension, composer, and a database.

You may need to [install ZMQ and php-zmq on Linux](https://eole-io.github.io/sandstone/install-zmq-php-linux.html).

#### Install a new Sandstone project

``` bash
composer create-project eole/sandstone-edition
cd sandstone-edition/
```

 - Create a database for your project.
 - Configure your environment in `config/environment-dev.php`.
 - Run `php bin/console orm:schema-tool:create` to initialize the database schema.

Run the websocket server with:

``` bash
php bin\websocket-server
```

Then go to the diagnostic page: `http://localhost/sandstone-edition/www/hello/world.html`

Access to the **Silex console**:

``` bash
php bin/console
```

#### Other links

> I will assume here that your webserver point to your application root with:
>
> `http://localhost/sandstone-edition/www/`

You should also access to:

 - `http://localhost/sandstone-edition/www/index-dev.php/api/hello` *hello world* route in **dev** mode.
 - `http://localhost/sandstone-edition/www/api/hello` *hello world* route in **prod** mode.
 - `http://localhost/sandstone-edition/www/index-dev.php/_profiler/` Symfony web profiler (only dev mode).
 - `ws://localhost:8482` Websocket server.


## Cookbook

Once you have a running installation of Sandstone,
you can start to build your real-time RestApi.

That means creating API endpoints, websocket topics...


### Creating an API endpoint

As Sandstone extends Silex, just create a controller class and a method, then mount it with Silex.

Also, this edition allows to use **annotations** for routing.

In **src/App/Controller**:
``` php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use DDesrosiers\SilexAnnotations\Annotations as SLX;
use Alcalyn\SerializableApiResponse\ApiResponse;

/**
 * @SLX\Controller(prefix="/api")
 */
class HelloController
{
    /**
     * Test endpoint which returns a hello world.
     *
     * @SLX\Route(
     *      @SLX\Request(method="GET", uri="hello/{name}"),
     *      @SLX\Value(variable="name", default="world")
     * )
     *
     * @param string $name
     *
     * @return ApiResponse
     */
    public function getHello($name)
    {
        $result = [
            'hello' => $name,
        ];

        return new ApiResponse($result, Response::HTTP_OK);
    }
}
```

> **Note**: Using `ApiResponse` allows to make your controller return a HTTP-agnostic object,
> and is better when used with serializer
> (see [Github alcalyn/serializable-api-response](https://github.com/alcalyn/serializable-api-response)).
> Sandstone transform the `ApiResponse` to a Symfony `Response` only at the last time (after serialization).

*Related documentation*:

 - [danadesrosiers/Silex annotations](https://github.com/danadesrosiers/silex-annotation-provider)
 - [Silex routing](http://silex.sensiolabs.org/doc/2.0/usage.html#routing)
 - [ApiResponse](https://github.com/alcalyn/serializable-api-response)

### Creating a websocket topic

A websocket topic is like a "category", or a "channel" of communication.
It allows to listen to messages from a same source,
without receive all messages from the websocket server.

Technically, it also separate each topic in classes,
then each topic has its own logic.

Under Sandstone, a topic has a name (i.e `chat/general`) and can be declared like a route.

#### Creating the topic class

In **src/App/Topic/ChatTopic.php**:
``` php<commit_msg>[Fixes #1] Clarify documentation about ApiResponse<commit_after>Sandstone Edition
===================

Build a real-time RestApi !

It uses Silex and websockets.


## Technical stack

This edition integrates:

 - [Sandstone](https://eole-io.github.io/sandstone/) (Silex with websockets)
 - **Docker** environment to mount the whole application (RestApi, websocket server, MariaDB, PHPMyAdmin)
 - **Doctrine ORM** and Doctrine commands
 - **Symfony web profiler** for debugging RestApi requests and Push events
 - [Silex annotations](https://github.com/danadesrosiers/silex-annotation-provider) for controllers and routing annotations


## Installation

Sandstone requires PHP 5.5+, ZMQ and php-zmq extension.

But the edition also has a Docker installation,
so you don't need to install PHP, ZMQ, php-zmq, mysql... using Docker.


### Docker installation

This installation requires **make**, **Docker** and **docker-compose**.

``` bash
# Install a new Sandstone project
curl -L https://github.com/eole-io/sandstone-edition/archive/dev.tar.gz | tar xz
cd sandstone-edition-dev/

# Install and mount environment
make
```

> **Note**: Sometimes you'll need to do either a
> `chown -R {your_user}:{your_group} .`
> or a
> `chmod -R 777 var/*`
> to make it work.

Check your installation by going to the diagnostic page: http://0.0.0.0:8480/hello/world.html

Docker runs the whole environment, the RestApi, the websocket server and PHPMyAdmin. You now have access to:

 - http://0.0.0.0:8480/index-dev.php/api/hello *hello world* route in **dev** mode.
 - http://0.0.0.0:8480/api/hello *hello world* route in **prod** mode.
 - http://0.0.0.0:8480/index-dev.php/_profiler/ Symfony web profiler (only dev mode).
 - http://0.0.0.0:8481/ PHPMyAdmin.
 - `ws://0.0.0.0:8482/` Websocket server.

You can now start to create your RestApi endpoints and websocket topics.

Access to the **Silex console**:

``` bash
docker exec -ti sandstone-php /bin/bash -c "php bin/console"
```

Open a bash session to PHP Docker container:

``` bash
make bash
```

#### Docker default ports

Once the environment mounted, Docker exposes by default these ports:

 - `8480:http` Web server for the RestApi (nginx)
 - `8481:http` PHPMyAdmin instance
 - `8482:ws` Websocket server


### Normal installation

This requires PHP 5.5+, ZMQ, php-zmq extension, composer, and a database.

You may need to [install ZMQ and php-zmq on Linux](https://eole-io.github.io/sandstone/install-zmq-php-linux.html).

#### Install a new Sandstone project

``` bash
composer create-project eole/sandstone-edition
cd sandstone-edition/
```

 - Create a database for your project.
 - Configure your environment in `config/environment-dev.php`.
 - Run `php bin/console orm:schema-tool:create` to initialize the database schema.

Run the websocket server with:

``` bash
php bin\websocket-server
```

Then go to the diagnostic page: `http://localhost/sandstone-edition/www/hello/world.html`

Access to the **Silex console**:

``` bash
php bin/console
```

#### Other links

> I will assume here that your webserver point to your application root with:
>
> `http://localhost/sandstone-edition/www/`

You should also access to:

 - `http://localhost/sandstone-edition/www/index-dev.php/api/hello` *hello world* route in **dev** mode.
 - `http://localhost/sandstone-edition/www/api/hello` *hello world* route in **prod** mode.
 - `http://localhost/sandstone-edition/www/index-dev.php/_profiler/` Symfony web profiler (only dev mode).
 - `ws://localhost:8482` Websocket server.


## Cookbook

Once you have a running installation of Sandstone,
you can start to build your real-time RestApi.

That means creating API endpoints, websocket topics...


### Creating an API endpoint

As Sandstone extends Silex, just create a controller class and a method, then mount it with Silex.

Also, this edition allows to use **annotations** for routing.

In **src/App/Controller**:
``` php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use DDesrosiers\SilexAnnotations\Annotations as SLX;
use Alcalyn\SerializableApiResponse\ApiResponse;

/**
 * @SLX\Controller(prefix="/api")
 */
class HelloController
{
    /**
     * Test endpoint which returns a hello world.
     *
     * @SLX\Route(
     *      @SLX\Request(method="GET", uri="hello/{name}"),
     *      @SLX\Value(variable="name", default="world")
     * )
     *
     * @param string $name
     *
     * @return ApiResponse
     */
    public function getHello($name)
    {
        $result = [
            'hello' => $name,
        ];

        return new ApiResponse($result, Response::HTTP_OK);
    }
}
```

> **Note**: Using `ApiResponse` allows to make your controllers return a non-yet-serialized object
> (see [alcalyn/serializable-api-response](https://github.com/alcalyn/serializable-api-response)).
>
> Sandstone transforms the `ApiResponse` to a Symfony `Response` only at the very end, after serialization.

*Related documentation*:

 - [danadesrosiers/Silex annotations](https://github.com/danadesrosiers/silex-annotation-provider)
 - [Silex routing](http://silex.sensiolabs.org/doc/2.0/usage.html#routing)
 - [ApiResponse](https://github.com/alcalyn/serializable-api-response)

### Creating a websocket topic

A websocket topic is like a "category", or a "channel" of communication.
It allows to listen to messages from a same source,
without receive all messages from the websocket server.

Technically, it also separate each topic in classes,
then each topic has its own logic.

Under Sandstone, a topic has a name (i.e `chat/general`) and can be declared like a route.

#### Creating the topic class

In **src/App/Topic/ChatTopic.php**:
``` php
{'fraction_non_alphanumeric': 0.1288959323824617, 'fraction_numerical': 0.012942419440042261, 'mean_word_length': 3.894011202068074, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 29, 'https://': 14, 'lorem ipsum': 0, 'www.': 10, 'xml': 0}, 'pii_count': 12, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4054384', 'n_tokens_mistral': 3709, 'n_tokens_neox': 3474, 'n_words': 1160}