domenica 25 novembre 2018

Riempire il Database di dati fake tramite Seeder

Una volta creata la nostra struttura. sarebbe comodo poter avere dei dati su cui lavorare o in generale un automatismo per poter riempire le tabelle in gioco.

Per farlo si usano i Seed, delle classi fatte appossa che utilizzano, come vedremo, la Factory Faker.

Per farlo occorre creare il Seed per la tabella che vogliamo riempire.

php artisan make:seed SeedUserTable

Laravel fornisce già una Factory per la tabella USER che si trova in ModelFactory.php dentro factories. Sarà qui che creeremo le nostre factory.

Dato che quella per User esiste già, ci basta aprire il file SeedUserTable che abbiamo creato e implementare il metodo:


 public function run()
 {
     DB::statement("SET FOREIGN_KEY_CHECKS=0;");
     User::truncate();
     factory(App\User::class,30)->create(); //create and save on DB
     $users = factory(App\User::class,30)->make(); //create but not save on DB
 }

Come si vede, occorre per prima cosa disabilitare il check sulle foreign key altrimenti la truncate non verrebbe eseguita.
Poi richiamiamo la factory con lo stesso nome che trovate nel ModelFactory ovvero App\User::class

Diciamo quindi di creare 30 utenti.
L'istruzione successiva, non necessaria, serve solo a mostrare come sia possibile creare utenti al volo senza persisterli su DB tramite il metodo make. Tale sistema riempie la variabile users che potebbe essere usata per altri scopi.

A questo punto è possibile lanciare la singola Seed per User o inserirla all'interno del seed principale DatabaseSeeder.php che sarà lanciata quando si lancia il seed senza parametri.


public function run()
{
   $this->call(SeedUserTable::class);
}

Se si vuole lanciare solo il seeder creato, basta usare il comando:

php artisan db:seed --class=SeedUserTable

Altrimenti se si ometterà il parametro class, verrà lanciato il DatabaseSeeder e tutti i seeder che avrete configurato dentro il metodo run.

Gestione Database - Parte 1

Anche Laravel gestisce il database tramite ORM e migrazioni.

Quando installate Laravel da Composer, drovrete lanciare la prima migrazione che creerà la tabella users e reset password.

Per impostare i dati di connessione al database, dovete aprire il file database.php dentro config.

A questo punto da linea di comando, lanciate:

php artisan:migrate


A questo punto potete creare le vostre tabelle su database e creare la migrazione sulla base di esse. Per farlo, una volta creata la tabella, sempre da linea di comando lanciate:

php artisan make:migration -h

per avere un elenco dei comandi.

A questo punto lanciate il comando seguente per creare una migrazione per una ipotetica tabella people.

php artisan make:migration create_people_table --create people

Laravel creerà una migrazione tendenzialmente vuota con solo l'ID auto-incremento ed un timestamp.

A questo punto potete aggiungere le vostre colonne nella migrazione seguendo questa guida:

https://laravel.com/docs/5.7/migrations#creating-tables

Dove sono indicate le tipologie di colonne che solitamente sono 1:1 con i nomi di mysql.

IMPORTANTE: La migrazione base create da Laravel, inserisce la tipologia timestamps che creerà di default due colonne timestamp chiamate created_at e updated_at, utili a tenere traccia delle modifiche (Laravel lo farà automaticamente quando useremo i suoi ORM).
Se dovete tenere traccia anche delle cancellazioni logiche, allora dovete inserire:

$table->softDeletes();

Per creare foreign key su altre tabelle, basta creare la colonna adibita normalmente e poi assegnarvi una foreign key.



 Schema::create('people_power', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned();
            $table->foreign('user_id')->on('users')->references('id')
                  ->onDelete('cascade')->onUpdate('cascade');
            $table->integer('power_id')->unsigned();
            $table->foreign('power_id')->on('power')->references('id')
                  ->onDelete('cascade')->onUpdate('cascade');
            $table->timestamps();
        });

Nell'esempio avevamo creato precedentemente una tabella Power tramite migrazione ed abbiamo creato questa tabella aggiuntiva per avere una relazione N:N tra Power e People.
Come si vede, abbiamo creato prima le due colonne di collegamento e su ognuna abbiamo applicato la foreign key.

Modifica

Per modificare una tabella per aggiungere colonne, togliere colonne, cambiare le specifiche di una colonna, occorre creare una migrazione apposita.
Questa volta però senza l'opzione create ma tramite l'opzione table.

php artisan make:migration update_table_people_name --table people


Questo creerà una migrazione vuota.
A questo punto basterà riempire i due metodi up e down per specificare l'entità della modifica e cosa fare in caso di rollback.
Come si evince, in questa migrazione stiamo cambiando la dimensione di una colonna che già esisteva (name) e stiamo aggiungendo 6 nuove colonne utilizzando la stessa sintassi che si usa quando si creano.

Nella down invece stiamo rimettendo la dimensione della colonna name a quella originale e stiamo droppando le colonne che avevamo aggiunto.
ATTENZIONE: Se non droppato le colonne, queste rimarranno su DB ed una successiva migrazione dello stesso file, vi darà errore perché Laravel tenterebbe di creare le sei nuove colonne che però sono già su DB.


 public function up()
    {
        Schema::table('people', function (Blueprint $table) {
            $table->string('name',300)->change();
            $table->tinyInteger('strength');
            $table->tinyInteger('dexterity');
            $table->tinyInteger('constitution');
            $table->tinyInteger('inteligence');
            $table->tinyInteger('wisdom');
            $table->tinyInteger('charisma');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('people', function (Blueprint $table) {
            $table->string('name',250)->change();
            $table->dropColumn('strength');
            $table->dropColumn('dexterity');
            $table->dropColumn('constitution');
            $table->dropColumn('inteligence');
            $table->dropColumn('wisdom');
            $table->dropColumn('charisma');
        });
    }

domenica 18 novembre 2018

I cicli in Blade

Ciclare è importante e necessario. Blade ovviamente mappa le istruzioni di PHP ma permette di avere qualche utile hook incluso nel ciclo per fare della logica.

Nel codice sottostante è interessante notare l'uso della variabile automatica $loop che contiene info sul ciclo come l'attuale iterazione, quanti elementi mancano, quanto è lungo il ciclo ecc... Nei cicli annidati è anche possibile avere a disposizione la profondità attuale ($loop->depth) e accedere al ciclo padre con ($loop->parent)

E' possibile inserire il continue addirittura impostanto la condizione direttamente nell'istruzione mentre con forelse è possibile gestire velocemente i casi in cui occorre stampare qualcosa quando la lista è vuota

 @foreach ($list as $element)
   @php $color = "black" @endphp
   @if ($loop->first)
      @php $color = "#990000" @endphp
   @endif

   @if ($loop->last)
      @php $color = "#000099" @endphp
   @endif

   @continue ($element == "fifth")
   <li style = "color : {{ $color }} "> 
     {{ $element }} ( {{$loop->iteration}} / {{$loop->count}} / {{$loop->remaining}} )
   </li>

 @endforeach

 @forelse ($nolist as $noelement)
     {{ $noelement }}
  @empty
     <p> No Element </p>
 @endforelse

Estendere sezioni di template

Nel post precedente abbiamo visto come creare un template base ed estenderne il contenuto con una vista parziale.
Si usa la direttiva @yield per creare un punto di aggancio tra il template e la vista.
Tuttavia è frequente la casistica che vuole il template base avere una sezione già con contenuto, per esempio il footer, a cui la vista parziale deve solo aggiungere qualcosa senza però sovrascrivere tutto il contenuto del pezzo di template.

Per fare questo nel template base NON si usa più @yield ma si usa proprio una sezione @section come quando siamo nelle viste parziali.

<?php
<footer class="page-footer" style="background-color: black; color: white; padding-left:300px">
        <div class="container">
            <div class="row">
                    @section('footer')
                        Sono il footer - Realizzato da me 2018
                    @show
            </div>
        </div>
        <div class="footer-copyright">
            <div class="container">
            © 2018 Qualcuno                    
            </div>
        </div>
        <script src="https://code.getmdl.io/1.3.0/material.min.js"></script>

</footer>
?>

Nel footer creiamo una sezione che rappresenta il punto in cui possiamo far agganciare il pezzo della vista parziale. Qui nell'esempio la sezione "sensibile" è molto piccola perché racchiusa in un DIV ma è possibile farla anche più grande. Dovete solo considerare che quello che mettete viene aggiunto in coda.
IMPORTANTE: E' buona norma mettere il JAVASCRIPT in fondo alla pagina quindi conviene creare nel footer una sezione chiamata javascript dove le viste parziali possono aggiungere il loro codice javascript o includere js esterni.

Nella nostra vista parziale basta quindi creare la section come se dovessimo farla mettere in uno @yield ma preoccupandoci di mettere @parent per fare in modo di ereditare il pezzo del padre altrimenti il nostro lo sovrascriverebbe come se ci fosse @yield.

@section('footer')
   @parent
   - Sono una aggiunta
@stop

Il risultato finale della scritta nel footer sar: Sono il footer - Realizzato da me '2018 - Sono una aggiunta

Ereditarietà dei template

E' normale per tutti gli MVC avere un sistema di viste parziali ovvero di template ripetuti dove le pagine delle view vengono immesse come parte di essa.
E' il metodo più comodo per avere parti di template uguali per tutte le pagine.

Il LARAVEL otteniamo questo usando la direttiva @yield

Creiamo quindi un template base. Per esempio prendentene uno da Bootstrap o Material.
Nella directory delle view, create una directory templates e create un file tipo base.balde.layout

Copiate il template che avete prelevato da internet.
Andate nella sezione del title e mettete la seguente direttiva:

<title> {{ $title }} </title>
<title> @yield("title","Admin Basic Page") </title>

Come vedete ho messo due modi per poter rendere il title del sito dinamico.
Il primo preleva direttamente la variabile passata alla view nella maniera vista nel post precedente.
La seconda invece imposta un placeholder che di default sarà uguale a "Admin Basic Page" ma se chi estenderà questo template lo imposta, userà quello.
Andiamo poi nel body della nostra pagina template o comunque nel punto dove vogliamo far agganciare il contenuto di CHI estenderà questo layout. Mettiamo un altro placeholder chiamandolo content
<main class="mdl-layout__content mdl-color--grey-100">
   <div class="mdl-grid demo-content">
       @yield('content')
   </div>
</main>

A questo punto la nostra vista index che avevamo fatto prima, diventerà una "vista parziale" perché di fatto aggiunge solo contenuto ad una vista più grande.

@extends('templates.adminlayout')

@section('title',$title)

@section('content')
    @if ($title)

        <h1> {{ $title }} </h1>
        @if ($subtitle)
            <h2> {{ $subtitle }}
        @endif

    @endif
@endsection
//@stop si può usare anche stop invece che endsection


Come vedete abbiamo prima detto che questa view estende quella principale.
Poi abbiamo definito una sezione atomica con il titolo del sito ed una sezione più corposa nella quale abbiamo messo il codice che avevamo già.

Template Blade

Laravel supporta il templating con BLADE quindi lo farete anche voi.


E' un modo più comodo ed elegante di scrivere il codice delle pagine.
Di base possiamo riassumere le operazioni di blade con questi assiomi:
  • Il contenuto di una variabile si stampa racchiudendola nella doppia graffa senza echo/print;
  • Il codice HTML si scrive puro senza escape o roba strana;
  • Si usano generalmente le funzioni di PHP come IF, FOREACH ecc... ma preceduti da ghiocciola;
  • Non si usano le graffe per aprire e chiudere le funzioni ma si utilizza la sintassi endfunzione;

<?php
<html>
    <head>
        <title> {{ $title }} </title>
    </head>
    <body>

        @if ($title)

            <h1> {{ $title }} </h1>
            @if ($subtitle)
                <h2> {{ $subtitle }}
            @endif

        @endif

    </body>
</html>
?>

Passare dati alla view

Passare dati alla view è la cosa fondamentale per poterli stampare a video.
Esistono tre metodi per farlo scritti in sequenza qua sotto:
  • Passare direttamente un array: E' il metodo migliore e più classico;
  • Tramite il metodo with: Utile per passare al massimo un paio di parametri;
  • Tramite metodo magico withLabel: Laravel converte direttamente la parte dopo With come chiave del valore

<?php
public function index (Request $req)
{
        $data_to_view = [ 
                             "title"     => "ADMIN PAGE",
                             "subtitle"  => "Just a Page"
                         ];
        return view('admin/index', $data_to_view);
        return view('admin/index')
                ->with("title","ADMIN PAGE")
                ->with("subtitle","Just a Page");
        return view('admin/index')
                ->withTitle("ADMIN PAGE")
                ->withSubtitle("Just a Page");
}
?>

sabato 17 novembre 2018

Mappare gruppi di rotte

Avere un solo file per tutte le rotte non è bello e conviene sempre organizzarle in blocchi separati semanticamente.
Per esempio potreste voler separare tutte le rotte di tipo /admin rispetto a quelle del sito base.

Per farlo si crea nella directory delle routes, un nuovo file chiamato, per esempio, admin.php

Al suo interno si registrano le rotte come abbiamo fatto nel post precedente considerando come assodato che le URL saranno del tipo www.miosito.com/admin/. In pratica NON dovete specificare /admin.
Aprite quindi il file RouteServiceProvider.php dentro la directory provider ed aggiungete il nuovo blocco di route come metodo alla classe.
Fate copia-incolla di quello web e cambiate il prefisso e il nome del file delle rotte.
Aggiungete il metodo al metodo map siete a posto.


<?php
    protected function mapAdminRoutes()
    {
        Route::prefix('admin')
             ->namespace($this->namespace)
             ->group(base_path('routes/admin.php'));
    }
?>


    public function map()
    {
        $this->mapApiRoutes();
        $this->mapAdminRoutes();
        $this->mapWebRoutes();
        //
    }

Rotte

Le rotte si trovano nella directory Routes e sono divise per tipologia.
Quelle per il Web sono in web.php

Di base una rotta è:

<?php
Route::get('/', function () {
    return view('welcome');
});
?>

senza parametri nell'URL richiama la vista welcome.
Per comporre URL con multi parametri, si usa la sintassi:

<?php
Route::get('/{name?}/{lastname?}', function ($name = "", $lastname = "") {
    return "<h1>Ciao ".$name." ".$lastname;
});
?>

Il punto di domanda indica che il parametro è opzionale quindi serve impostare il valore di default nella function.

Per creare filtri sul tipo di parametro, si usa il where ed una espressione regolare. Nell'esempio si può imporre che i due parametri siano solo testo.

<?php
Route::get('/{name?}/{lastname?}', function ($name = "", $lastname = "") {
    return "<h1>Ciao ".$name." ".$lastname;
})->where("name","[a-zA-Z]+")->where("lastname","[a-zA-Z]+");
?>

E' possibile passare alla where un array di espressioni regolari:
<?php
Route::get('/{name?}/{lastname?}/{age?}', function ($name = "", $lastname = "", $age = 0) {
    return "<h1>Ciao ".$name." ".$lastname." di ".$age." anni";
})->where([
            "name"      => "[a-zA-Z]+",
            "lastname"  => "[a-zA-Z]+",
            "age"       => "[0-9]{1,3}"
          ]);

?>


Avere tante rotte con logica è sconveniente perché il file potrebbe crescere in complessità quindi conviene passare direttamente all'uso dei controller. Per creare un controller potete fare a mano oppure usare il comando:

php artisan make:controller LoginController

Aggiungiamo un metodo login:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class LoginController extends Controller
{
    //
    public function login()
    {
        return "Sono il login";
    }
}

e nella route creiamo la rotta di default che punta a tale metodo del controller:

<?php
Route::get('/', "LoginController@login");
?>

E' buona cosa farsi sempre dare la Request nel controller, così da averlo a disposizione nel caso ci serva per della logica. Per farlo basa mettere:


public function login(Request $req)


GET /isigym/public/ HTTP/1.1
Accept:                    text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding:           gzip, deflate, br
Accept-Language:           it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control:             max-age=0
Connection:                keep-alive
Cookie:                    admin_auth=eyJpdiI6ImNxWkQxemFkdXJjZjROVlIzbFFlV0E9PSIsInZhbHVlIjoiNEc3SWV0ZmVTV0FqaHhcL2RYNHZrWGR5RGZ0N3c4Z3VISHFHeHk0QmNxTkNZQXo5dEhYUjU1RFNLVzQ2ZzlxWnBIVTJRcVhManNLZksrd2hZc25NNXkzM2k2d2NJc3dCaHAwRnVSZUtxUmtiRXJzdXErYTJmdWoxcjJNcHB4NU5kIiwibWFjIjoiZmU5OTU0YzQzMTgwMTdmOGUyOTlhMjc0ZDNkOWE2ZGFiN2M5YmFlM2M3ODQ0NzNiZTU3OGUzODdkZmEwNDk5MCJ9; XSRF-TOKEN=eyJpdiI6IkJMQ3NaOE9Qais2MkRWR1J0SWFhT1E9PSIsInZhbHVlIjoiTVZSNVU1RW96U2dhOGpLZGh6cDhnYWMrbjd6SGh1ZXZ0TExyWnowVWhkc1BjNXNRdUpHS2dUM3FPSFZNWFRwb0w0d3RUbjM0MzdoOGlVRVUzQ0RKalE9PSIsIm1hYyI6ImVkM2MzY2JlYjY4YjQ2NThkMjZkZjU2ZDZjOTZiMTc1ODc1ZjdjYTczMGMyN2VmZTQyMjBiM2EzZDdkZGM0YTUifQ%3D%3D; laravel_session=eyJpdiI6InFjTzlhQVZEcUZHXC9tMkZ2Y1ZVc1hRPT0iLCJ2YWx1ZSI6IndMMFl6eDBqcHJpeFlabzByb0I1ZFB4TndPQlduOGFES29UdFNDM1pQRWQySTNJXC9kNUR1cmxqRUdkOWVTUFdtbk03a1BDUlJpRDFuREJlTUNcL3BrZlE9PSIsIm1hYyI6ImY0MWRlMzM4NjQyZTIzMmM1YmU5YWQ0MDA5MDg4M2MzYzY0NTZmZDMwZjIwNzFkOGI5NTI3YWQxY2U4OGJjYjMifQ%3D%3D
Host:                      localhost
Referer:                   http://localhost/isigym/
Upgrade-Insecure-Requests: 1
User-Agent:                Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36
Cookie: admin_auth=; XSRF-TOKEN=GMaKfvlv6SnV7prIixE25tWIaZf8U7m5rTaS9knX; laravel_session=OAPI84csT9lfBS0mKiu698ohwXFb8zoVTo7Zr9G9

Analogamente a sopra, possiamo passare parametri di URL al controller semplicemente usando la sintassi sopra per la route e censendo il parametro nel metodo del controller


Route::get('/{username?}', "LoginController@login");



public function login($username = "", Request $req)
    {
        return "Sono il login di ".$username;
    }



I parametri in GET che possono cambiare, non vanno censiti nella rotta ma vengono letti direttamente dal metodo del controller grazie proprio alla Request.
<?php
public function login($username = "", Request $req)
    {
        $lang = $req->input("lang");
        return "Sono il login di ".$username." in lingua ".$lang;
    }
?>

http://localhost/site/public/Emiliano?lang=it

dsda