Skip to content

Commit

Permalink
docs: Improve the documentation for controller and service providers
Browse files Browse the repository at this point in the history
  • Loading branch information
MontealegreLuis committed Sep 2, 2017
1 parent 492ffa2 commit 6ff91f3
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 30 deletions.
63 changes: 47 additions & 16 deletions docs/controller-providers.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
# Controller providers

Following the example, you would have routes to update the information of your products.
In order to group the routes of your module in a single class, you need to implement
`ComPHPPuebla\Slim\ControllerProvider`
A controller provider is a special type of service provider. A controller provider binds a service
definition to a Slim route.

## Creating a controller provider

Following the catalog of products example, let's register some routes to update the information of
existing products. In order to group the routes of your module in a single class, you need to
implement the interface `ComPHPPuebla\Slim\ControllerProvider`

```php
use Slim\Slim;

interface ControllerProvider
{
public function register(Slim $app, Resolver $resolver);
}
```

Suppose you want to register two routes, one to show the form to edit a product (GET) and another
to update the product information in the database (POST).

```php
namespace Modules\ProductCatalog;
Expand All @@ -11,7 +28,13 @@ use ComPHPPuebla\Slim\ControllerProvider;
use ComPHPPuebla\Slim\Resolver;
use Slim\Slim;

class ProductCatalogControllers implements ControllerProvider
/**
* This provider registers to routes
*
* - `GET /catalog/product/edit/:id`
* - `POST /catalog/product/update`
*/
class CatalogControllerProvider implements ControllerProvider
{
public function register(Slim $app, Resolver $resolver)
{
Expand All @@ -28,15 +51,16 @@ class ProductCatalogControllers implements ControllerProvider
The `Resolver` class resolves the controller, method, and arguments to be
used when Slim matches the route being defined. It works the following way:

* It splits the string with the format `controller_key:method`, looking for the
controller in the application container using the `controller_key` part, and it will
use the `method` part to build a valid `callable`.
* The controller won't be instantiated unless the route is matched. The resolver
generates a function, instead of instantiating the object (giving the same effect as
if you were using `$app->container->protect`). This function will create the
controller, and it will pass the original route's arguments, the request
and the Slim application as arguments to your controller method.
* Once the arguments are resolved it will execute the method.
1. It splits the string with the format `controller_key:method`
1. It looks for the controller in the application container using the `controller_key` part
1. It will use both the controller and the `method` to build a `callable` (`[$object, 'methodName']`).
1. This callable will be wrapped in an anonymous function, that will acts as a proxy to your
controller (similar to `$app->container->protect` which returns a function instead of the object).
This "controller function" has the following characteristics:
* It will create your controller once the route is matched
* It will pass the original route's arguments, as well as the request and the Slim application
to your controller method
* It will execute the controller method

Let's suppose the controller you registered in your provider is as follows:

Expand All @@ -60,6 +84,9 @@ class ProductController
$this->catalog = $catalog;
}

/**
* @param int $productId The `:id` coming from the path `/catalog/product/edit/:id`
*/
public function showProductForm($productId, Request $request, Slim $app)
{
if (!$product = $this->catalog->productOf($productId)) {
Expand All @@ -69,10 +96,14 @@ class ProductController
// Populate your form and pass it to the view
}

/**
* When there are no parameters in the path, like in this case: `/catalog/product/update`.
* The first argument will be the `Request` object, then the Slim application
*/
public function updateProductInformation(Request $request)
{
if ($this->form->isValid($request->params()) {
$product = $this->catalog->productOf($this->form->getProductId());
$product = $this->catalog->productWith($this->form->getProductId());
$product->update($this->form->getValues());
$this->catalog->update($product);
}
Expand All @@ -81,15 +112,15 @@ class ProductController
}
```

In order to add your controller to your application you would register it in your
In order to add your controllers to your application you would register the provider in your
`index` file.

```php
$app = new Slim\Slim();

/* Register your services first */

$controllers = new Modules\ProductCatalog\ProductCatalogControllers();
$controllers = new Modules\ProductCatalog\CatalogControllerProvider();
$controllers->register($app, new ComPHPPuebla\Slim\Resolver());

$app->run();
Expand Down
77 changes: 63 additions & 14 deletions docs/service-providers.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,53 @@
# Service providers

You can package your module's services definitions in a single class implementing
A service is an object that does something as part of a larger system. Examples of services: a
database connection, a templating engine, an HTTP client, or a mailer. Almost any global object
can be a service.

With a service provider you will be able to:

* Register services in the Slim application container
* Extend existing service definitions, which is a feature the original Slim service container lacks
* Pass parameters like: paths, passwords, API keys, and any configuration value that should not be
hard-coded in the provider

## Contents

* [Creating service providers](#creating-service-providers)
* [Integrating third-party libraries](#integrating-third-party-libraries)
* [Extending service definitions](#extending-service-definitions)

## Creating service providers

In order to package your services definitions in a class, it must implement the
`ComPHPPuebla\Slim\ServiceProvider` interface.

Suppose you have a product catalog module that is part of a small application.
```php
use Slim\Slim;

interface ServiceProvider
{
public function configure(Slim $app, Resolver $resolver, array $options = []);
}
```

Suppose you have a product catalog module that is part of a Slim application. This module defines
a repository and a controller for products.

```php
namespace Modules\ProductCatalog;

use ComPHPPuebla\Slim\ServiceProvider;
use ComPHPPuebla\Slim\Resolver;

/**
* This provider registers the following services
*
* - `catalog.product_repository`
* - `catalog.product_controller`
*/
class ProductCatalogServices implements ServiceProvider
{
/**
* Register all your module services as usual
*/
public function configure(Slim $app, Resolver $resolver, array $options = [])
{
$app->container->singleton(
Expand Down Expand Up @@ -50,10 +82,10 @@ $services->configure($app, new ComPHPPuebla\Slim\Resolver());
$app->run();
```

## Integrating libraries
## Integrating third-party libraries

You can also use a service provider to integrate third party libraries like Twig.
As shown in the following example:
You can also use a service provider to integrate third-party libraries. The following is an example
of a service provider for Twig:

```php
use ComPHPPuebla\Slim\ServiceProvider;
Expand All @@ -62,6 +94,12 @@ use Slim\Slim;
use Twig_Loader_Filesystem as Loader;
use Twig_Environment as Environment;

/**
* This provider registers the following services
*
* - `twig.loader`
* - `twig.environment`
*/
class TwigServiceProvider implements ServiceProvider
{
public function configure(Slim $app, Resolver $resolver, array $options = [])
Expand All @@ -81,7 +119,7 @@ pass values like filesystem paths, and other configuration settings that you
don't want to hard-code in your providers.

The following is an example of what kind of values you would pass to your provider.
They are still hard-coded to simplify the example, but this values should come from
They are still hard-coded to simplify the example, but these values should come from
a configuration file or from environment variables.

```php
Expand All @@ -104,14 +142,20 @@ $twig->configure($app, new Resolver(), [
$app->run();
```

## Extending services
## Extending service definitions

If you have used Twig, you will know it is a common use case to register extensions.
In this case you will need to extend a service definition, i. e. take the original
service and modify it. Suppose our module needs to register its own Twig extension.
You would have to use the `Resolver` and call its `extend` method. You will need to
pass your Slim application, the original service key, and a callable to modify the
original service as arguments. The callable will receive the original service object
service and modify it.

Suppose our module needs to register its own Twig extension. You would have to use the `Resolver`
and call its `extend` method.

You will need to pass 3 arguments:

* Your Slim application
* The original service key and
* A callable to modify the original service. The callable will receive the original service object
as argument.

```php
Expand All @@ -120,6 +164,11 @@ use ComPHPPuebla\Slim\Resolver;
use Slim\Slim;
use Twig_Environment as Environment;

/**
* This provider extends the following service
*
* - `twig.environment`
*/
class ProductCatalogServices implements ServiceProvider
{
public function configure(Slim $app, Resolver $resolver, array $options = [])
Expand Down

0 comments on commit 6ff91f3

Please sign in to comment.