Home Blog Pagina 4

I Package della Settimana – N.16 – 21 Novembre 2016

0

La rubrica I Package della Settimana si propone, ogni Lunedì mattina, di suggerire cinque package per Laravel descrivendoli brevemente. L’obiettivo? Riuscire a dare nuovi spunti ai lettori, far conoscere nuovi tool ed ottimizzare il flusso di lavoro. Durante la pausa caffè.

Vediamo cosa c’è questa settimana!

I Package della Settimana


  • Laravel Facade for Goutte: un semplice package che permette di usare, attraverso un service provider ad-hoc, il package Goutte con Laravel. Viene messa infatti a disposizione una comoda Facade Goutte con cui fare richieste HTTP;
  • Laravel Newsletter: un nome che lascia poco spazio al mistero. Laravel Newsletter, di Spatie, mette a disposizione una serie di tool per facilitare l’implementazione di un servizio newsletter con Laravel;
  • HTML Purifier: se un codice HTML inizia a diventare troppo “sporco”, HTML Purifier entra in azione. Compatibile con Laravel 5, mette a disposizione degli helper comodi ed un sistema di configurazione semplice da usare per andare a sistemare e rendere più leggibile il codice HTML che generiamo;
  • Testbench: il package Testbench, di Orchestral, mette a disposizione una serie di strumenti molto utili per migliorare la scrittura di test quando si ha a che fare con un’app Laravel. Basta usare la classe TestCase del package al posto di PHPUnit_Framework_TestCase, ed il gioco è fatto;
  • Stapler: Stapler di CodeSleeve è un ottimo package dedicato alla gestione degli upload di file in relazione a ciò che andiamo a salvare sul nostro database. Particolarmente utile, quindi, se abbiamo a che fare con degli upload di un utente, ad esempio. Per saperne di più la cosa migliore e guardarsi la documentazione del package;

Devo dire la verità: Testbench mi ha incuriosito e non poco, credo proprio lo proverò in modo più approfondito appena possibile. Anche Laravel Newsletter mi è sembrato molto utile, e il nome “spatie” è una garanzia.

E tu, hai qualche package da suggerire? Lasciaci le tue impressioni qui sotto, in un commento, o faccelo sapere sullo Slack di Laravel-Italia!

Ci vediamo la prossima settimana.

Laravel Cafè #4 – Whoops, Looks Like Something Went Wrong

0


Benvenuto al Laravel Cafè! Ogni settimana proporremo un nuovo argomento sul mondo Laravel, quindi trova un posto libero, prendi un caffè e condividi le tue opinioni con la comunità! L’idea è di creare un punto di discussione. Se hai qualche perplessità sull’argomento trattato, leggi fino alla fine e fai una domanda usando il forum! Cercheremo di risponderci a vicenda e di aiutarci, ed il confronto ci farà crescere tutti un po’ di più.

Di mercoledì in mercoledì parleremo di qualcosa di diverso, quindi tornaci a trovare! Potresti dare una mano a qualcuno in difficoltà, o ricevere tu un aiuto in caso di problemi! Dai, siediti, il primo caffè lo offriamo noi.

L’Argomento

Quante volte abbiamo letto sul nostro monitor “Whoops, looks like something went wrong”, magari lanciando qualche imprecazione, o magari sapendo già il motivo di quell’errore e correggendo al volo il codice? Le eccezioni fanno parte della struttura di un software da sempre e, come tutte le altre “parti” del nostro software, vanno gestite a dovere. Come avrete intuito oggi dedicheremo la nostra pausa caffè alle Exception di Laravel, a come gestirle e come customizzarle.

Il Codice

A differenza degli ultimi articoli, durante questo caffè ci soffermeremo più sulla teoria che sulla pratica, dato che la personalizzazione delle exception può variare a seconda delle esigenze, o può semplicemente rimanere invariata. Come prima cosa vediamo nel dettaglio il workflow della gestione delle exception di Laravel. La documentazione ufficiale come al solito risulta un po’ scarna sotto questo aspetto dato che spiega nel dettaglio gli strumenti, ma il workflow dell’Exception Handler rimane tutto racchiuso nel suo codice sorgente (anche questo molto intuitivo, ma molto meno commentato rispetto ad altre classi). Mi focalizzerò esclusivamente sul render di una exception, ma in realtà il framework gestisce anche il richiamo del logger della stessa.

Come ho anticipato, i sorgenti sono abbastanza intuitivi quindi vi consiglio di sbirciare i link ai sorgenti di Github che ho incluso nei vari punti.

Viene lanciata una exception e attraverso la classe IlluminateFoundationBootstrapHandleExceptions viene richiamato il metodo handleException($e). Come si può vedere il framework gestisce in maniera differente se è stato lanciato un comando da console o si è verificato un errore in una qualche request, in questo articolo ci focalizzeremo comunque solo sul secondo caso:

/**
* Handle an uncaught exception from the application.
*
* Note: Most exceptions can be handled via the try / catch block in
* the HTTP and Console kernels. But, fatal error exceptions must
* be handled differently since they are not normal exceptions.
*
* @param  Throwable  $e
* @return void
*/
public function handleException($e)
{
if (! $e instanceof Exception) {
$e = new FatalThrowableError($e);
}

// Il metodo report controlla se l'exception va loggata o meno
$this->getExceptionHandler()->report($e);

if ($this->app->runningInConsole()) {
// Render dell'exception da linea di comando
$this->renderForConsole($e);
} else {
// Render dell'exception come view
$this->renderHttpResponse($e);
}
}

// Secondo step
/**
* Render an exception as an HTTP response and send it.
*
* @param  Exception  $e
* @return void
*/
protected function renderHttpResponse(Exception $e)
{
$this->getExceptionHandler()->render($this->app['request'], $e)->send();
}

Il metodo renderHttpResponse richiama la classe AppExceptionsHandler che eredita il metodo render dalla classe IlluminateFoundationExceptionsHandler e, nel caso in cui l’utente non modifichi nulla, lo richiama. Ricordiamoci che da questo momento in poi la gestione dell’exception viene standardizzata dal framework, e questo comporta una certa “rigidità” del flusso dal momento in cui il metodo `parent::render($request, $exception)’ viene richiamato, al momento in cui riceviamo il response finale. Al tempo stesso, viene lasciata al developer la più totale libertà di modificarlo, dato che al momento l’unica cosa che ha fatto il framework è intercettare una exception e richiamare la classe addetta alla sua gestione.

Vediamo quindi come si comporta il framework da questo momento in poi


/** * Render an exception into an HTTP response. * * @param IlluminateHttpRequest $request * @param Exception $exception * @return IlluminateHttpResponse */ public function render($request, Exception $exception) { /* * Qui si possono aggiungere le istruzioni custom per la gestione delle exception */ // Caso base return parent::render($request, $exception); }

Richiamato il metodo parent::render($request, $exception), il framework inizia a preparare un normale response (come avviene per qualsiasi altro controller dell’applicazione) che contiene sia i dati della request che l’exception lanciata. Prima di arrivare al response finale però effettua alcuni controlli sul tipo di exception lanciata, convertendola dove necessario per facilitarne la gestione.

Per farvi un esempio, il metodo $this->validate($request, $rules) nel caso in cui riscontra la violazione di una regola, lancia una ValidationException che segue lo stesso iter delle normali exception, ma il response finale è sarà un redirect:

// Validation exception
redirect()->back()->withInput($request->input())->withErrors($errors)

Nel caso in cui l’exception non rientri in uno dei casi particolari, allora verrà si procederà con la creazione del response:

/**
* Render an exception into a response.
*
* @param  IlluminateHttpRequest  $request
* @param  Exception  $e
* @return SymfonyComponentHttpFoundationResponse
*/
public function render($request, Exception $e)
{
$e = $this->prepareException($e);

if ($e instanceof HttpResponseException) {
return $e->getResponse();
} elseif ($e instanceof AuthenticationException) {
return $this->unauthenticated($request, $e);
} elseif ($e instanceof ValidationException) {
return $this->convertValidationExceptionToResponse($e, $request);
}

// Caso base
return $this->prepareResponse($request, $e);
}

/**
* Prepare response containing exception render.
*
* @param  IlluminateHttpRequest  $request
* @param  Exception $e
* @return SymfonyComponentHttpFoundationResponse
*/
protected function prepareResponse($request, Exception $e)
{
if ($this->isHttpException($e)) {
return $this->toIlluminateResponse($this->renderHttpException($e), $e);
} else {
return $this->toIlluminateResponse($this->convertExceptionToResponse($e), $e);
}
}

Si passa quindi al render vero e proprio, che rispecchia la “rigidità” precedentemente citata e che, a mio avviso, può essere il motivo principale di modifica e personalizzazione della classe AppExceptionHandler. Nel caso in cui l’exception sia un’istanza della classe SymfonyComponentHttpKernelExceptionHttpException o una sua estensione, verrà controllato lo statusCode (4xx, 5xx) e ricercata la corrispondente view nella cartella resources/views/errors/, altrimenti l’ExceptionHandler ritornerà l’html con lo stacktrace dell’eccezione. Mi soffermo solo sui metodo getHtml($exception) e decorate($content, $css), richiamati nel caso in cui l’exception non sia un’istanza/estensione della classe SymfonyComponentHttpKernelExceptionHTTPException, poiché è il responsabile della generazione della pagina che tutti noi conosciamo bene, e che potrebbe essere spunto per una propria pagina custom.

/**
* Gets the full HTML content associated with the given exception.
*
* @param Exception|FlattenException $exception An Exception or FlattenException instance
*
* @return string The HTML content as a string
*/
public function getHtml($exception)
{
if (!$exception instanceof FlattenException) {
$exception = FlattenException::create($exception);
}

return $this->decorate($this->getContent($exception), $this->getStylesheet($exception));
}

private function decorate($content, $css)
{
return <<<eof <!doctype="" html="">


<meta charset=""{$this-">charset}" />
<meta name=""robots"" content=""noindex,nofollow""></eof>

<style>
            /* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html */<br />
            html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:text-top;}sub{vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}</p>
<p>            html { background: #eee; padding: 10px }<br />
            img { border: 0; }<br />
            #sf-resetcontent { width:970px; margin:0 auto; }<br />
            $css<br />
        </style>

$content


EOF;
}

Per quanto questo workflow sia completo in ogni sua forma, possono nascere esigenze diverse per eccezioni particolari per le quali preferiamo che l’applicazione abbia un comportamento diverso rispetto allo standard. Vorrei ricordare inoltre che i metodi del “dietro le quinte”, ovvero della classe IlluminateFoundationExceptionsHandler, possono essere a loro volta utilizzati, evitando quindi codice ridondante. Vi faccio un piccolo esempio:

/**
* Render an exception into an HTTP response.
*
* @param  IlluminateHttpRequest  $request
* @param  Exception  $exception
* @return IlluminateHttpResponse
*/
public function render($request, Exception $exception)
{
if ($exception instanceof AppExceptionsMyCustomException && $request->ajax()) {
// Ritorno l'eccezione in formato JSON e log
Log::error($exception->getMessage());
return response()->json(['whoops' => $exception->getMessage()], $exception->getStatusCode());
} else if($exception instanceof AppExceptionsMyCustomException && env('APP_ENV', 'local') == 'production'){
// Ritorno una view apposita
return response()->view('errors.custom_error.blade.php', ['whoops' => $exception->getMessage()]);
} else if($exception instanceof AppExceptionsMyCustomException) {
// Ritorno la view di debug
return $this->convertExceptionToResponse($exception);
}

return parent::render($request, $exception);
}

Da qui in poi dipende tutto dalle necessità che ciascun developer ha per la propria applicazione. Il consiglio che do è comunque di sfruttare il più possibile l’handler soprattutto per evitare spiacevoli schermate bianche in produzione o per crearsi un piccolo “monitor” che notifichi in tempo reale uno sviluppatore di quanto è accaduto (quindi dati della request, eccezione lanciata ecc…) senza dover scartabellare nella cartella dei log in cerca di risposta.

… ed ora?

Come gestite gli errori e i log nelle vostre applicazioni?

Se volete avere maggiori informazioni riguardo errori ed gestione degli stessi, piuttosto che gestire viste customizzate o response in JSON per, ad esempio, le rotte API, vi invito a continuare la discussione in tutti i canali di Laravel Italia così da riuscire rendere più completo ed esaustivo l’articolo di oggi.

Ah, e ci trovate anche sul nostro Slack!

Laravel Cafè #3 – Creazione ed Invio di Mail

0


Benvenuto al Laravel Cafè! Ogni settimana proporremo un nuovo argomento sul mondo Laravel, quindi trova un posto libero, prendi un caffè e condividi le tue opinioni con la comunità! L’idea è di creare un punto di discussione. Se hai qualche perplessità sull’argomento trattato, leggi fino alla fine e fai una domanda usando il forum! Cercheremo di risponderci a vicenda e di aiutarci, ed il confronto ci farà crescere tutti un po’ di più.

Di mercoledì in mercoledì parleremo di qualcosa di diverso, quindi tornaci a trovare! Potresti dare una mano a qualcuno in difficoltà, o ricevere tu un aiuto in caso di problemi! Dai, siediti, il primo caffè lo offriamo noi.

L’Argomento

Nel mondo del web 2.0 l’invio di mail automatizzate è parte integrante della maggioranza delle applicazioni. L’esempio più semplice è la mail di conferma che ci arriva dopo aver effettuato la registrazione ad un portale web, un forum, o a un social network, che viene inviata automaticamente dall’applicativo. Oggi, per la nostra abituale pausa caffè del mercoledì, vediamo come implementare questo servizio su Laravel 5.3.

N.B.: Laravel 5.3 ha diverse novità rispetto ai suoi predecessori e alcune di queste riguardano proprio le API per le mail, ma il codice che vi propongo è tranquillamente adattabile alle versioni antecedenti con un controller apposito.

Il Codice

Tralascerò la parte di configurazione del servizio (e vi rimando direttamente alla documentazione ufficiale) dato che può variare a seconda delle necessità del developer. Come di consueto vi propongo prima di tutto la struttura del filesystem:

app/
|-- Http/Controllers
|-- MailController.php     <-- Controller per l'invio delle mail
 |-- Mail/                      <-- Cartella base dei modelli "mailable" https://laravel.com/docs/5.3/mail#generating-mailables
     |-- Contact.php            <-- Classe che si occupa del build della mail di richiesta informazioni
resources/views/                <-- Cartella viste
     |-- emails                 <-- Cartella omissibile
         |-- contact.blade.php  <-- View che fa da template per mail richiesta informazioni

Utilizzando il comando php artisan make:mail Contact Laravel ci aiuta ad automatizzare la creazione della classe che rappresenta in modo astratto quanto l’utente fanale riceverà nella propria casella di posta. A mio avviso se automatizzasse anche la creazione della vista base non sarebbe male, ma potrebbe essere uno spunto per qualche plugin o una futura feature di Laravel.

Dopo aver lanciato il comando precedentemente descritto, il risultato sarà la creazione di un’estensione della classe IlluminateMailMailable e contenente il metodo build() che ci permette di impostare sia tutte le informazioni riguardanti mittenti e destinatari, la vista da utilizzare come template ed eventualmente implementare anche code di invio.

Contact.php

namespace AppMail;

use IlluminateBusQueueable;
use IlluminateMailMailable;
use IlluminateQueueSerializesModels;
use IlluminateContractsQueueShouldQueue;

class Contact extends Mailable
{

    use Queueable,
        SerializesModels;

    /**
     * Array with sender informations.
     * [ 'name' => 'John', 'mail' => 'john@doe.com' ]
*
* @var array
*/
private $sender;

/**
* Collection with recipients informations.
* Collection: [
*      [ 'name' => 'stewie', 'mail' => 'stewie@griffin.com' ]
*      [ 'name' => 'Peter', 'mail' => 'peter@griffin.com' ]
*      [ 'name' => 'Lois', 'mail' => 'lois@griffin.com' ]
*      [ 'name' => 'Chris', 'mail' => 'chris@griffin.com' ]
*      [ 'name' => 'Brian', 'mail' => 'brian@griffin.com' ]
*    // No one care about Meg
* ]
* @var Collection
*/
private $recipients;

/**
* Mail view template.
*
* @var String
*/
private $template;

/**
* Collection with other attributes for the view.
*
* @var mixex
*/
public $attributes;

/**
* Create a new message instance.
*
* @return void
*/
public function __construct($sender, $recipients, $template, $attributes)
{
$this->sender = $sender;
$this->email = $recipients;
$this->template = $template;
$this->attributes = $attributes;
}

/**
* Build the message.
*
* @return $this
*/
public function build()
{
// Senders addresses
$this->from($this->sender['name'], $this->sender['mail']);

// Recipients addresses
foreach($recipients as $recipient) {
$this->to($recipient['name'], $recipient['mail']);
}

// Subject
$this->subject("Richiesta informazioni");

// Return view
return $this->view($this->template);
}

}

Come potete vedere la classe contiene tre variabili private ($sender, $recipients, $template) e una variabile pubblica $attributes. Le classi Mailable hanno questa caratteristica (vedi la documentazione.

any public property defined on your mailable class will automatically be made available to the view.

Tradotto:

qualsiasi proprietà pubblica definita nella tua classe verrà resa automaticamente disponibile alla view.

Quindi, nella view descritta di seguito, avrò a disposizione un array $attributes che conterrà tutte le variabili dinamiche da passare ad un determinato template, a differenza delle variabili $sender, $recipients, $template, utilizzate solo per impostare la testata della mail. Ovviamente dato che de gustibus non est disputandum, se avete suggerimenti o volete aggiungere la vostra implementazione della classe, vi invito a continuare la discussione!

contact.blade.php




<meta charset=""UTF-8""> <h4>Nuova richiesta informazioni da parte di {{ $attributes['name'] }}</h4> {{ $attributes['name'] }} ti ha invito il seguente messaggio: {{ {{ $attributes['message'] }} }} <h4>Ulteriori informazioni:</h4> <ul> @foreach($attributes['infos'] as $key => $info) <li><strong>{{ $key }}:</strong> {{ $info }}</li> @endforeach</ul>

Il Test

La pausa caffè sta per terminare, quindi verifichiamo che tutto funzioni creando un piccolo controller, richiamato, ad esempio, da un form di richiesta informazioni.

namespace AppHttpControllers;

use IlluminateHttpRequest;
use AppHttpRequests;
use Mail;
use Collection;
use AppMailContact;

class MailController extends Controller
{

public function contact(Request $request)
{
// validation and other stuff...
[...]

// sender
$sender = [ 'name' => $request->name, 'mail' => $request->mail ];

// recipients
$recipients = new Collection;
$recipients->push(['name' => 'Laravel Italia', 'mail' => 'info@laravel-italia.it']);

// attributes
$attributes = [
'name' => $request->name,
'message' => $request->message,
'infos' => [
'telefono' => $request->phone,
'indirizzo' => $request->address,
// [...]
]
];

Mail::send(new Contact($sender, $recipients, 'mail.contact', $attributes));

// return result
[...]
}

}

Questo controller si occuperà di inviare la mail, richiamando il metodo build() della classe Contact, il risultato? Beh guardate la casella di posta per scoprirlo!

… ed ora?

Cosa ne pensate di queste feature? Implementate anche voi le classi Mailable o lavorate in maniera differente?

In questo articolo ho tralasciato la parte di configurazione dei vari driver mail, l’aggiunta di allegati e l’invio tramite una coda. Se volete avere maggiori informazioni riguardo questi argomenti, vi invito a continuare la discussione in tutti i canali di Laravel Italia così da riuscire rendere più completo l’esempio proposto!

Fateci sapere cosa ne pensate, e non dimenticate che c’è sempre il nostro bellissimo Slack, pieno di gente che discute del framework più bello del mondo!

I Package della Settimana – N.15 – 14 Novembre 2016

0

La rubrica I Package della Settimana si propone, ogni Lunedì mattina, di suggerire cinque package per Laravel descrivendoli brevemente. L’obiettivo? Riuscire a dare nuovi spunti ai lettori, far conoscere nuovi tool ed ottimizzare il flusso di lavoro. Durante la pausa caffè.

Vediamo cosa c’è questa settimana!

I Package della Settimana


  • Laravel Slack API: questo interessante package, che tra l’altro è stato usato anche per l’auto inviter tool del nostro Slack, è un comodo ponte tra le API di Slack e Laravel. Consente di lavorare un po’ a tutti i livelli, dal team fino al singolo, channel, per arrivare a fare anche cose più complesse;
  • BootForms: questo package di Adam Wathan consente di generare, attraverso comode chiamate, interi form bootstrap da poter usare nella nostra applicazione. Fa perno su un altro package di Wathan, Forms, e ne integra alcune funzionalità portandolo ad un livello superiore. Da provare sicuramente se non ci piace scrivere il codice HTML dei form a mano;
  • Baum: un’implementazione del Nested Set da usare in Eloquent. Cosa vuol dire? Beh, il file readme.md del progetto spiega bene un po’ tutti i concetti teorici fondamentali. In poche parole, comunque, serve a gestire in modo efficiente gerarchie in cui un model può essere “padre” di un altro model dello stesso tipo;
  • Laravel Server Monitor: questo package, davvero molto interessante, si occupa di monitorare la nostra applicazione Laravel attraverso una serie di piccoli controlli, in modo tale da farci stare un po’ più sereni. Uso del disco, semplice ping, controllo sui certificati SSL e così via;
  • Laravel Analytics: questo package di Spatie si integra con Google Analytics, consentendo di interrogarlo per avere una panoramica completa di quello che succede nella propria applicazione. La sintassi sembra davvero espressiva (con metodi come fetchMostVisitedPages). Da tenere sott’occhio!

E tu, hai qualche package da suggerire? Lasciaci le tue impressioni qui sotto, in un commento, o faccelo sapere sullo Slack di Laravel-Italia!

Ci vediamo la prossima settimana.

Laravel Cafè #2 – Dietro le Facade

0


Benvenuto al Laravel Cafè! Ogni settimana proporremo un nuovo argomento sul mondo Laravel, quindi trova un posto libero, prendi un caffè e condividi le tue opinioni con la comunità! L’idea è di creare un punto di discussione. Se hai qualche perplessità sull’argomento trattato, leggi fino alla fine e fai una domanda usando il forum! Cercheremo di risponderci a vicenda e di aiutarci, ed il confronto ci farà crescere tutti un po’ di più.

Di mercoledì in mercoledì parleremo di qualcosa di diverso, quindi tornaci a trovare! Potresti dare una mano a qualcuno in difficoltà, o ricevere tu un aiuto in caso di problemi! Dai, siediti, il primo caffè lo offriamo noi.

L’Argomento

Laravel e le Facade… La prima cosa che ho pensato è “ok, come si legge?” Ecco, adesso lo sai!

Possiamo quindi passare alla parte di teoria: direi di partire da quanto riportato nella documentazione ufficiale.

Facades provide a “static” interface to classes that are available in the application’s service container. Laravel ships with many facades which provide access to almost all of Laravel’s features. Laravel facades serve as “static proxies” to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.

Traduco per tutti:

Le Facade forniscono un’interfaccia “statica” alle classi che sono disponibili nel service container dell’applicazione. Laravel è fornito di molte facade che forniscono accesso praticamente a tutte le funzionalità dell’applicazione. Le facade di Laravel agiscono da “proxy statici” per sottolineare classi nel service container, fornendo i benefit di una sintassi concisa ed espressiva mantenendo nel mentre maggiore testabilità e flessibilità dei tradizionali metodi statici.

Per rendere il tutto più semplice, partirei da questo schema, che spiega molto bene l’idea generale:
Facade.

Fondamentalmente il container dell’applicazione attraverso le Facade ci peremente di utilizzare metodi di classi più complesse richiamando però un singolo metodo statico. Laravel utilizza massivamente le facade (richiamate in alcuni casi anche dagli helper del framework). Un esempio classico può essere il seguente:

Route::get([...]);

Instanziamo tramite il metodo statico get() una nuova rotta, in maniera semplice e rapida, ma quanto andiamo a richiamare è ben più complesso! Per chi volesse approfondire l’argomento consiglio questo articolo che spiega molto bene il “dietro le quinte”. Ehi, è in inglese!

Il Codice

Svilupperemo l’esempio con la versione 5.3 di Laravel, ma qualunque versione a partire dalla 5.0 non presenta incompatibilità con la procedura applicata. Partiamo dal filesystem che contiene la struttura base utilizzata per questo episodio di Laravel cafè:

app/
|-- Facades/                       <-- Cartella base delle facade
     |-- QuoteFacade.php            <-- La nostra facade d'esempio
 |-- Utilities/                     <-- Cartella base delle classi implementate dalle facade
     |-- Quote.php                  <-- La vera implementazione della facade
 |-- Providers/                     <-- Cartella base dei providers dell'applicazione
     |-- QuoteServiceProvider.php   <-- Il service provider della classe Quote

Come potete intuire, il tutto si traduce in una classe che generi delle quotazioni randomiche del famoso meme “Be Like Bill” da inglobare all’interno di una view. In verità questo esempio è abbastanza fine a sé stesso… però dai, a chi non piace “Be Like Bill”?

P.S.: Just for fun, Potrete trovare i sorgenti su github e provare qui il billgen.

Partiamo esaminando la classe Quote.php

<!--?php

namespace AppUtilities;

class Quote
{

    /**
     * Get the bill quote image tag.
     *
     * @param string $name
     * @param string $sex
     * @param array $img_attributes
     * @return string
     */
    public function getBillQuote($name = null, $sex = null, $img_attributes = [])
    {
        $url = 'http://belikebill.azurewebsites.net/billgen-API.php?default=1';
        $url .= $name !== null ? '&name=' . $name : '';
        $url .= $sex !== null ? '&sex=' . $sex : '';

        $image = '<img src="' . $url . '"';
        foreach ($img_attributes as $key =--> $val) {
$image .= ' ' . $key . '="' . $val . '"';
}
$image .= ' />';

return $url;
}
}

Creiamo quindi l’apposito service provider che ci permette di registrare la classe Quote e poterla quindi aggiungere all’interno del contenitore dell’applicazione. Ci basta lanciare il comando

php artisan make:provider QuoteServiceProvider

Una volta creata la classe, ci basta modificare il metodo register per potere effettuare il bind della classe Quote.

namespace AppProviders;

use IlluminateSupportServiceProvider;

class QuoteServiceProvider extends ServiceProvider
{

/**
* Register the application services.
*
* @return void
*/
public function register()
{
app()->bind('quote', function() {
return new AppUtilitiesQuote;
});
}
}

Per ora abbiamo solamente “inserito un oggetto in uno scatolone”, dobbiamo far si che questo “oggetto” venga aggiunto al container dell’applicazione per poter essere riutilizzato quando necessario. Dobbiamo quindi modificare il file config/app.php, aggiungendolo all’array providers la classe appena creata.

'providers' => [
[...]

/*
* Quote Service Providers...
*/
AppProvidersQuoteServiceProvider::class,
],

Ora l’applicazione ci permette di richiamare la classe Quote: tuttavia, manca ancora il bind alla sua facade. Come abbiamo visto prima nello schema del filesystem, dobbiamo creare la classe AppFacadesQuoteFacade.php che conterrà il seguente codice:

namespace AppFacades;

use IlluminateSupportFacadesFacade;

/**
* Quote facade
*
* @author ilgala
*/
class QuoteFacade extends Facade
{

protected static function getFacadeAccessor()
{
return 'quote'; // IoC bind
}

}

Ce l’abbiamo fatta! Neanche il tempo di finire il caffè che abbiamo già a disposizione tutto quello che ci serve per poter richiamare staticamente la nostra classe Quote. Possiamo dunque richiamare la nostra facade importandola nelle classi di nostro interesse utilizzando un semplice

use AppFacadesQuoteFacade as Quote

oppure aggiungendo la nostra facade all’array degli alias del file config/app.php

'aliases' => [
[...]
'Quote' => AppFacadesQuoteFacade::class,
],

e richiamando (dove necessario) un semplice use Quote;. Questa seconda soluzione la preferisco, anche perché (a parte in questo esempio) il più delle volte genero delle facade per poter richiamare metodi ridondanti all’interno di controller o di viste, quindi avere un alias per non dover richiamare la Facade risulta essere particolarmente utile.

Infine, per far si che le nostre classi vengano caricate automaticamente dall’applicazione, rendendo operativa al 100% la facade appena creata, vanno lanciati i comandi

composer dump-autoload

// oppure

php artisan clear-compiled
php artisan optimize

Il Test

Per provare l’effettivo funzionamento della facade, ci basta creare una rotta apposita

Route::get('/quote', function() {
return Quote::getBillQuote();
});

Se tutto è andato a buon fine, il risultato finale che vedremo all’interno della nostra view è una vignetta random del meme “Be Like Bill”.

… ed ora?

Vi siete mai creati delle Facade custom? Per cosa le utilizzate?

Molti dicono che le facade siano un pattern pericoloso e, a lungo termine, persino dannoso per l’applicazione, siete della stessa idea? Spero che la discussione continui in tutti i canali di Laravel Italia!

Fateci sapere cosa ne pensate, e non dimenticate che c’è sempre il nostro bellissimo Slack, pieno di gente che discute del framework più bello del mondo!

I Package della Settimana – N.14 – 7 Novembre 2016

0

La rubrica I Package della Settimana si propone, ogni Lunedì mattina, di suggerire cinque package per Laravel descrivendoli brevemente. L’obiettivo? Riuscire a dare nuovi spunti ai lettori, far conoscere nuovi tool ed ottimizzare il flusso di lavoro. Durante la pausa caffè.

Vediamo cosa c’è questa settimana!

I Package della Settimana


  • Quicksand: questo package permette di programmare, dopo un certo periodo di tempo, una rimozione effettiva dei model che abbiamo precedentemente cancellato tramite soft-delete. Molto interessante, ad esempio, per poter implementare la riattivazione di un utente disattivato entro un certo limite temporale;
  • Lambo: questo tool, utile soprattutto per gli utilizzatori di Valet, aggiunge alla creazione di un nuovo progetto una serie di piccole migliorie, tra cui una prima commit con un messaggio standard, predisposizione di variabili d’ambiente e così via;
  • Laravel Elixir Elm Integration: mai sentito parlare di Elm? Se dovesse interessarti, questo package permette di agevolare l’integrazione di Laravel Elixir con questo linguaggio. Può essere una buona occasione per far felici i frontend!
  • Mailthief: questo package consiste in un fake mailer appositamente concepito per testare tutto quello che riguarda l’invio di email dalla propria applicazione. Una volta impostato, tramite un trait potremo testare in maniera approfondita ogni singola variabile legata all’invio di un messaggio;
  • Laravel Notification Hipchat Channel: il sistema di notifiche di Laravel 5.3 mi piace davvero tanto. E piace anche ai ragazzi del progetto laravel-notification-channels, che si stanno dando davvero tanto da fare. Questo è il channel del sistema di messaggistica Hipchat;

E tu, hai qualche package da suggerire? Lasciaci le tue impressioni qui sotto, in un commento, o faccelo sapere sullo Slack di Laravel-Italia!

Ci vediamo la prossima settimana.

Laravel Cafè #1 – Eloquent: Relazioni ed Eventi

0


Benvenuto al Laravel Cafè! Ogni settimana proporremo un nuovo argomento sul mondo Laravel, quindi trova un posto libero, prendi un caffè e condividi le tue opinioni con la comunità! L’idea è di creare un punto di discussione. Se hai qualche perplessità sull’argomento trattato, leggi fino alla fine e fai una domanda usando il forum! Cercheremo di risponderci a vicenda e di aiutarci, ed il confronto ci farà crescere tutti un po’ di più.

Di mercoledì in mercoledì parleremo di qualcosa di diverso, quindi tornaci a trovare! Potresti dare una mano a qualcuno in difficoltà, o ricevere tu un aiuto in caso di problemi! Dai, siediti, il primo caffè lo offriamo noi.

L’Argomento

Iniziamo questa nuova rubrica con uno dei fondamenti dell’architettura MVC: i modelli. Laravel ha sempre mantenuto una documentazione molto completa a riguardo, ma un pochino frammentata in esempi non sempre collegati fra loro. Per un developer alle prime armi, un modello in Laravel si traduce in poco più di una semplice “raffigurazione” di una tabella di un database e delle sue relazioni, con al massimo un paio di scopes per automatizzare una particolare query, ad esempio per ritrovare tutti gli utenti appartenenti ad un certo gruppo, piuttosto che le ultime news pubblicate. In verità un modello può diventare parte attiva anche della business logic, evitando di dover replicare operazioni CRUD all’interno di uno o più controller semplicemente gestendo il fire degli eventi che Eloquent lancia.

Oggi vi propongo quindi la mia visione su come strutturare un modello in Laravel 5.3, i suoi eventi e le sue relazioni e come modificarle durante un cambio di stato del modello.

Il Codice

Iniziamo con la struttura del filesystem. Personalmente preferisco creare varie sotto cartelle (e di conseguenza namespaces che andranno inclusi nei vari controller), anziché salvare tutto nella cartella app. Questa operazione rende la cartella meno affollata e dispersiva, soprattutto su applicazioni di una certa complessità. Di seguito trovi lo schema utilizzato per l’esempio di oggi.

app/
|-- Models/                    <-- Cartella base dei modelli
     |-- Core/                  <-- Cartella gruppo modelli Core
         |-- User.php           <-- Classe base creata automaticamente da Laravel
         |-- Registry.php       <-- Classe rappresentante il modello Anagrafica (relazione 1:1 con User)
         |-- Note.php           <-- Classe rappresentante il modello Nota
     |-- Observers/             <-- Cartella degli Observers
         |-- Core/              <-- Cartella omissibile, ma mi piace mantenere la struttura
             |-- RegistryObserver.php   <-- Observer del modello Registry
             |-- NoteObserver.php       <-- Observer del modello Note
     |-- Traits/
         BaseModels.php          <-- Trait utilizzato per le relazioni di creazione, modifica ed eliminazione

Utilizzando il comando php artisan make:model Models/Gruppo/Modello Laravel ci fa il favore di inserire il modello nella cartella corretta e con il relativo namespace.

Per rendere il codice di un modello più pulito e ordinato ho preso l’abitudine di creare dei Trait specifici (prima tendenzialmente estendevo classi o implementavo interfacce). In alcuni casi ho utilizzato i Trait per creare dei semplici metodi pubblici di supporto (ex. un prodotto ha dimensione, peso e quantità ordinate, con un metodo pubblico si può recuperare questi dati e formattarli in una stringa di riepilogo localizzata)

File BaseModels.php

namespace AppModelsTraits;

trait BaseModels
{

    /**
     * Get the create user record associated with the model.
     */
    public function createUser()
    {
        return $this->belongsTo('AppModelsCoreUser', 'id_user_create');
}

/**
* Get the update user record associated with the model.
*/
public function updateUser()
{
return $this->belongsTo('AppModelsCoreUser', 'id_user_update');
}

/**
* Get the delete user record associated with the model.
*/
public function deleteUser()
{
return $this->belongsTo('AppModelsCoreUser', 'id_user_delete');
}

/**
* Local scope, retrieve by create user id
*
* @param type $query
* @param type $id_user
*/
public function scopeCreatedBy($query, $id_user)
{
$query->where('id_user_create', '=', $id_user);
}

}

In una visione più avanzata dei modelli, la gestione dei timestamps di un modello è complementare alla gestione degli utenti di creazione, modifica ed eliminazione. Per questo motivo ho deciso di includere questo Trait nel codice d’esempio dato che si sposa perfettamente con quanto andrò ad implementare negli eventi del modello.

Model

La classe Note è molto semplice, non ha relazioni, se non quelle dettate dal trait e gli attributi indispensabili…

File Note.php

namespace AppModelsCore;

use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentSoftDeletes;
use AppModelsTraitsBaseModels;

class Note extends Model
{

use BaseModels,
SoftDeletes;

/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = [
'created_at', 'updated_at', 'deleted_at'
];

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillabe = [
'title', 'body'
];

/**
* The attributes that aren't mass assignable.
*
* @var array
*/
protected $guarded = [
'id_user_create', 'id_user_update', 'id_user_delete'
];

/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'id_user_create', 'id_user_update', 'id_user_delete'
];

}

Non c’è molto da dire, tranne che ci servirà per l’esempio finale… Focalizziamoci invece sulle due possibili gestioni degli eventi di un modello. Il primo è l’utilizzo degli observers, che ho implementato per la classe Registry. Il secondo è il metodo “classico”, implementato all’interno del metodo boot() della classe User.

N.B.: Nella documentazione di Laravel 5.1 e 5.2 non vengono citati da nessuna parte gli Observers, ma ci sono e funzionano sempre allo stesso modo della 5.0.

File Registry.php

namespace AppModelsCore;

use IlluminateDatabaseEloquentModel;
use IlluminateDatabaseEloquentSoftDeletes;
use AppModelsTraitsBaseModels;

class Registry extends Model
{

use BaseModels,
SoftDeletes;

/**
* The attributes that should be mutated to dates.
*
* @var array
*/
protected $dates = [
'created_at', 'updated_at', 'deleted_at'
];

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillabe = [
'id_user', 'name', 'surname'
];

/**
* The attributes that aren't mass assignable.
*
* @var array
*/
protected $guarded = [
'id_user_create', 'id_user_update', 'id_user_delete'
];

/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'id_user_create', 'id_user_update', 'id_user_delete'
];

/**
* Get the user record associated with the registry.
*/
public function user()
{
return $this->belongsTo('AppModelsCoreUser', 'id_user');
}

}

Nella descrizione della classe qui di seguito trovate il workflow completo degli eventi di un modello, ma per questo esempio sono stati riportati solamente i metodi utili.

File ObserversCoreRegistryObserver.php

namespace AppModelsObserversCore;

use AppModelsCoreRegistry;
use Auth;

/**
* https://laravel.com/docs/5.3/eloquent#events
*
* Eloquent models fire several events, allowing you to hook into various points
* in the model's lifecycle using the following methods: creating, created,
* updating, updated, saving, saved, deleting, deleted, restoring, restored.
* Events allow you to easily execute code each time a specific model class is
* saved or updated in the database.
*
* Whenever a new model is saved for the first time, the creating and created
* events will fire. If a model already existed in the database and the save
* method is called, the updating / updated events will fire. However, in both
* cases, the saving / saved events will fire.
*
* EVENTS WORKFLOW:
*
* - CREATE
* - saving
* - creating
* - created
* - saved
*
* - SAVE
* - saving
* - updating
* - updated
* - saved
*
* - DELETE
* - deleting
* - deleted
*
* - RESTORE
* - restoring
* - saving
* - saved
* - restored
*
* - FORCE DELETE
* - deleting
* - deleted
*
*/
class RegistryObserver
{

/**
* Listen to the Registry creating event.
*
* @param  Registry  $registry
* @return void
*/
public function creating(Registry $registry)
{
// Check if user in session
if (Auth::check()) {
// Add create user
$registry->createUser()->associate(Auth::user());
}
}

/**
* Listen to the Registry updating event.
*
* @param  Registry  $registry
* @return void
*/
public function updating(Registry $registry)
{
// Check if user in session
if (Auth::check()) {
// Add update user
$registry->updateUser()->associate(Auth::user());
}
}

/**
* Listen to the Registry deleting event.
*
* @param  Registry  $registry
* @return void
*/
public function deleting(Registry $registry)
{
// Check if model is soft deleting or force deleting
if (!$registry->isForceDeleting()) {
// Check if user in session
if (Auth::check()) {
// Add delete user
$registry->deleteUser()->associate(Auth::user());
}
}
}

/**
* Listen to the Registry created event.
*
* @param  Registry  $registry
* @return void
*/
public function restored(Registry $registry)
{
// Remove delete user
$registry->deleteUser()->dissociate();

// Add update user
$registry->updateUser()->associate(Auth::user());

$registry->save();
}

}

Per rendere operativo il nostro observer va aggiunto il seguente codice all’interno del metodo boot() della classe AppProvidersAppServiceProvider.

Come si può vedere, è presente anche l’observer per il modello Note che ho omesso dato che è identico a quello del modello Registry

public function boot()
{
Registry::observe(RegistryObserver::class);
Note::observe(NoteObserver::class);
}

Dal momento in cui l’observer è attivo, quando verrà eseguito il fire di ciascun evento, automaticamente verrà gestito l’inserimento (o la rimozione nel caso del restore) degli utenti di creazione, modifica ed eliminazione. Vediamo ora la maniera più classica di gestire gli eventi, direttamente dal modello User.

File: User.php

namespace AppModelsCore;

use IlluminateNotificationsNotifiable;
use IlluminateFoundationAuthUser as Authenticatable;
use IlluminateDatabaseEloquentSoftDeletes;
use AppModelsTraitsBaseModels;
use Auth;

class User extends Authenticatable
{

use Notifiable,
BaseModels,
SoftDeletes;

/**
* The "booting" method of the model.
*
* @return void
*/
protected static function boot()
{
parent::boot();

static::creating(function(User $user) {
// Check if user in session
if (Auth::check()) {
// Add create user
$user->createUser()->associate(Auth::user());
}
});

static::updating(function(User $user) {
// Check if user in session
if (Auth::check()) {
// Add update user
$user->updateUser()->associate(Auth::user());
}
});

static::deleting(function(User $user) {
// Check if model is soft deleting or force deleting
if (!$user->isForceDeleting()) {
// Check if user in session
if (Auth::check()) {
// Add delete user
$user->deleteUser()->associate(Auth::user());
}
}
});

static::deleted(function(User $user) {
if ($user->forceDeleting) {
// Force delete relations
$user->registry()->withTrashed()->forceDelete();

$notes = Note::withTrashed()->createdBy($user->id)->get();

foreach ($notes as $note) {
$note->forceDelete();
}
} else {
// Soft delete relations
$user->registry->delete();

$notes = Note::withTrashed()->createdBy($user->id)->get();

foreach ($notes as $note) {
$note->delete();
}
}
});

static::restored(function(User $user) {
$user->deleteUser()->dissociate();

// Add update user
$user->updateUser()->associate(Auth::user());

$user->save();

// Restore relations
$user->registry()->withTrashed()->restore();

$notes = Notes::withTrashed()->createdBy($user->id)->get();

foreach ($notes as $note) {
$note->restore();
}
});
}

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
];

/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];

/**
* Get the registry record associated with the user.
*/
public function registry()
{
return $this->hasOne('AppModelsCoreRegistry', 'id_user');
}

}

Come potete vedere non cambia praticamente nulla dall’utilizzo di un observer, a parte la possibilità di poter richiamare la variabile protected $forceDeleting senza dover passare per il getter.

Il Test

Siamo giunti al momento della domandona del giorno: funzioneranno correttamente questi eventi? Verranno correttamente modificati modelli e relazioni “a cascata”?

Per rispondere a questa domanda vi riporto il codice del test che ho eseguito su tre routes create appositamente per simulare le normali operazioni di un controller. Nel dettaglio il test è suddiviso in quattro parti:

  1. La prima richiama la route “store” che crea un modello Registry e un modello Note e verifica che gli utenti di creazione siano corretti;
  2. La seconda richiama la route “update” che modifica un modello Registry e crea un nuovo modello Note e verifica che gli utenti di modifica/creazione siano corretti;
  3. La terza richiama la route “delete” che elimina un modello User e verifica che venga inserito l’utente di eliminazione durante il soft delete sul modello Registry ad esso collegato, così come per tutte le note create dall’utente del test;
  4. La quarta richiama nuovamente la route “delete” che questa volta andrà ad eliminare definitivamente dal database il modello User e a cascata le sue relazioni;

use IlluminateFoundationTestingWithoutMiddleware;
use IlluminateFoundationTestingDatabaseMigrations;
use IlluminateFoundationTestingDatabaseTransactions;

class ExampleTest extends TestCase
{

use DatabaseMigrations;

/**
* A basic functional test example.
*
* @return void
*/
public function testModels()
{
$this->visit('/')
->see('Laravel');

// Create user
$user = factory(AppModelsCoreUser::class)->create();

// Test creation
$response_create = $this->actingAs($user)
->json('POST', '/storeRegistry', ['name' => 'John', 'surname' => 'Doe'])
->seeStatusCode(200)
->decodeResponseJson();

$registry_id_create_user = $response_create['registry']['create_user']['id'];
$create_note_id_create_user = $response_create['note']['create_user']['id'];

$this->assertEquals($user->id, $registry_id_create_user);
$this->assertEquals($user->id, $create_note_id_create_user);

// Test update
$response_update = $this->actingAs($user)
->json('POST', '/updateRegistry', ['id' => $response_create['registry']['id'], 'name' => 'John', 'surname' => 'Dooe'])
->seeStatusCode(200)
->decodeResponseJson();

$registry_id_update_user = $response_update['registry']['update_user']['id'];
$update_note_id_create_user = $response_update['note']['create_user']['id'];

$this->assertEquals($user->id, $registry_id_update_user);
$this->assertEquals($user->id, $update_note_id_create_user);

// Test soft delete
$response_soft_delete = $this->actingAs($user)
->json('POST', '/deleteUser', ['id' => $user->id])
->seeStatusCode(200)
->decodeResponseJson();

$user_id_delete_user = $response_soft_delete['user']['delete_user']['id'];
$registry_id_delete_user = $response_soft_delete['user']['registry']['delete_user']['id'];

$this->assertEquals($user->id, $user_id_delete_user);
$this->assertEquals($user->id, $registry_id_delete_user);

foreach (AppModelsCoreNote::createdBy($user->id)->with('delete_user')->get() as $note) {
$this->assertEquals($user->id, $note->delete_user->id);
}

// Test force delete
$this->actingAs($user)
->json('POST', '/deleteUser', ['id' => $user->id])
->seeJsonEquals([
'deleted' => true,
]);
}

}

Se volete ordinare qualcos’altro per provare ad implementare il codice e vedere se il test va a buon fine, questo è il momento giusto per farlo!

… ed ora?

Cosa ne pensate di questa modifica a cascata dei modelli? L’avete già utilizzata o preferite seguire altre vie per gestire le relazioni?

Sempre riguardo le relazioni e la loro gestione, quali sono i problemi più comuni che vi siete ritrovati ad affrontare?

In questo esempio abbiamo visto solo una delle possibili implementazioni degli eventi di un modello. Tuttavia, nel mezzo possono essere inserite molte altre automazioni, come l’invio di una mail di benvenuto alla creazione dell’utente. Voi utilizzate questi eventi? Preferite la maniera “classica” o la creazione degli Observer?

Lo scopo dei Laravel Cafè è il confronto. Per questo motivo, fatevi avanti e fatemi sapere cosa ne pensate! Non solo: se state avendo un problema collegato a model e relazioni, lasciate un commento e vediamo insieme cosa si può fare!

E non scordate che c’è anche lo Slack di Laravel-Italia! Vi aspettiamo!

I Package della Settimana – N.13 – 31 Ottobre 2016

0

La rubrica I Package della Settimana si propone, ogni Lunedì mattina, di suggerire cinque package per Laravel descrivendoli brevemente. L’obiettivo? Riuscire a dare nuovi spunti ai lettori, far conoscere nuovi tool ed ottimizzare il flusso di lavoro. Durante la pausa caffè.

Vediamo cosa c’è questa settimana!

I Package della Settimana


  • Annotations: i più attenti sicuramente hanno già sentito parlare delle annotation. Erano state introdotte nella versione 5.0 del framework, ma poi sono state scartate. Sono finite quindi nel progetto LaravelCollective, che si occupa di mantenerle attive e pronte per essere usate nei tuoi progetti;
  • Laravel elFinder: questo package consente di integrare alla perfezione elFinder, uno dei file manager più usati sul web. Fornisce il supporto completo all’installazione, configurazione tramite file apposito, integrazione con i disk messi a disposizione da flysystem, e così via. Da provare!
  • Laravel Tagging: Laravel Tagging si occupa di implementare, attraverso un’installazione easy ed un semplice trait, il concetto di “taggabilità” di un elemento. Basterà installare il package, configurarlo al volo ed includere nel proprio model il trait Taggable e avremo un sistema di tag pronto ad essere usato;
  • Pretty Routes: uno di quei package che vanno ad aggiungere un po’ di “zucchero” alla vita dello sviluppatore. Il compito di Pretty Routes infatti sarà mostrare allo sviluppatore, in modo più gradevole del solito, l’elenco delle route registrate per il progetto a cui sta lavorando. Una volta installato esporrà una semplice route /routes da usare per consultare l’elenco;
  • Twitter Notification Channel: questo semplice package molto interessante permette di pubblicare un update di status su twitter usando il sistema di notifiche di Laravel 5.3. Sicuramente da tenere in considerazione se si usa l’ultima versione del framework e si vuole lavorare con Twitter;

E tu, hai qualche package da suggerire? Lasciaci le tue impressioni qui sotto, in un commento, o faccelo sapere sullo Slack di Laravel-Italia!

Ci vediamo la prossima settimana.

Annuncio – Laravel Meetup a Milano! – 27 Ottobre 2016

0

Ecco un altro articolo che fa davvero piacere scrivere.

Tra una settimana, il 27 Ottobre 2016, si terrà a Milano un meetup interamente dedicato a Laravel! Un’ottima occasione per conoscersi, condividere un po’ di conoscenza e fare gruppo.


LINK ALLA DIRETTA STREAMING

Guarda la diretta su YouTube!


Il meetup, organizzato da S2K Agency, si terrà presso Mikamai in Via Giulio e Corrado Venini 42! L’evento prevede due talk, su due argomenti decisamente interessanti:

  • Integrazione di Laravel con WordPress;
  • Autenticazione con JWT;

Per chi non dovesse avere la possibilità di venire, nessun problema! Verrà predisposta una diretta su Google+ ed il link verrà inserito anche qui al momento dell’evento, quindi stay tuned!

Per ulteriori informazioni, segui gli sviluppi sul sito del meetup!

Aggiunte al Sito – Inviare su Slack gli Ultimi Articoli Pubblicati

0

Dopo aver finalmente aggiornato la codebase del sito a Laravel 5.3, sono riuscito a dedicarmi meglio all’aggiunta di tante piccole implementazioni che ho in mente. Nel prossimo periodo, infatti, ne vedremo delle belle!

Una delle cose che più mi sprona, tra l’altro, è di poterle condividere anche qui… visto che il codice è totalmente open-source!



Ad ogni modo, molti di voi già sanno che c’è uno Slack, già usato da tanti membri della community, sui cui ci confrontiamo, ci scambiamo opinioni, e così via. Da oggi, per comodità degli utenti, ogni volta che un nuovo articolo sarà pubblicato sul sito ne verrà mandato un link anche sul canale #general del nostro Slack.

Tutti i dettagli dell’implementazione, se dovesse interessare, possono essere trovati qui.

Un modo in più per rimanere connessi, e per conoscere nuovi strumenti e tecniche di lavoro.