Come realizzare un’applicazione Laravel Multi-tenant — Part 6 — Automated Tests

Francesco Lettera
5 min readJan 27, 2019

--

Parte 0 | Parte 1 | Parte 2 | Parte 3 | Parte 4 | Parte 5 | Parte 6

Questa è la traduzione della serie Full-featured multi-tenant Laravel app from scratch di Ashok Gelal

In questa parte ci occuperemo di:

✅ Fare i necessari cambiamenti all’UI

✅ Aggiungere e registrare un account tenant legato alle routes

✅ Mostrare un profilo utente e permettergli di aggiornarlo

✅ Aggiungere test per la view del profilo e l’update

Una app multi-tenant è complessa già di suo, e la sua complessità aumenta man mano che l’applicazione diventa “grande”. Se non stabilisci delle regole sull’organizzazione del tuo progetto sin dall’inizio, la tua app sarà impraticabile dal punto di vista della manutenzione e della scalabilità.

Questa parte della serie è piuttosto facile e potrebbe sembrare non importante o poco profonda. Ma dal momento in cui ci siamo inoltrati in una app tenant, faremo alcuni piccoli cambiamenti che renderanno facile aggiungere più funzionalità e test automatici in futuro.

Tra le altre cose, ti mostrerò come organizzare la tua app tenant legata alle classi, e come gestire le tue route, così come aggiungere alcuni metodi importanti ai tuoi test in modo che tu possa iniziare ad aggiungere test automatici alla tua app.

Salta questa parte solo se non ti interessa come organizzare la tua multi-tenant Laravel app

1. Fare i cambiamenti necessari all’UI

Faremo alcuni cambiamenti all’UI — aggiornando all’ultima versione di Bootstrap 4, aggiungendo una sidebar, e aggiungendo un layout specificatamente per le view.

In brevità, non posterò tutti i cambiamenti, ma tutti possono essere analizzati nel mio repository su GitHub

Uno dei cambiamenti più importanti è l’aggiunta di una view per il layout, resources/views/tenant/app.blade.php. Potremmo modificare il classico file app.blade, ma avere un layout dedicato rende tutto il progetto più pulito.

2. Aggiungere e registrare gli account tenant collegati alle routes

Invece di aggiungere tutte le nostre route a routes/web.php , organizzeremo le nostre route in file separati. Anche perché Laravel metterà in cache tutte le route, e non dovremo preoccuparci delle performance.

Creiamo il file routes/account.php:

Per adesso non preoccupiamoci di ProfileController , ma focalizziamoci sul fatto che useremo il nome tenant.account.profile.edit

Questo file di route non è caricato da Laravel automaticamente, devi registrarlo nel file app/Providers/RouteServiceProvider.php . Apri il file e fai queste modifiche:

public function map()
{
$this->mapApiRoutes();

$this->mapWebRoutes();

$this->mapTenantRoutes(); // add this call
}

Aggiungiamo il metodomapTenantRoutes() :

/**
* Definte the "tenant" routes for the application.
*
* These routes should be aliased as tenant.x to make it explicit when referring
* from view. This would help remove confusion in the future when we have
* "system" routes. Think of it as a namespace for the routes.
*/
protected function mapTenantRoutes()
{
Route::middleware(['web', 'auth'])
->namespace($this->namespace)
->as('tenant.account.')
->prefix('account')
->group(base_path('routes/account.php'));
}

Nota come as('tenant.account.') sia una sorta di namespace per questa route. Questo ci permette di chiamare il nostro account legato alla route in questo modo tenant.account.profile.edit

3. Mostrare un profilo utente e permettere il suo aggiornamento

Adesso che le nostre route sono pronte, creiamo un controller, app/Http/Controllers/ProfileController.php per la gestione degli aggiornamenti di profilo:

Questa azione è invocata quando si effettua un routing a tenant.account.profile.edit, che renderizza una view: resources/views/tenant/account/profile.blade.php

Come puoi notare, se è presente una complessa gerarchia di file, mi piace tenere organizzati i file mettendoli nelle rispettive cartelle.

profile.blade.php è un file troppo grande da postare qui, ma è una semplice view che ha un form con un campo input text per mostrare/aggiornare il nome dell’utente e un altro campo per mostrare/aggiornare la sua email. Lo trovi nel file su GitHub.

L’unica cosa importante da osservare nel file profile.blade.phpè l’action del form, impostato su tenant.account.profile.update. Abbiamo già aggiunto questa route, ma non abbiamo aggiunto l’action per occuparci dell’update, facciamolo. Aggiungiamo il seguente metodo al controller ProfileController.php:

...public function update(UpdateProfileRequest $request)
{
$request->commit();
session()->flash('alert', ['type' => 'success', 'message' => 'Your profile has been updated.']);

return redirect(route('tenant.account.profile.edit'));
}
...

Il metodo si spiega da solo: stiamo utilizzando l’oggetto $request e utilizzando le sessioni per mostrare un messaggio e poi redirezionare sulla pagina tenant.account.profile.edit .

Ma dove si trova la classe UpdateProfileRequest e perché ne abbiamo bisogno?

Bene, quando facciamo una request POST/PATCH/DELETE, quasi sempre attivo le custom request di Laravel. Questo non solo rende pulito il codice, ma permette al controller di “mantenersi” leggero. Diamo un’occhiata:

Niente di complesso qui, authorize()può solo restituire true anche perchè la route è già controllata dal middleware auth(controlla il metodo RouteServiceProvider.php#mapTenantRoutes() che abbiamo aggiunto poco fa).

rules() definisce le regole di validazione utili per gli input data. Nel nostro caso, sia il nome che l’email sono campi richiesti. Inoltre, l’email deve essere univoca, a meno che non appartenga a questo utente. Ti faccio notare come stiamo utilizzando tenant.users quando definiamo l’univoca regola per il campo email. Il tenant. è un prefisso che dice a Laravel che ha bisogno di dare un’occhiata alla connessione db chiamata tenantper consultare il db stesso.

Infine, commit()fa il lavoro di aggiornamento del nome e dell’email per l’utente autenticato.

Una cosa importante da osservare è come abbiamo separato il codice in due classi — il controller e l’oggetto request associato. Questo ha diversi vantaggi — entrambi le classi sono pulite, entrambi le classi sono “self-documented” e sono molto leggibili, questo faciliterà molto scrivere unit test per ogni classe, ogni classe può indipendentemente crescere in termini di complessità qualora ce ne fosse bisogno.

La semplicità è un prerequisito per la affidabilità. — Edsger W. Dijkstra

Ho cercato di seguire questo Controller/Request pattern quasi religiosamente nei miei progetti attuali. E per la nostra app Townhouse, faremo lo stesso.

3. Aggiungere test per la view del profilo e il suo aggiornamento

Aggiungiamo alcuni test automatici per l’aggiornamento del profilo. Ma prima abbiamo bisogno di un piccolo cambiamento nel nostro metodo app/Tenant.php#createFrom() perchè ci permetta di passare opzionalmente una password:

...public static function createFrom($name, $email, $password = null)
{
...
...
$admin = static::makeAdmin($name, $email, $password ?: str_random());
...
...
}

Abbiamo anche bisogno di aggiungere altri metodi nel tests/TenantAwareTestCase.php per rendere ancora più facili i nostri test. Dai un’occhiata ai cambiamenti su GitHub.

Adesso aggiungiamo i test tests/Feature/ProfileTest.php:

E’ tutto per oggi.

Sei già iscritto alla Laravel Letter? No???
https://laravel.happyflow.it/

https://laravel.happyflow.it/

--

--