Desarrollo de paquetes en Laravel
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 nuevo 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.
Otra cosa a tener en cuenta es que si tu librería tiene dependencias estas no se cargarán en el autoloader de la aplicación debido a que solo estamos cargando lo que está en src. Si ese es el caso y queremos cargar el proyecto junto con todas sus dependencias olvida lo anterior y agrega lo siguiente al archivo composer.json:
"repositories": [
{
"type": "path",
"url": "/proyect_path/packages/pleets/laravel-paypal"
}
]
Después de esto debes instalar el repositorio de la siguiente forma:
composer require proyect/package-name@dev-master
Scaffolding de librería en PHP
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,
]);
}
}