Creare un package per Laravel è un ottimo modo per estenderne le funzionalità, e non è così difficile come potresti pensare.
In questo tutorial ti racconto come realizzarne uno molto semplice e, volendo, come condividerlo su Github e Packagist.
L'obiettivo è quello di realizzare una libreria per la generazione in tempo reale di una sitemap.
Si chiamerà Sitemapper e sarà una libreria per siti di poche pagine, come questo, e, alla richiesta dell'url /sitemap.xml, si occuperà di generare l'albero xml secondo le indicazioni fornite da codice.
Per lo sviluppo del package ti suggerisco di dedicare un'installazione di Laravel a questo scopo.
Il vantaggio è che puoi raccogliere, testare e gestire tutti i vari packages in un unico posto.
Crea quindi una cartella packages
nella root dell'installazione di Laravel, allo stesso livello delle cartelle app
, bootstrap
, config
, etc.
Per creare un package devi seguire alcune linee guida:
La gerarchia dei file di Sitemapper è la seguente:
Come accennato poco fa, apri il terminale e posizionati nella cartella tuonome/sitemapper e crea il file composer.json tramite il comando composer init
.
Questo comando ti guida nella creazione chiedendoti le informazioni per le varie voci del file.
Qui di seguito trovi quello creato per Sitemapper:
{
"name": "tuonome/sitemapper",
"type": "library",
"keywords": ["laravel", "sitemap","generator"],
"homepage": "",
"description": "Ridiculously small package for manual sitemap generation in small websites",
"authors": [
{
"name": "Nome Cognome",
"email": "info@example.com",
"role": "Developer"
}
],
"license": "MIT",
"require": {
"php": ">=5.5.9"
},
"autoload": {
"classmap": [
"src"
],
"psr-4": {
"Tuonome\\Sitemapper\\": "src/"
}
},
"minimum-stability": "",
"require": {}
}
L'altra operazione che ti rimane da fare, a questo punto, è creare un nuovo repository GIT dentro la cartella tuonome/sitemapper eseguendo git init
e facendo poi il first commit di rito.
Per far sì che il package venga caricato da Laravel, devi indicare il percorso nel composer.json nella root del sito.
Dentro alla voce psr-4, sotto la riga già presente, inserisci:
"Tuonome\\Sitemapper\\": "packages/tuonome/sitemapper/src"
.
Indicando che il namespace Tuonome\Sitemapper si trova in packages/tuonome/sitemapper/src, l'autoloader sa dove andare a cercare i file.
Se vuoi lasciare le cose ad un livello base, non ti serve implementare nessun Service Provider.
Ma se hai un file di configurazione, una route, un controller o altri elementi che devono essere utilizzati da Laravel, diventa indispensabile.
Poniamo caso che vuoi aggiungere un file di configurazione per modificare una preferenza di Sitemapper: crea, allora, il SitemapperServiceProvider, digitando
artisan make:provider SitemapperServiceProvider
Questo comando si occupa di creare il file all'interno della cartella app/providers: prendilo e spostalo dentro la cartella src del package, aggiornando, ovviamente, il namespace.
Il service provider estende la classe astratta ServiceProvider e richiede la presenza di due metodi: boot e register.
Ecco il codice:
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
// pubblica il file di ocnfigurazione nella cartella config/
$this->publishes([__DIR__.'/config/sitemapper.php' => config_path('sitemapper.php')]);
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
//qui nulla
}
Quello che serve è che nel metodo boot() venga invocato il metodo publishes() con i due percorsi, quello del file dentro il package e quello del file dentro config.
Nel metodo register() non inserire nulla: per lo scopo del tutorial, puoi lasciarlo vuoto.
Manca ancora un passaggio: il caricamento del service provider durante l'avvio di Laravel.
Inserisci all'interno di config/app.php, nell'array providers : Tuonome\Sitemapper\SitemapperServiceProvider::class
Dopo aver eseguito questa operazione, digita artisan vendor:publish
e il file di configurazione verrà spostato nella cartella config.
Il codice di Sitemapper è molto semplice: permette di aggiungere nodi alla sitemap tramite il metodo addUrl() e creare la sitemap richiamando il metodo render().
Ha un po' di variabili:
/**
* @var string
*/
protected $header = '<?xml version="1.0" encoding="UTF-8"?>';
/**
* @var string
*/
protected $open_urlset = '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
/**
* @var string
*/
protected $end_urlset = '</urlset>';
/**
* @var string
*/
protected $compositing = '';
/**
* @var string
*/
protected $sitemap_file_name_to_verify;
che si occupano di indicare l'header del file xml ($header), l'elemento contenitore urlset ($open_urlset e $end_urlset), inizializzare una variabile in cui inserire di volta in volta i vari nodi ($compositing) e definire una variabile col nome del file su cui viene fatta una verifica ($sitemap_file_name_to_verify): Sitemapper, infatti, verifica che non sia già presente un file fisico nella root del sito.
Nel caso ci sia, restituisce quel file, altrimenti genera la sitemap: in $sitemap_file_name_to_verify indichiamo appunto il nome del file da verificare.
Il codice della libreria vera e proprira è il seguente:
/**
* Sitemapper constructor.
*/
function __construct()
{
if (!is_null(config('sitemapper.sitemap_file_name_to_verify'))) {
$this->sitemap_file_name_to_verify = config('sitemapper.sitemap_file_name_to_verify');
} else {
$config_package = include(__DIR__ . '/config/sitemapper.php');
$this->sitemap_file_name_to_verify = $config_package['sitemap_file_name_to_verify'];
}
}
/**
* Aggiunge un nodo nell'albero xml
* @param $location
* @param string $lastmod
* @param string $changefreq
* @param string $priority
*/
public function addUrl($location, $lastmod = null, $changefreq = null, $priority = null) {
$output = '<url>';
$output .= '<loc>' . url($location) . '</loc>';
if (!is_null($lastmod)) {
$datetime = new \DateTime($lastmod);
$output .= '<lastmod>' . $datetime->format('Y-m-d\TH:i:sP') . '</lastmod>';
}
if (!is_null($changefreq)) $output .= '<changefreq>' . $changefreq . '</changefreq>';
if (!is_null($priority)) $output .= '<priority>' . $priority . '</priority>';
$output .= '</url>';
$this->compositing .= $output;
}
/**
* Restituisce l'intero albero xml
* @return string
*/
public function render() {
if (is_file( public_path( $this->sitemap_file_name_to_verify ) )) {
response()->file( public_path( $this->sitemap_file_name_to_verify ) );
} else {
$sitemap = $this->header;
$sitemap .= $this->open_urlset;
$sitemap .= $this->compositing;
$sitemap .= $this->end_urlset;
return $sitemap;
}
Questo è quello che fa: il __construct() si preoccupa di verificare se il file di configurazione è stato pubblicato nella cartella config/. Se non lo trova, carica il valore di default dal file di configurazione all'interno della libreria.
Il metodo addUrl() accetta 4 parametri:
Il file di configurazione rispetta la sintassi presente negli altri file di configurazione di Laravel, ritorna, cioè, un array associativo:
return[
'sitemap_file_name_to_verify' => 'sitemap.xml',
];
Di default il nome del file da verificare è sitemap.xml, ma può essere cambiato una volta pubblicato.
In realtà, non ci sono molti motivi per modificare il nome del file, ma ho incluso questa parte per mostrarti come creare un file di configurazione e come usarlo.
Per poter includere facilmente Sitemapper come dipendenza in eventuali prossimi progetti, lo devi dapprima caricare su Github e poi registrarlo su Packagist.
Crea un nuovo repository su Github e, dato che in locale avevi inizializzato il repository nella cartella sitemapper, aggiungi come remote origin il repository remoto tremite git remote add origin https://github.com/user/repo.git
, sostituendo, ovviamente i tuoi parametri al posto di user e repo.git.
Infine, con git push
, carica tutto.
Una volta fatta questa operazione, crea un account su packagist e registra un nuovo package:
Tutto fatto.
L'unico inconveniente è che l'update del package è manuale: questo vuol dire che ogni volta che fai un push su github, dovresti andare anche su packagist, dentro al package e cliccare update. Scomodo, vero?
La soluzione è creare un webhook su Github:
Ora tutto è pronto perchè il tuo package possa essere incluso in altri progetti, inserendo nel composer.json, tra i require, "tuonome/sitemapper": "*" ( al posto dell'asterisco puoi indicare una versione specifica, creata con git tag
- approfondisci git tag nella documentazione ufficiale di GIT ).
Nel caso la dipendenza debba rimanere privata o tu non abbia necessità o volontà di condividerla, è possibile riutilizzare il package su tutti i sistemi che vuoi semplicemente creando una cartella package (o altro nome di tua preferenza) e inserendo i parametri autoload nel composer.json della root. A Laravel non interessa molto dove metti il package, ma gli interessa sapere dove lo deve trovare se lo usi.
Nel caso di Sitemapper, siamo di fronte a qualcosa di veramente banale, ma è perfetto per capire le basi dello sviluppo di un package: basta comprendere quali sono i passaggi da fare e le regole da rispettare.
Se poi vuoi contribuire a migliorare Sitemapper, sei il benvenuto: puoi trovare il mio Sitemapper a questa pagina di GitHub.
documentazione ufficiale Laravel
tutorial in inglese per lo sviluppo di un package