Cómo utilizar el Service Provider de Laravel para simplificar tu código

Spread the love

Que levante la mano quien no a haya tenido un código como este …

<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use BlockCypher\Rest\ApiContext;
use BlockCypher\Auth\SimpleTokenCredential;
use BlockCypher\Client\AddressClient;

/**
 * Class AddressController
 *
 * @package App\Http\Controllers\Web
 */
class AddressController extends Controller
{
  
    /**
     * @var ApiContext
     */
    private $apiContext;

    /**
     * AddressController constructor.
     * 
     */
    public function __construct()
    {
        $this->apiContext = ApiContext::create(env('BLOCKCYPHER_ENV', 'test3'), 'btc', 'v1', new SimpleTokenCredential($this->token),
            array('log.LogEnabled' => true, 'log.FileName' => 'BlockCypher.log', 'log.LogLevel' => 'DEBUG')
        );
    }

    ...

    /**
     *
     */
    public function create()
    {
        $addressClient = new AddressClient($this->apiContext);
        $addressKeyChain = $addressClient->generateAddress();
        ...
    }

    ...
}

Solo a los iluminados de San Taylor Otwell no les a sucedido esto.


use the Service Provider…

San Taylor Otwell

jajaja.

La verdad confieso que también he tenido código de esa forma. y con el tiempo y el uso de pruebas he mejorado.

pero te has de preguntar.

¿Qué tiene eso de malo?

Y en realidad no tiene nada de malo y no es un error, pero que pensaría si te digo que en algún tiempo eso puede darte la incomodidad de tener que hacer cambios en mas de un lugar.

¿Qué como es eso?

muy bien, que te parece si miramos mas de cerca el método create del ejemplo.

/**
 * Class AddressController
 *
 * @package App\Http\Controllers\Web
 */
class AddressController extends Controller
{
  
    ...

    /**
     * Create a new Address blockchain
     */
    public function create()
    {
        $addressClient = new AddressClient($this->apiContext);
        $addressKeyChain = $addressClient->generateAddress();
        ...
    }

    ...
}

observa la primera linea, sí notas  como la clase AddressClient recibe como parámetro la clase ApiContext mediante $this->apiContext

Esta propiedad se asigna inyectando la clase ApiContext (Que no hemos visto) en el constructor.

Con eso en mente,  ahora imagina que va a pasar cuando tengas mas clases con esa misma dependencia.

va a ser un verdadero problema, porque tienes que copiar el código del constructor en mas de un controlador

y es probable que haciendo cambios en los parámetros de configuración de la clase ApiContext tengas que cambiar también todo el código copiado a otros controladores.

En este punto puede ser que ya no te este gustando tanto tener el caso que presente en el inicio.

y es en este momento en el que te digo que existe una forma de evitar la incomodidad que puedes tener en el futuro.

Mediante algunos cambios realmente sencillos, aprovechando el Service Provider y el contenedor de dependencias que nos proporciona Laravel.

Podemos llegar a tener algo mas sencillo de leer y mantener

/**
 * Class AddressController
 *
 *
 * @package App\Http\Controllers\Web
 */
class AddressController extends Controller
{
   ...

    /**
     * AddressController constructor.
     * @param AddressClient $client
     */
    public function __construct(AddressClient $client)
    {
        $this->client = $client;
    }

    ....

    /**
     * Create a new address blockchain  
     */ 
    public function create()
    {
        $addressKeyChain = $this->client->generateAddress();
        ....
    }

    ....
}
Shocked dog

Si, todo eso va a hacer Laravel por tí.

Así que veamos como.

Mudando nuestro código al  Service Provider

La razón para esto, es que es un excelente punto para colocar el código que requiere ser configurado o tiene dependencias con otras clases.

y es donde Laravel hace su magia y nos ayuda a crear objetos por nosotros!.

En nuestro ejemplo lo primero que vamos a hacer, sera mover los datos que pueden estar en el config.

Moviendo elementos al config.

Este es uno de los lugares que a veces  no utilizamos, y es genial para tener datos que nos servirán para configurar el estado inicial de nuestras clases.

Así que, vamos a meter todos los valores que recibe nuestro ApiContext en un archivo de configuración que vamos a nombrar como config/block_cyhper.php

return [
 
    'key' => env('BLOCKCYPHER_TOKEN'),
 
    'chain' => env('BLOCKCYPHER_ENV', 'test3'),
 
    'coin' => 'btc',
 
    'version' => 'v1',
 
    'config' => [
 
        'mode' => 'sandbox',
 
        'log.LogEnabled' => true,
 
        'log.FileName' => 'BlockCypher.log',
 
        'log.LogLevel' => 'DEBUG'
 
    ],
];

De esta forma si quieres cambiar los parámetros solo tienes que hacerlo desde el config sin tocar nada en el controlador.

Creando nuestro Service Provider

Llego la hora de la verdad y vamos a hacer la parte mas difícil, pero no te preocupes en realidad lo estoy diciendo para asustar!

Muy bien creemos nuestro Service Provider

	
$ php artisan make:provider BlockCypherProvider

Ahora abre el archivo que se genero en app/Provider/BlockChyperService.php

y en el método register hacemos los cambios necesarios y hacemos uso de singleton.

<?php

namespace App\Providers;

use BlockCypher\Auth\SimpleTokenCredential;
use BlockCypher\Rest\ApiContext;
use Illuminate\Support\ServiceProvider;

class BlockCypherProvider extends ServiceProvider
{
    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = false;

    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton(ApiContext::class, function($app){
            $config = $app->make('config')->get('block_cypher');

            return ApiContext::create(
                $config['chain'],
                $config['coin'],
                $config['version'],
                new SimpleTokenCredential($config['key']),
                $config['config']
            );
        });

    }

}

Con esto le indicamos al contenedor de dependencias que resuelva y cree por nosotros la clase ApiContext, y solo podrá tener una sola instancia por petición.

ademas observa que podemos acceder al archivo de configuración para pasar parámetros.

Pero vamos que no hemos terminado; pero antes veamos como esta quedando nuestro controlador.

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use BlockCypher\Rest\ApiContext;
use BlockCypher\Auth\SimpleTokenCredential;
use BlockCypher\Client\AddressClient;

/**
 * Class AddressController
 *
 *
 * @package App\Http\Controllers\Web
 */
class AddressController extends Controller
{
    /**
     * @var ApiContext
     */
    private $apiContext;

    /**
     * AddressController constructor.
     * @param ApiContext $apiContext
     */
    public function __construct(ApiContext $apiContext)
    {
        $this->apiContext = $apiContext;
    }

    ....

    /**
     * Create a new address blockchain  
     */ 
    public function create()
    {
        $addressClient = new AddressClient($this->apiContext);
        $addressKeyChain = $addressClient->generateAddress();
        ....
    }

    ....

}

¿Qué te parece? mil veces mas sencillo.

pero… se puede mejorar, así que demos un paso mas.

volvamos al método register y ahora hagamos uso del bind para nuestra clase AddressClient

<?php

namespace App\Providers;

use BlockCypher\Auth\SimpleTokenCredential;
use BlockCypher\Client\AddressClient;
use BlockCypher\Rest\ApiContext;
use Illuminate\Support\ServiceProvider;

class BlockCypherProvider extends ServiceProvider
{
    /**
     * Indicates if loading of the provider is deferred.
     *
     * @var bool
     */
    protected $defer = false;

    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton(ApiContext::class, function($app){
            $config = $app->make('config')->get('block_cypher');

            return ApiContext::create(
                $config['chain'],
                $config['coin'],
                $config['version'],
                new SimpleTokenCredential($config['key']),
                $config['config']
            );
        });

        $this->app->bind(AddressClient::class, function($app){
            $apiContext = $app->make(ApiContext::class);

            return new AddressClient($apiContext);
        });

    }

}

Observa como construimos el AddressClient pasando a su constructor el ApiContext.

Así que manos a la obra vamos por el ultimo paso, así que ajustemos el constructor de nuestro controlador y el método create.

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use BlockCypher\Client\AddressClient;

/**
 * Class AddressController
 *
 *
 * @package App\Http\Controllers\Web
 */
class AddressController extends Controller
{
    /**
     * @var AddressClient
     */
    private $client;

    /**
     * AddressController constructor.
     * @param AddressClient $client
     */
    public function __construct(AddressClient $client)
    {
        $this->client = $client;
    }

    ....

    /**
     * Create a new address blockchain  
     */ 
    public function create()
    {
        $addressKeyChain = $this->client->generateAddress();
        ....
    }

    ....

}

¿Qué te parece ahora? mucho mejor, mas sencillo ahora solo se tiene que inyectar solo una clase y en general el código es menos complejo de leer.

Que sigue

Dos cosas, aplicar la idea que acabamos de ver en tú código, comienza con una clase o un controlador y ve como te queda.

Ademas es importante que leas la documentación, el Service Provider es una pieza fundamental para que crees código potable que después puedes utilizar en otros proyecto de Laravel

También es un punto de entrada para utilizar el Service Container que en nuestro ejemplo nos ayudo a crear una clase con dependencias, pero tienes mucha utilidad.

Y por ultimo espero que te haya gustado el articulo, y recuerda si tienes duda deja tu mensaje!.

Y recuerda compartir de esa forma nos ayudas a llegar mas lejos.