diff --git a/docs/controller-providers.md b/docs/controller-providers.md index 992ea7b..ec1ae98 100644 --- a/docs/controller-providers.md +++ b/docs/controller-providers.md @@ -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; @@ -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) { @@ -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: @@ -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)) { @@ -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); } @@ -81,7 +112,7 @@ 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 @@ -89,7 +120,7 @@ $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(); diff --git a/docs/service-providers.md b/docs/service-providers.md index e01d461..3f52308 100644 --- a/docs/service-providers.md +++ b/docs/service-providers.md @@ -1,9 +1,38 @@ # 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; @@ -11,11 +40,14 @@ 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( @@ -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; @@ -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 = []) @@ -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 @@ -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 @@ -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 = [])