Qualche giorno fa mi sono imbattuto in un comportamento a dir poco bizzarro sulla mia macchina Ubuntu: digitando nel browser un domain name completamente a caso, tipo http://mememem.it/ o http://nxdomain1.it/ (il nome è voluto: NXDOMAIN …
Ovvero come si scambiavano messaggi instantanei due utenti Unix prima dell’avvento di WhatsApp e Telegram. Qualche giorno fa stavo ragionando sui primi sistemi di messaggistica su ARPANET [E] e mi è tornato in mente il …
Da un po’ di tempo avevo il PC disseminato di container Docker installati qua e là, senza un vero criterio. Ho deciso che era il momento di mettere ordine e, allo stesso tempo, di costruire …
Continuo qui una serie di articoli che riguardano il funzionamento di Docker. In questo post ho condiviso la comprensione del perché Docker non possa essere pensato come un virtualizzatore hardware: non viene virtualizzata una macchina …
Qualche giorno fa Ubuntu mi ha mostrato una notifica che annunciava la disponibilità di un aggiornamento del firmware UEFI del mio portatile HP. Ci ho messo troppo a cliccarla, come spesso accade con le notifiche …
Il nome dell’applicazione può essere visualizzato nelle views di Laravel e può essere definito in almeno tre modi
file .env
Il file di configurazione delle variabil di ambiente è situato nella radice dell’albero dell’applicazione e si chiama .env.
Al suo interno si trova una variabile che possiamo personalizzare:
APP_NAME=MyBeautifulAppName
Poi, all’interno della view, ci riferiremo a questa variabile nel modo seguente
{{env('APP_NAME')}}
Tuttavia il file .env non viene normalmente sottoposto a versione perché è caratteristico del singolo deploy su ogni server. Quindi tendo a non utilizzare questa variabile.
file config/app.php
Questo file contiene una variabile predefinita:
'name' => env('APP_NAME', 'MyBeautifulAppName'),
Qui in realtà fa un’override del file .env, infatti utilizza la stessa variabile env, però è a runtime, cioè acquisisco il valore di default e lo metto in un’altra variabile name, eventualmente modificandolo. Per accedere al valore della variabile name in una view si scrive:
{{ config('app.name', 'Default name') }}
il secondo argomento essendo il default.
Condivisione di una variabile tra tutte le view
Il terzo metodo è il più elaborato ma consente una maggiore flessibilità: si definisce una variabile dal nome arbitrario nel file
<APP>/app/Providers/AppServiceProvider.php
e al suo interno, nel metodo boot(), si invoca il metodo statico View::share():
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
View::share('appTitle', "MyBeautifulAppName - powered by Laravel");
}
La variabile è poi disponibile nelle view: {{$appTitle}}.
Se questi contenuti vi risultano utili, condivideteli. Ciao 🙂
BabelEdit è un programma che ci aiuta a localizzare l’applicazione. Si devono infatti personalizzare i file di localizzazione della lingua che già Laravel mette a disposizione nella cartella resources/lang. Fondamentalmente l’applicazione fa uso di etichette che poi recuperano per associazione il valore esteso del testo da visualizzare.
Per aiutare in questo tipo di attività, soprattuto se il lavoro di traduzione viene svolto da non programmatori, si può utilizzare questo software. Non sto facendo endorsement, voglio solo riportare una possibile soluzione cooperativa.
Download e installazione
Una volta installato, (al solito io uso Linux) lanciarlo con
$ BabelEdit
Dashboard di BabelEdit
Utilizzo di BabelEdit
Ovviamente prima occorre dare l’OK per l’accettazione dei termini di licenza e per l’attivazione della Trial di 7 giorni.
Si carica il file delle etichette originarie (che sono quelle del plugin Auth):
Seleziona la sorgente dei dati
Per il nostro progetto, i file di dizionario sono PHP. Si apre così la maschera per caricare i file da tradurre:
Configurazione dei linguaggi
Si possono aggiungere i file origine e destinazione
BabelEdit aggiungere le lingue
Si associa il file alla lingua
Aggiunta nuova lingua
Si ripetono questi passaggi per tutte le lingue che vogliamo trattare.
Cliccando su “Primary Language” selezioniamo qual è la lingua di riferimento da cui derivieremo tutte le traduzioni:
BabelEdit selezione lingua primaria
si apre la maschera di selezione del servizio
Babel configurazione del servizio di traduzione online
Apriamo un messaggio, vengono visualizzate le etichette così come sono state configurate nei due file di lingua. Cliccando in basso a destra lo scudo con stellina
Selezione del servizio di traduzione online
si può vedere la traduzione proposta dal servizio online (ad esempio Google)
Babel traduzione automatica
cliccando sulla proposta, il valore dell’etichetta viene modificato con quello proposto da Google. In assenza del servizio si può tradurre in autonomia.
CRUD è l’acronimo per Create, Read, Update, Delete che sono le quattro operazioni fondamentali sui dati.
Laravel non ha molta automazione da questo punto di vista, dobbiamo costruirci le form HTML per creare e modificare i file, così come le viste per visualizzare tutti i dati oppure solo un record e alla fine la funzionalità di cancellazione.
Però ha un sistema di creazione di pagine di Blade che ci permettono il riutilizzo intelligente di molte parti di layout.
In questo aritcolo ci muoveremo su tutti e tre i domini del paradigma MVC.
CRUD 1: creiamo il template
Come prima operazione creiamo un template che riutilizzeremo in tutta l’applicazione. Lo creiamo in resources/views/layouts/app.blade,php:
La direttiva @yield prenderà il contenuto della sezione content della vista chiamata e la metterà qui. Quindi se il controller chiama la view ‘edit.blade’ il suo contenuto verrà calcolato (cioè verranno messi dentro i dati) e poi prenderà posto dentro il tag “container”. È analoga ad una direttiva include. Quindi tutte le view avranno il titolo Logistic Mapper in questo caso.
CRUD 2: rassegna dei metodi da implementare
Create
Cominciamo con la creazione di nuovi records, operazione che nel dominio dei database è una insert.
Attenzione: questa azione consiste nella visualizzazione della form vuota (non stiamo ancora creando alcun record). Il verbo HTTP a cui viene associata è GET e il nome della rotta è company.create. Ciò significa che la pagina web che dobbiamo progettare starà sotto /resources/view/company e si chiamerà create.blade.phpe verrà invocata dal metodo create del controller CompanyController.
La prima direttiva (@extends) indica in quale template deve essere inclusa questa pagina web, ed è layouts/app.blade.php (blade.php è un suffisso standard, il formato in dot notation della vista è layouts.app).
La seconda dichiarazione @section è la sezione content, che è il frammento che dev’essere incluso nel template layouts.app.
Segue la form. Attenzione!
la action dev’essere semplicemente /company: la risorsa da invocare davvero non è decisa qui ma è decisa a livello della tabella di routing, in questo caso è il metodo CompanyController@create
il method dev’essere conguente con il metodo espresso nella tabella di routing , quindi GET.
c’è un’ulteriore direttiva @csrf che serve per evitare che il metodo venga chiamato via HTTP da un’altro sito. Ci siamo già occupati di questo attacco in questo post.
Come si vede ho una serie di campi di tipo testo e una combobox che devo popolare in anticipo. Il controller deve fare tutte queste cose:
public function create()
{
$company_types= CompanyType::all();
return view('company.create', compact('company_types'));
}
La query su CompanyType serve appunto per procurarsi la combo box. Per questo si usa il metodo all() del model. Il risultato questo:
Create company
Store
DOpo aver compilato il modulo, occorre salvare il dato. L’operazion Eloquent equivalente all’insert come detto è store. Quindi per vedere come dobbiamo comprtarci dobbiamo studiarci la riga corrispondente della tabella di routing:
| POST | company | company.store | App\Http\Controllers\CompanyController@store |
In questo caso il form HTML dovrà utlizzare il metodo POST che in questo caso viene preso davvero in consderazionie e non viene riscritto da alcuna direttiva. Inoltre il metodo di controller sarà CompanyController@store . Anche se è prevista la view company.store non si verrà usata perché verrà fatto un redirecto verso l’elenco di tutte le Company (company.index) in caso di successo.
Quindi il metodo store è il seguente:
public function store(Request $request)
{
$company = new Company($request->all());
$company->save();
return redirect(route('company.index'));
}
È semplicissimo: si crea un nuovo oggetto della classe Company inizializzandolo con i valori dell’array $request->all().
Attenzione! se tentiamo di passare come argomento semplcemente $request, Laravel s’incazza perché $request è un oggetto, mentre il costruttore del model vuole un array.
Il metodo save() di Eloquent si trasforma in una insert della tabella companies.
Finalmente, viene inviata al browser una direttiva di redirect verso la view company.index.
Edit
Il terzo metodo ci consente di editare i dati per poi eseguire un update. Quindi questo metodo serve per visualizzare la form di modifica e non per modificare i dati, operazioe che sarò in carico al metodo update.
Al solito ci facciamo guidare dalla tabella di routing
Qui l’URI di accesso alla pagiba di edit era già stato generato nella pagina index dalla funzione
route('company.edit', $v->id)
l’URI generato è proprio del tipo company/{company}/edit come indicato dalla tabella di routing, dove il segnaposto {company} è sostituito dal valore numerico della chiave primaria della company. Il verbo HTTP utilizzato anche in quetso è GET (Apache deve servirci una pagina web).
Il metodo edit del CompanyController deve caricare dal database i dati da inserire nella form di modifica:
il valore $company_type_id che ci serve solo per posizionare correttamente la combo box; non sarebbe necessaria ma la scrittura della view ne risulta così un po’ più chiara.
dobbiamo utillizzare il verbo HTTP PUT e impostare la action su company/{company}.
Il metodo di Controller update sarà
public function update(Request $request, $id)
{
$company = Company::find($id);
$company->update($request->all());
return redirect(route('company.index'));
}
Viene creato l’oggetto $company da una query sul model per ID, viene passato al metodo update l’array $request->all() e alla fine viene rediretto il browser sull’indice.
Perché per operazioni diverse siamo verbi HTTP diversi?
La RFC-2616 (Hyper Text Transfer Protocol HTTP/1.1) stabilisce che il metodo PUT va usato per sostituire una risorsa esistente nel server. Ci si ricordi che all’inizio HTTP era un protocollo che si usava solo per lavorare coi file.
Il metodo POST invece va usato per trasferire dati (nuovi) dal client al srever, quindi il risultato è la creazione di una nuova risorsa.
Per questo il metodo PUT è idempotente: se applicato più volte, produce lo stesso risultato.
Il metodo POST invece non lo è, ogni volta che lo si applica vengono create nuove risorse; questo è proprio il comportamento delle operazioni database di Update (PUT) e Insert (Post), per cui questo è il senso di utilizzare i verbi HTTP in modo appropriato per le varie operazioni.
Questa distinzione va fatta anche quando si progetta l’interfaccia API di un server.
Abbiamo visto come intercettare una rotta (URI) e come gestirla con una funzione anonima, utilizzando sia una vista per visualizzare una pagina complessa che un output diretto.
Abbiamo poi visto come gestre questa rotta utilizzando un controller, e limitandoci a visualizzare una stringa di benvenuto.
Ora ci accingiamo a fare in modo che il controller esegua una query sul model in conseguenza ad una certa rotta. Per esempio abbiamo visto che esiste un metodo standard del controller chiamato index che effettuerà una query che estrae tutti i record del model (e quindi attraverso Eloquent, della tabella del database corrispondente a quel model).
Accesso diretto al database dal Controller (senza model)
Con questa modalità, che è quella dirty, sconsigliata, possiamo vedere come si possibile interagire direttamente con il database dal controller. Ma non è la prassi consigliata, lo facciamo vedere solo come primo approcio.
Occorre personalizzare il metodo index (ad esempio) del CompanyController come segue:
public function index()
{
$companies = DB::select('select * from companies');
return view('company.index', compact('companies'));
}
la IDE si arrangia di solito ad importare le classi di cui abbiamo bisogno; in questo caso nel preambolo verrà aggiunto il namespace della classe DB per potervi accedere:
use Illuminate\Support\Facades\DB;
Accesso ai dati attraverso il model
Tuttavia impostando bene il model (dichiarando tutte le dipendenze uno a uno, uno a molti ecc) questi può fare molto meglio il lavoro per cui è sufficiente scrivere questo metodo, invece:
public function index()
{
$companies = Company::all();
return view('company.index', compact('companies'));
}
il metodo all() è già definito nella classe Model di cui Company è un’estensione, non serve che lo scriviamo. Model ha una quantità di metodi già pronti per l’uso, più avanti ne vediamo anche un altro. Infatti la classe Company ha soltanto questa dichiarazione:
use Illuminate\Database\Eloquent\Model;
class Company extends Model
{
}
Dovremo personalizzare la classe Company solo se vorremo fare delle cose particolari.
Come si vede è estremamente semplice recuperare i dati attravero il model, non serve scrivere nessuna query, già Eloquent lo fa per noi.
Passaggio dei dati dal model alla view
L’altra “magia” che fa il controller è quella di inviare i dati ad una vista. Questa volta ci serve una pagina un po’ più elaborata del semplice output diretto.
Avendo utilizzato per la gestiione delle rotte il metodo resource() abbiamo tutte le rotte belle e pronte e serve creare per ogni rotta una pagina web sotto la directory resources/views/. La convenzione è che ogni view abbia una cartella dedicata ad ogni model: per esempio creeremo la cartella /resources/views/company e al suo interno definiremo tanti file quente sono le rotte da servire e cioè quanti sono i metodi del controller:
index
edit
create
ecc…
L’insieme delle rotte si può stampare con il comando php artisan route:list
route:list con resource
Definiamo, tanto per partire due viste con Blade sotto /resources/views/company/ chiamandole con il nome del metodo del controller che le invocherà (anche questa è una convenzione da segurie per farci venire fuori i risultati con il minor sforzo):
index.blade.php (che conterrà la tabella con l’elenco delle company)
edit.blade.php (che conterrà la form di modifica di un record di company)
Il controller dovrà fornire alla view tutti i dati che vogliamo mostrare. Per esempio, per quanto riguarda la pagina delle company, vogliamo mostrae i dati della tabella company. Per questo mettiamo nella variabile $companies i dati ottenuti dal model come sopra e li passiamo alla view con la funzione compact($companies).
Nella view poi facciamo il ciclo. Una prima versione della view potrebbe essere questa, molto grezza:
Del linguaggio Blade parliamo in un articolo a parte, qui ci interessa vedere come vengono acceduti i dati che il controller passa alla view. La view vede un array di oggetti che si chiama $companies e cicla su questo array prelevando per ogni ciclo gli attributi dell’oggetto-componente dell’array $v. Il risultato è il seguente, è molto grezzo ma ci consente di vedere che stiamo progredendo rapidamente:
Laravel: company index page
Una attenzione particolare va alla costruzione del link che ci consente di navigare dall’elenco delle Company al singolo record per poterlo editare. Notate il costrutto veramente efficiente e semplice:
href="{{ route('company.edit', $v->id) }}"
la funzione route() chiama per nome la risorsa che gestisce l’edit; è la rotta che leggiamo dalla tabella sopra, generata con artisan:
In questa vista index.blade avevamo soltanto una variabile da visualizzare (che il controller passa alla view) che è il recordset. Più in generale alla view devono essere passati dati diversi che conviene impacchettare in un array associativo, come vediamo nel secondo esempio che invece riguarda la view che contiene la form HTML di gestione di un singolo record:
il record da editare: queste informazioni vengono prelevate dal model Company, che è la rappresentazione ORM della tabella companies, con il metodo find() a cui passiamo l’$id del record che ci arriva dalla request ed è il parametro passato al controller. Il risultato è equivalente alla select sul record $id della tabella companies. Ma è molto meno stressante. Vale la pena di notare che tutto questo passaggio di parametri $id è fluido, non dobbiamo preoccuaprci del nome di campi e delle variabili, per il fatto che abbiamo adottato la convenzione.
l’elenco delle company_types che deve consentirci di costruire la combobox omonima: per questo invochiamo un altro Model, CompanyType, che è quello che sovrintende alla tabella company_types. Qui ci servono tutti i valori della tabella per cui utilizziamo il metodo all() di Eloquent.
il valore della chiave esterna company_type_id per questo record che ci deve aiutare a posizionare la combobox nella selezione corretta; lo estraiamo direttamente dall’oggetto $company e lo mettiamo a disposizione della vista in modo separato (non sarebbe necessario, ma ci consente di scrivere molto meno nella view).
Il controller quindi definisce una variabile $array che è un vettore associativo con le tre componenti che abbiamo enumerato sopra e che vengono passate alla view come array di oggetti con la funzione compact().
Finalmente la view che mostra la form di editing è la segente (company/edit.blade.php):
Proseguendo con l’invocazione delle rotte con Laravel, facciamo un riassunto: abbiamo visto come invocare l’applicazione usando una rotta. E abbiamo visto come si può usare il metodo statico Route::get() per associare un percorso URI (rotta) ad una funzione anonima. La quale al suo interno può emettere un output semplice (echo) oppure invocare una vista Blade.
Il passo successivo è quello di alzare il tiro ed effettuare una elaborazione più complessa a fronte dell’URI di richiesta.
Il componente naturale che viene chiamato per gestire una richesta è il controller. Come si ricorderà, il controller è il centro dell’applicazione (vedi l’articolo Laravel – primi passi – MVC) ed è quindi in questa sede che va gestito il processo.
Ma vediamo prima di tutto come creare un nuovo controller.
In generale dovremo creare un controller per ogni tabella del database:
una tabella -> un model -> un controller con diverse azioni -> diverse view, una per ogni azione, ma non solo.
Lo facciamo da Artisan con il quale, oltre a creare le migrazioni per defnire le tabelle, crea anche i Controller (e tantissime altre cose ancora):
$ php artisan make:controller CompanyController
Un piccolo inciso:
il comando è make:controller
il nome del controller va scritto in CamelCase con il nome della tabella al singolare. Questo perché se ci adeguiamo alla convenzione, ci risparmiamo tonnellate di file di configurazione da gestire (convention over configuration). In ogni caso è possibile derogare dalla convenzione agendo su opportuni file.
Il comando sopra ci crea un controller vuoto (c’è sola la dichiarazione della classe), ma riempiamolo un po’. Se invece del precedente, lanciamo questo comando:
il controller avrà al suo interno già definito 7 risorse che sono i metodi standard per effettuare le operazioni di CRUD sul modello:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class CompanyController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index() // elenco di tutti i record
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create() // for di inserimento nuovo record
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request) // salvataggio in insert
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id) // mostra un singolo record
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id) // form di modifica di un record esistente
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id) // salvataggio in update
{
//
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id) // delete di un record
{
//
}
}
Ora se personalizziamo nel modo seguente il metodo index():
public function index()
{
return "This is the Company index";
}
e definiamo la rotta così
Nota: dalla versione 8 di Laravel in poi, nella definzione della rotta, il controller deve essere specificato con tutto il suo namespace:
È possibile anche passare parametri dalla rotta al controller. Per esempio lo faremo quendo vorremo mostrare un singolo record utlizzando il metodo show del controller; definiamo quindi la rotta nel file routes/web.php (nota il segnaposto {id} per il parametro della rotta):
e quindi scriviamo il metodo show del CompanyController che gestice questa rotta(nota il parametro di ingresso $id):
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show(int $id):string
{
//
return "This is company # ".$id;
}
Il risultato è il seguente:
Metodo show per gestire la rotta
Ultimo paragrafo per resentare la rotta speciale resource che praticamente genera per noi tutte le rotte standard che riguardano uno stesso modello e tutti i controller stadard.
Avremo un rotta per l’index e un metodo di controller per l’index; una rotta di show e un cotnroller per lo show. e così va.
se ci facciamo visualizzare la tabella delle rotte da Artisan vediamo:
$ php artisan route:list
e questa è la tabella delle rotte che verrà visulizzata:
route:list con resource
sono stati generati tutte le rotte, qassegnata ad ognuna un nome e associato il verbo del protocollo HTTP corrspondente (PUT/PATCH per update, POST per store e così via).
Se proviamo dal browser le varie rotte non avrò neanche un errore 404, nel senso che trova la risorsa che magari provoca un errore 500, oppure funziona: sono infatti da implementare le view che servono le rotte di tipo create, edit, update e delete. Inoltre, se si analizza la tabella delle rotte,si vedrà che l’URI non cambia al cambiare del metodo per show, update delete: in questo caso viene utilizzato un diverso verbo del protocollo HTTP!
Nel prossimo articolo farà la comparsa il model, vedremo così come il Controller chiede i dati al model.
Laravel adotta lo schema convieni anziché configurare che in sostanza vuol dire adeguarsi ad uno standard convenuto per i nomi dei file in modo tale da evitare di dover scrivere e manutenere prolissi file di configurazione in cui, oggetto per oggetto, definiamo il nome che devia dalla convenzione.
Un esempio rende tutto chiaro.
Se abbiamo un modelloCompanyType (in CamelCase al singolare), dovremo comportarci così per tutti gli altri oggetti
il nome della tabella è al plurale (company_types)
il nome del controller è al singolare (CompanyController)
i nomi delle chiavi esterne prendono dalla tabella al singolare e aggiungono _id (company_type_id)
i nomi delle rotte sono al plurale (/companies)
Più avanti estenderò questa lista aggiungendo altre convenzioni. Ma intanto riassumo n una tabella:
Oggetto
Modalità
OK
KO
Controller
singolare, CamelCase
PostController
PostsController
Route
plurale
posts/1
post/1
Named Route
snake_case con notazione punto
posts.show_active
show-active-posts
Model
singolare, CamelCase
Post
Posts
Tabella DB
plurale, snake_case
company_types
company_type
Proprietà Model
snake_case
$model->created_at
$model->createdAt
Foreign Key
nome model singolare suffisso _id
company_type_id
companyTypeId, id_company_types
Variabili
camelCase
$postAuthor
$post_auhtor
Alcune convenzioni sul naming degli oggetti PHP per Laravel
Qui di seguito scrivo una breve guida passo passo per caricare su Bitbucket un repository Git e utilizzarlo come copia di riferimento nello sviluppo. Bitbucket consente di ospitare repository remoti privati con alcune limitazioni.
Git: creare il repository in locale
Come primo passaggio occorre inzializzare il repository git in locale:
Qui è neceessario individuare il nome che git ha attribuito al branch principale per poterlo replicare in remoto:
$ git branch
* master
La versione di Git che ho a bordo è
$ git --version
git version 2.30.2
Bitbucket: creare il repository in remoto
Dopodiché bisogna creare un nuovo repository su Bitbucket, un repository vuoto in cui il branch principale divrà chiamarsi master (Bitbucket da’ l’opzione di chiamare come si vuole il branch principale, ma è bene fare molta attenzione su questo punto!).
Il progetto Laravel contiene già i file Readme.md e .gitignore quindi non è necessario generarli lato Bitbucket.
In “impostazioni avanzate” della maschera di creazione del repository Bitbucket possiamo scegliere il linguaggio con cui stiamo sviluppando, i questo caso PHP.
Quindi colleghiamo il repo locale con quello remoto con il comando:
Qui si è scelto di chiare “origin” il repository remoto come si fa spesso. Ma lo possiamo chiamare come vogliamo. Ora si può sparare sù il repository:
$ git push -u origin master
Se il risultato è il seguente:
sign_and_send_pubkey: signing failed for RSA "/home/marcob/.ssh/id_rsa" from agent: agent refused operation
git@bitbucket.org: Permission denied (publickey).
fatal: Impossibile leggere dal repository remoto.
Assicurati di disporre dei privilegi d'accesso corretti
e che il repository esista.
c’è un problema con l’autenticazione con SSH. Vedere questo articolo per risolvere il problema.
In sostanza occorre avviare l’agente ssh (se lo si deve fare ogni volta, è preferibile aggiungerlo negli script di avvio di Linux).
Infatti il risultato dopo l’esportazione delle variabili di ambiente di ssh-agent è il seguente
$ git push -u origin master
Enter passphrase for key '/home/marcob/.ssh/id_rsa':
Enumerazione degli oggetti in corso: 109, fatto.
Conteggio degli oggetti in corso: 100% (109/109), fatto.
Compressione delta in corso, uso fino a 4 thread
Compressione oggetti in corso: 100% (92/92), fatto.
Scrittura degli oggetti in corso: 100% (109/109), 226.50 KiB | 4.36 MiB/s, fatto.
109 oggetti totali (7 delta), 0 riutilizzati (0 delta), 0 riutilizzati nel file pack
remote: Resolving deltas: 100% (7/7), done.
To bitbucket.org:mxaos/complex.git
* [new branch] master -> master
Branch 'master' impostato per tracciare il branch remoto 'master' da 'origin'.
La situzione in remoto ora è la seguente
Git: caricare un nuovo repository su Bitbucket da progetto locale
Ho un nuovo repository su Bitbucket. Ho generato una chiave con ssh-keygen e ho caricato la chiave pubblica su Bitbucket.
Tuttavia il comando seguente mi va in errore:
$ git pull remote master
sign_and_send_pubkey: signing failed for RSA "/home/marcob/.ssh/id_rsa" from agent: agent refused operation
git@bitbucket.org: Permission denied (publickey).
fatal: Impossibile leggere dal repository remoto.
Assicurati di disporre dei privilegi d'accesso corretti
e che il repository esista.
Il problema è che, dopo aver generato la chiave, mi sono dimenticato di aggiungerla al keyring utlizzato dall’agent ssh. Quindi è necessario lanciare questo comando:
$ ssh-add
Enter passphrase for /home/marcob/.ssh/id_rsa:
Identity added: /home/marcob/.ssh/id_rsa (marcob@jsbach)
Alla fine si può verificare che la chiave è stata aggiunta
Due parole sulla autenticazione a chiave pubblica SSH
Posso effettuare un’autenticazione su un server remoto con questi ingredienti:
Un repository locale in cui risiede una coppia di chiavi RSA (generate con l’utility ssh-keygen)
Un agente locale (ssh-agent) che gestisce le coppie RSA generate in locale
Un server in cui gira un demone OpenSSH
Un agente remoto che colloquia con l’agente locale
Inoltre, il protocollo SSH implementa l’inoltro dell’agente, un meccanismo per cui un client SSH consente a un server SSH di utilizzare l’agente ssh locale sul server a cui l’utente accede, come se fosse locale.
Quando l’utente contatta il client SSH sul server, il client tenterà di contattare l’agente implementato dal server e il server inoltra la richiesta al client che ha originariamente contattato il server, che la inoltra ulteriormente all’agente locale. In questo modo, ssh-agent e agent forwarding implementano il single sign-on che può progredire in modo transitivo.
Comunque: se oggi funziona e dopo un po’ non funziona più, è perché dev’essere riavviato l’ssh-agent.
SSH agent
ssh-agent è in sostanza un portachiavi. Un programma per contenere chiavi private usate per l’autenticazione a chiave pubblica.
Attraverso l’uso di variabili d’ambiente, l’agente può essere individuato da ssh e utilizzato automaticamente per l’autenticazione durante la registrazione in altre macchine.
L’avvio del portachiavi con inizializzazione delle variabili di ambiente viene fatta con il comando
$ eval `ssh-agent -s`
Per esempio le variabili possono assumere valori come questi:
Qui le variabili inizializzate sono due: SSH_AUTH_SOCK e SSH_AGENT_PID.
SSH può accedere al portachiavi utilizzando queste due informazioni.
Se l’agente vi chiede nuovamente la passphrase
Vuol dire che non avete aggiunto la chiave al keyring; anzi, probabilmente non è colpa vostra: lo avete fatto ma l’accesso al portachiavi è come sempre a tempo: dopo un po’ di tempo dovete riaprirlo a mano. Potete però decidere per quanto tenerlo aperto con questo comando:
$ ssh-add -t 1h30m
per esempio così rimane aperto per un’ora e mezza. Ricordatevi comunque che è bene che il portachiavi si chiuda dopo un po’.
Prima dell’apertura del portachiavi questa è la risposta del server Git da cui voglio scaricare gli aggiornamenti:
$ git pull origin master
sign_and_send_pubkey: signing failed for RSA "/home/marcob/.ssh/id_rsa" from agent: agent refused operation
git@bitbucket.org: Permission denied (publickey).
fatal: Impossibile leggere dal repository remoto.
Assicurati di disporre dei privilegi d'accesso corretti
e che il repository esista.
Quindi riapro il portachiavi:
$ eval `ssh-agent -s`
Agent pid 170012
Ora se provo a ridare il comando non avrò più l’errore ma mi verrà chesta la passphrase della mia chiave privata:
$ git pull origin master
Enter passphrase for key '/home/marcob/.ssh/id_rsa':
Se voglio anche non dover ridigitare la password, aggiungo la mia chiave privata al portachiavi (devo però digitare la passphrase almeno questa volta)
$ ssh-add
Enter passphrase for /home/marcob/.ssh/id_rsa:
Identity added: /home/marcob/.ssh/id_rsa (marcob@jsbach)
Per le successive invocazioni con il Git server remoto non mi verrà più chiesta la password (fino allo scadere del timeout del portachiavi):
$ git pull origin master
Da bitbucket.org:mxaos/logisticmapper
* branch master -> FETCH_HEAD
Già aggiornato.
Routes: credo che il modo più semplice di imparare come funziona il framework MVC Laravel sia quello di partire con le rotte. Rotte proprio come le linee ideali seguite da una nave.
Almeno questa è la mia esperienza.
Cosa sono le rotte (routes)?
Cosa sono le rotte? In inglese routes, sono gli URI (Uniform Resource Identifier), i link che vengono generati dall’applicazione, quelli che compaiono sulla barra degli indirizzi del browser:
Routes: sono gli URI nella barra degli indirizzi
Il principale URI generato dall’applicazione è la home page, nel mio esempio:
http://www.complex.local/
È un’applicazione di esempio locale per la quale ho configurato un virtual host Apache.
Per vedere come viene servita questa rotta (che in sostanza è risorsa di più alto livello, la radice e si indica con /) bisogna fare riferimento al file <APP>/routes/web.php.
Attenzione: sto usando la versione 8.73.1 di Laravel e, rispetto al passato, l’organizzazione delle cartelle è leggermente cambiata. Con <APP> intendo la cartella radice dell’applicazione (nel mio caso il nome della cartella è /complex. Al suo interno c’è tutta l’organizzazione delle cartelle di Laravel, tra cui una che si chiama /app (che non è <APP>, bensì la prima sottocartella di questa, in ordine alfabetico). Nella cartella /app c’è la maggior parte del materiale che utilizzeremo (i model e i controller per esempio). Un’altra cartella importante è la cartella /database dove troveremo tutte le migrazioni. Infine , nella cartella /resources/views ci sono le viste e, nella separata cartella /routes, le rotte.
Ebbene, ecco il contenuto del file <APP>/routes/web.php:
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Questo programma utilizza la classe di Laravel Route e in particolare il suo metodo get(), che è definito con una closure che non è altro che una funzione definita sul posto, detta più propriamente funzione anonima perché è senza nome. Questa closure ritorna la funzione view() a cui passo l’argomento 'welcome'.
Nota: view() è una funzione, non un metodo. Infatti non è definita all’interno di una classe ma all’interno di un file, helpers.php, situato sotto <APP>/vendor/laravel/framework/src/Illuminate/Foundation.
Il primo importante parametro del metodo get() è proprio la rotta da servire. In questo caso la ‘/’, la home page.
Il secondo parametro altrettanto importante è cosa deve fare Laravel quando dal browser arriva la richiesta di servire la rotta indicata. In questo caso ciò che deve fare Laravel è scritto dentro alla closure, e deve ritornare la view di nome ‘welcome’.
Cos’è questa view 'welcome'? È un file PHP scritto in Blade che si trova sotto <APP>/resources/views/welcome.blade.php. Quindi ogni volta che di decide il nome di una nuova view deve essere necessariamente associata ad un nuovo file all’interno della cartella <APP>/resources/views/.
Per esempio, se dobbiamo servire una rotta return view('edit'); ci dovrà essere un form HTML scritto dentro al file <APP>/resources/views/edit.blade.php.
Quindi in generale:
Route::get([URL], function () {
return view([view file name]);
});
Ora, la view di home generata automaticamente da Laravel è piuttosto complessa:
laravel 8.73.1 standard home
Facciamone una più semplice:
$ cd resources/views/
$ cp welcome.blade.php welcome.blade.ORIG.php
e modifichiamo a piacere l’orginale (welcome.blade.php):
Ho lasciato un po’ di “codice” html/css per fare vedere lo scheletro della pagina, come vengono caricati gli stili, come si usa il linguaggio di templatingBlade (es. str_replace()) – per vedere come si possano iniettare piccoli frammenti di codice per automatizzare la pagina – e Bootstrap con la definizione delle classi.
Ma proprio il minimo indispensabile.
Il cuore della view è:
<h1>Buongiorno Laravel!</h1>
<p>Questa è la home page</p>
Il risultato è un po’ deludente ma siamo risuciti a servire la nostra prima rotta
laravel home modificata
Routes: il metodo statico Route::get()
Ho glissato su un aspetto tecnico piuttosto importante, il frammento di programma che chiama la view è scritto così:
Se non definisco una rotta, non la posso chiamare dal browser. Per esempio se provo ad invocare
http://www.complex.local/esempio
il risultato è un errore 404
Route errore 404
Se però definisco la rotta:
Route::get('/esempio', function () {
return 'view di esempio';
});
la vista che appare è la seguente
Route: view di esempio
Possiamo anche passare parametri nell’URL e intercettarli all’interno dei nostri programmi per farne ciò che vogliamo. Ad esempio se definiamo questa rotta nel file /routes/web.php:
Route::get('/company/{id}', function ($id) {
return 'Company '.$id;
});
Otteniamo
Route per passare un parametro
Posso passare anche più parametri nel modo seguente (attenzione all’ordine in cui li scrivo e li nomino!):
Route::get('/company/{id}/{type_id}', function ($id, $type_id) {
return 'Company '.$id. ' is of type '.$type_id;
});
Il risultato è il seguente
Route per passare più parametri
Questo esempio che segue illustra la possibilità di dare un nome, una etichetta, ad una rotta.
Route::get('/user/example', array('as' => 'user.home', function() {
$uri = route('user.home');
print 'The URI is '.$uri;
}));
Il risultato è questo che per ora ci dice poco:
Route name
Tutte le rotte che definiamo le possiamo tenere sotto controllo con artisan:
Route: lista delle rotte
L’ultima lista dell’elenco è quella a cui abbiamo anche attriibuto un nome. In pratica lo potremo usare così:
<a href="route('user.home')">User home</a>
e laravel ci porterà a http://www.complex.local/user/example.
Ricordatevi di fare in modo che apache possa scrivere il file <APP>/storage/logs/laravel.log:
$ sudo chmod 777 storage/logs/laravel.log
[sudo] password di marcob:
$
o risulterà un errore.
Ulteriori modi di utilizzare le rotte è chiamando i metodi di un controller invece di una funzione anonima. Ma lo vediamo la prossima puntata.
Laravel primi passi: ispirandomi alle lezioni di Edwin Diaz ripropongo qui un approccio smooth.
Laravel è un framework per applicazioni scritte PHP che implementa un’architettura MVC.
Framework sta per infrastruttura software fatta di file, classi, metodi e proprietà, che aiutano a sviluppare un’applicazione.
È in se stessa un’applicazione, ma sapendola configurare e personalizzare, con essa possiamo fare ciò vogliamo.
Molte operazioni saranno quasi gratis, come le operazioni su un database, ma occorrerà comunque scrivere parecchio per realizzare ciò che vogliamo. Il fatto è che utiizzando il framework siamo costretti a sviluppare in modo ordinato e coerente e possiamo tenere sotto controllo gli errori, testare il software e documentarlo.
Un altro aspetto positivo dell’adozione di un paradigma MVC standard è che potrai essere affiancato da altri programmatori che non dovranno imparare come tu scrivi il software, che è il peggior problema quando si mettono le mani in codice scritto da altri: ognuno ha il suo stile e si fa il suo piccolo framework.
Anche qui ci sarà una curva di apprendimento ma lo standard è molto più robusto, scalabile, testato e documentato del piccolo framework personale
Laravel: architettura MVC
L’arhitettura MVC è un design pattern, cioè un paradigma progettuale, un modo di progettare, in cui il funzionamento di un software viene pensato come composto da tre componenti:
M per Model: il modello è la rappresentazione dei dati all’interno dell’applicazione. I dati risiedono in un database ma, nel contesto di una programmmazione ad oggetti, anche loro all’interno del software, devono venire rappresentati come degli oggetti del linguaggio che si sta usando. Per questo il modello in sostanza è implementato da un’architettura che si chiama ORM, Object Relationship Manager, ed è la parte di Laravel che si occupa di interagire con il database, come dice il nome, mappando il database sugli oggetti PHP. Nella stragrande maggioranza dei casi si può dire che ad ogni tabella del database corrisponde una classe del modello. ORM ce ne sono tanti, ogni linguaggio ha il suo. Per esempio un ORM di Java è Hibernate. L’ORM di PHP adottato da Laravel si chiama Eloquent. Con Eloquent possiamo fare le più semplici interrogazioni e query con join senza ricorrere alla scrittura diretta di istruzioni SQL. Possiamo anche creare e distruggere oggetti database senza ricorre al DDL (il Data Definition Language, per intederci le istruzioni di tipo CREATE TABLE) del DBMS . È comunque abbastanza flessibile da consentire anche di scrivere direttamente in SQL query troppo complicate rimanendo nel framework.
V per View: è la parte visuale dell’applicazione, l’html prodotto dall’applicazione e la gestione dell’interazione con il browser. Il fatto che l’interfaccia debba essere sollevata da interazioni dirette con il database dovrebbe essere chiara a chiunque abbia sperimentato un po’ di programazione old style che mescolava dentro ogni singola pagina: HTML, CSS, PHP, SQL, Javascript… Un vero incubo! Anche per le view esistono plugin per semplificare ulteriormente la produzione: per esempio il framework Blade consente di progettare pagine che acquisiscono dati dal Model attraverso il Controller e fanno operazioni di tipo ciclo o test o semplicemente output delle variabili nell’HTML. Utilizzando anche il tool HTML/CSS Bootstrap possiamo progettare in modo rapido e responsive le nostre pagine senza impazzire con il CSS al cambiare della UI.
C per Controller: devo dire che per me è stato il componente più difficile da imparare ma quando ho capito cos’è, si è semplificato tutto di brutto. Viene definito come il man in the middle, e un po’ di verità c’è. Ma la sostanza è che il controller è l’applicazione. È in assoluto la parte più importante del pattern MVC, quella in cui scarichiamo la maggior parte della nostra creatività. Ciò che vogliamo che l’applicazione faccia, lo definiamo nel controller. È il regista che prende gli attori (model) e li fa recitare nelle scene del set (view). Quindi la maggior parte del codice che scriveremo sarà dentro al controller.
Utilizziamo tecnologie come i cookie per memorizzare e/o accedere alle informazioni del dispositivo. Lo facciamo per migliorare l'esperienza di navigazione e per mostrare annunci personalizzati. Il consenso a queste tecnologie ci consentirà di elaborare dati quali il comportamento di navigazione o gli ID univoci su questo sito. Il mancato consenso o la revoca del consenso possono influire negativamente su alcune caratteristiche e funzioni.
Funzionale
Sempre attivo
L'archiviazione tecnica o l'accesso sono strettamente necessari al fine legittimo di consentire l'uso di un servizio specifico esplicitamente richiesto dall'abbonato o dall'utente, o al solo scopo di effettuare la trasmissione di una comunicazione su una rete di comunicazione elettronica.
Preferenze
L'archiviazione tecnica o l'accesso sono necessari per lo scopo legittimo di memorizzare le preferenze che non sono richieste dall'abbonato o dall'utente.
Statistiche
L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici.L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici anonimi. Senza un mandato di comparizione, una conformità volontaria da parte del vostro Fornitore di Servizi Internet, o ulteriori registrazioni da parte di terzi, le informazioni memorizzate o recuperate per questo scopo da sole non possono di solito essere utilizzate per l'identificazione.
Marketing
L'archiviazione tecnica o l'accesso sono necessari per creare profili di utenti per inviare pubblicità, o per tracciare l'utente su un sito web o su diversi siti web per scopi di marketing simili.
Commenti recenti