Desarrollo de paquetes en Laravel

Author
By Darío Rivera
Posted on 2020-11-05 in Laravel
Tags   Laravel 6x Laravel 7x

A medida que vas desarrollando proyectos en Laravel surge la necesidad de centralizar una que otra funcionalidad que comienza a repetirse en varios proyectos. Hace un tiempo vimos cómo crear una librería instalable con composer para hacer esto mismo en proyectos que solo requieren PHP. El día de hoy veremos cómo hacer esto mismo pero agregándole todas las funcionlidades de laravel tales como migraciones, configuraciones, modelos, providers, jobs, etc.

Configuración

Para crear un paquete la mejor opción es trabajar sobre un proyecto ya existente de Laravel. Lo que debemos hacer es crear la siguiente jerarquía de carpetas en la raíz de nuestro proyecto.

mkdir -p packages/project/package-name

De esta forma, puedes cargar tu paquete llamado package-name agregando la siguiente línea en el archivo composer.json del proyecto principal de laravel.

"psr-4": {
  "App\\": "app/",
  "Project\\PackageName\\": "packages/project/package-name/src"
}

Nota que la carga del directorio app viene por defecto en Laravel. Adicional a esto con la línea que hemos agregado le decimos a composer que cargue todo lo que esté en el directorio src de nuestra nueva librería.

El siguiente paso consiste en crear el scaffolding que requiere cualquier librería realizada en PHP dentro del directorio que hemos creardo (package-name). Para esto puedes seguir al pie de la letra el tutorial del que hablamos al comienzo de este post.

Cómo crear una librería en PHP instalable con composer

Una vez hecho esto, podemos probar si la configuración hecha es correcta. Para esto vamos crear la siguiente clase dentro de la carpeta src de nuestra librería.

namespace Project\PackageName;

class HolaMundo
{
    public static function print()
    {
        return 'Hola mundo!';
    }
}

Seguidamente vamos a ejecutar el siguiente comando en el proyecto de laravel para cargar las clases de nuestra librería.

composer dump-autoload

Si todo ha salido bien, podrías entrar a tinker y ejecutar el método estático que hemos creado en la librería obteniendo el siguiente resultado.

>>> Project\PacakgeName\HolaMundo::print()
=> "Hola mundo!"

Recuerda que esto solo prueba que cargaste la librería desde el proyecto de laravel. Internamente la librería debería tener su propio autoloader definido en el archivo composer.json tal y como se explica en el tutorial crear librería en php instalable con composer.

Scaffolding de paquetes en Laravel

Una vez tengamos el scaffolding clásico de toda librería en PHP vamos a instalar el framework de laravel.

composer require laravel/framework

Lo siguiente que debes hacer es crear el service provider del paquete de Laravel. Como ya sabes, los servicios son cargados a través de service providers en el bootstraping de la aplicación.

namespace Project\PackageName;

use Illuminate\Support\ServiceProvider;

class PackageNameProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot(): void
    {
        // bootstraping
    }
}

No olvides que este provider debes agregarlo a tu archivo config/app.php en la sección de providers.

'providers' => [
    ...
    Project\PackageName\PackageNameProvider,
]

Seguido a esto podrías agregarle a tu librería todo lo que necesites tal como migraciones, jobs, providers, observers, etc. Vamos a ver entonces cómo agregar cada uno de estos componentes y cómo utilizarlos.

Configuraciones

En la carpeta raíz de nuestra librería debemos crear la carpeta de configuraciones.

mkdir config

Siguiente a esto podemos cargar la configuraciones para que se puedan publicar.

public function boot(): void
{
    // ...
    $this->loadConfig();
}

private function loadConfig(): void
{
    $this->publishes(
        [
            __DIR__ . '/../config/something.php' => config_path('something.php'),
        ],
        'package-name-config'
    );
}

Con la función publishes se copiará las configuraciones en el directorio config para poder modifcarlas a disposición de nuestro proyecto.

Para publicar las configuraciones se debe utilizar el tag utilizado como segundo parámetro en el método publishes.

php artisan vendor:publish --tag="package-name-config"

Rutas

En la carpeta raíz de nuestra librería debemos crear la carpeta de rutas.

mkdir routes

Siguiente a esto podemos cargar las rutas y configurarlas para que se puedan publicar.

public function boot(): void
{
    // ...
    $this->loadRoutes();
}

private function loadRoutes(): void
{
    $this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
    $this->loadRoutesFrom(__DIR__ . '/../routes/api.php');

    $this->publishes(
        [
            __DIR__ . '/../routes' => base_path('routes'),
        ],
        'package-name-routes'
    );
}

Con la función loadRoutesFrom las rutas estarán automáticamente disponibles en nuestro proyecto de laravel. Adicional a esto, de ser necesario el método publishes copiará las rutas en el directorio routes para poder modifcarlas a disposición de nuestro proyecto.

Para publicar las rutas se debe utilizar el tag utilizado como segundo parámetro en el método publishes.

php artisan vendor:publish --tag="package-name-routes"

Migraciones

En la carpeta raíz de nuestra librería debemos crear la carpeta de migraciones.

mkdir -p database/migrations

Siguiente a esto podemos cargar las migraciones y configurarlas para que se puedan publicar.

public function boot(): void
{
    // ...
    $this->loadMigrations();
}

private function loadMigrations(): void
{
    $this->loadMigrationsFrom(__DIR__ . '/../database/migrations');

    $this->publishes(
        [
            __DIR__ . '/../database/migrations' => base_path('database/migrations'),
        ],
        'package-name-migrations'
    );
}

Con la función loadMigrationsFrom las migraciones estarán automáticamente disponibles en nuestro proyecto de laravel. Adicional a esto, de ser necesario el método publishes copiará las migraciones en el directorio database/migrations para poder modifcarlas a disposición de nuestro proyecto.

Para publicar las migraciones se debe utilizar el tag utilizado como segundo parámetro en el método publishes.

php artisan vendor:publish --tag="package-name-migrations"

Factories

En la carpeta raíz de nuestra librería debemos crear la carpeta de factories.

mkdir -p database/factories

Siguiente a esto podemos cargar las factories y configurarlas para que se puedan publicar.

public function boot(): void
{
    // ...
    $this->loadFactories();
}

private function loadFactories(): void
{
    $this->loadFactoriesFrom(__DIR__ . '/../database/factories');

    $this->publishes(
        [
            __DIR__ . '/../database/factories' => base_path('database/factories'),
        ],
        'package-name-factories'
    );
}

Con la función loadFactoriesFrom las factories estarán automáticamente disponibles en nuestro proyecto de laravel. Adicional a esto, de ser necesario el método publishes copiará las factories en el directorio database/factories para poder modifcarlas a disposición de nuestro proyecto.

Para publicar las factories se debe utilizar el tag utilizado como segundo parámetro en el método publishes.

php artisan vendor:publish --tag="package-name-factories"

Una vez las factories hayan sido registradas pueden ser utilizadas en tu aplicación.

factory(Project\PackageName\Model::class)->create();

Traducciones

En la carpeta raíz de nuestra librería debemos crear la carpeta de traducciones.

mkdir -p resources/lang

Siguiente a esto podemos cargar las traducciones y configurarlas para que se puedan publicar.

public function boot(): void
{
    // ...
    $this->loadTranslations();
}

private function loadTranslations(): void
{
    $this->loadTranslationsFrom(__DIR__ . '/../resources/lang', 'package-name-trans');

    $this->publishes(
        [
            __DIR__ . '/../resources/lang' => resource_path('lang'),
        ],
        'package-name-lang'
    );
}

Con la función loadTranslationsFrom las traducciones estarán automáticamente disponibles en nuestro proyecto de laravel. Adicional a esto, de ser necesario el método publishes copiará las traducciones en el directorio resources/lang para poder modifcarlas a disposición de nuestro proyecto.

Para publicar las traducciones se debe utilizar el tag utilizado como segundo parámetro en el método publishes.

php artisan vendor:publish --tag="package-name-lang"

Una vez las traducciones hayan sido registradas pueden ser utilizadas en tu aplicación.

__('package-name-trans::file.key')

Vistas

En la carpeta raíz de nuestra librería debemos crear la carpeta de vistas.

mkdir -p resources/views

Siguiente a esto podemos cargar las vistas y configurarlas para que se puedan publicar.

public function boot(): void
{
    // ...
    $this->loadViews();
}

private function loadViews(): void
{
    $this->loadViewsFrom(__DIR__ . '/../resources/views', 'package-name-view');

    $this->publishes(
        [
            __DIR__ . '/../resources/views' => resource_path('views/vendor/package-name'),
        ],
        'package-name-views'
    );
}

Con la función loadViewsFrom las vistas estarán automáticamente disponibles en nuestro proyecto de laravel. Adicional a esto, de ser necesario el método publishes copiará las vistas en el directorio resources/views/vendor/package-name para poder modifcarlas a disposición de nuestro proyecto.

Para publicar las traducciones se debe utilizar el tag utilizado como segundo parámetro en el método publishes.

php artisan vendor:publish --tag="package-name-views"

Una vez las vistas hayan sido registradas pueden ser utilizadas en tu aplicación.

view('package-name-view::name')

Componentes

Teniendo en cuenta la sección anterior podemos crear componentes personalizados a partir de nuestras vistas. Para esto primero debemos crear la carpeta de componentes.

mkdir -p resources/views/components

Seguido a esto podemos registrar nuestros componentes.

use Illuminate\Support\ServiceProvider;

// ...

public function boot(): void
{
    // ...
    $this->loadComponents();
}

private function loadComponents(): void
{
     Blade::component('package-name-view::components.my-component', 'my_component');
}

Una vez los componentes hayan sido registrados pueden ser utilizados en tu aplicación.

@my_component @endmy_component

Desde laravel 7x puedes hacer uso de componentes basados en clases. Para esto debes crear el directorio de las clases de los componentes.

mkdir -p src/View/Components

Seguido a esto podemos cargar los componentes.

use Project\PackageName\View\Components\Alert;

// ...

public function boot(): void
{
    // ...
    $this->loadClassComponents();
}

private function loadClassComponents(): void
{
    $this->loadViewComponentsAs('package-name-comp', [
        Alert::class,
    ]);
}

Una vez los componentes hayan sido registrados pueden ser utilizados en tu aplicación.

<x-package-name-comp-alert />

Comandos

En la carpeta raíz de nuestra librería debemos crear la carpeta de comandos.

mkdir -p src/Console/Commands

Siguiente a esto podemos cargar las comandos.

use Package\PackageName\Console\Commands\FooCommand;

// ...

public function boot(): void
{
    // ...
    $this->loadCommands();
}

private function loadCommands(): void
{
    if ($this->app->runningInConsole()) {
        $this->commands([
            FooCommand::class,
        ]);
    }
}

Si te ha gustado este artículo puedes invitarme a tomar una taza de café

Acerca de Darío Rivera

Author

Ingeniero de desarrollo en PlacetoPay , Medellín. Darío ha trabajado por más de 6 años en lenguajes de programación web especialmente en PHP. Creador del microframework DronePHP basado en Zend y Laravel.

Sólo aquellos que han alcanzado el éxito saben que siempre estuvo a un paso del momento en que pensaron renunciar.