Laravel

Page last edited 488 giorni 18 ore fa
Da wiki.betaingegneria.it.

Laravel[modifica]

Laravel è un framework simile a [Yii] per lo sviluppo agile di applicazioni PHP.

Qui di seguito appunti presi pari pari dal sito.

Installazione[modifica]

Requisiti di Sistema[modifica]

  • PHP >= 5.5.9
  • OpenSSL PHP Extension
  • PDO PHP Extension
  • Mbstring PHP Extension
  • Tokenizer PHP Extension

Installare Laravel[modifica]

Usando Laravel Installer

Primo, scaricare l'installer di Laravel utilizzando Composer

  $ composer global require "laravel/installer"

Mettete poi il percorso a laravel nel vostro PATH (ad esempio, scrivete la seguente istruzione dentro al file .bashrc):

  export PATH=$PATH:~/.composer/vendor/bin

Una volta installato laravel, creare lo scheletro di una nuova applicazione è semplicissimo (i problemi vengono dopo):

  $ cd /var/www/html
  $ laravel new blog

creerà una nuova directory blog che contiene una applicazione PHP nuova di zecca con tutte le dipendenze di Laravel già installate.

Configurazione[modifica]

Tutti i file di configurazione del framework Laravel si trovano nella directory config. Ogni opzione è documentata, quindi farete bene a sfogliarvi questa directory.

Permessi alle Directory[modifica]

Dopo l'installazione potreste aver bisogno di configurare alcuni permessi. Le sottodirectory delle cartelle:

  storage/
  bootstrap/cache/

devono essere scrivibili da Apache, o Laravel non funzionerà. IO nel mio sistema ho diritti 775 nella cartella blog per cui a queste due cartelle basta cambiare il gruppo:

  $ sudo chgrp www-data -R storage/ bootstrap/cache/

Se state usando la macchina virtuale Homestead, questi permessi sono già dati.

Chiave Applicativa (Application Key)[modifica]

Il prossimo passo è impostare la vostra application key con una stringa casuale. Se avete usato il Laravel installer, questa chiave è già impostata dal comando PHP

  artisan key:generate

Tipicamente questa stringa è di 32 caratteri. il valore di questa chiave può essere salvato nel file .env. Se non avete ancora rinominato il file

  .env 

in

  .env.example

è giunto il momento di farlo.

Se non impostate la chiave applicativa, la vostra sessione utente e altri data cifrati non saranno sicuri!

Altre configurazioni[modifica]

A Laravel non servono altre configurazioni appena scartato dalla confezione. Potete iniziare a sviluppare! In ogni caso , potreste voler dare un occhio al file

  config/app.php

e alla sua documentazione. Contiene opzioni come timezone e locale che potreste voler impostare per la vostra applicazione. Potreste anche impostare alcune altre cose come


  • Cache
  • Database
  • Session

Dopo aver installato Laravel dovreste anche configurare il vostro ambiente locale.

Installare una nuova applicazione web Laravel[modifica]

Per installare una nuova applicazione (che io chiamo organizer2) web Laravel, ci si posiziona nel punto della docroot di apache in cui vogliamo che sia creata:

   $ cd /var/www/html

e poi lanciamo il comando

   $ composer create-project laravel/laravel organizer2 --prefer-dist

Attenzione![modifica]

Questo comando va lanciato se non abbiamo l'applicazione laravel/installer già installata.

Se si segue questa guida, invece, il comando ce l'abbiamo perché abbiamo descritto sopra come installare l'installer laravel in modo globale con composer:

  $ composer require global "laravel/installer"
  • il comando require ha questo significato [1]

The require command adds new packages to the composer.json file from the current directory. If no file exists one will be created on the fly.

Nel mio caso il package è stato aggiunto al file global che si trova in /home/marcob/.composer/composer.json

   ~/.composer$ cat composer.json 
   {
       "require": {
           "laravel/installer": "^1.3",
           "symfony/event-dispatcher": "^3.0",
           "psr/log": "^1.0",
           "symfony/dependency-injection": "^3.0"
       }
   }


  • il comando global [2] di composer ha questo significato:

The global command allows you to run other commands like install, require or update as if you were running them from the COMPOSER_HOME directory.

Se abbiamo già l'installer laravel a disposizione è sufficiente questo comando più breve

   $ laravel new organizer2

Cos'è successo?

   $ cd organizer2
   $ ll
   totale 200
   drwxrwxr-x 12 marcob   marcob   4096 mar 10 09:21 ./
   drwxrwxr-x 29 www-data marcob   4096 mar  7 15:33 ../
   drwxrwxr-x 10 marcob   marcob   4096 mar  7 15:33 app/
   -rw-rw-r--  1 marcob   marcob   1646 mar  7 15:33 artisan
   drwxrwxr-x  3 marcob   marcob   4096 mar  7 15:33 bootstrap/
   -rw-rw-r--  1 marcob   marcob   1259 mar 10 09:21 composer.json
   -rw-rw-r--  1 marcob   marcob 108026 mar  7 15:33 composer.lock
   drwxrwxr-x  2 marcob   marcob   4096 mar  7 15:39 config/
   drwxrwxr-x  5 marcob   marcob   4096 mar  7 15:33 database/
   -rw-rw-r--  1 marcob   marcob    419 mar  7 15:33 .env
   -rw-rw-r--  1 marcob   marcob    403 mar  7 15:33 .env.example
   drwxrwxr-x  7 marcob   marcob   4096 mar  7 16:15 .git/
   -rw-rw-r--  1 marcob   marcob     61 mar  7 15:33 .gitattributes
   -rw-rw-r--  1 marcob   marcob     79 mar  7 16:15 .gitignore
   -rw-rw-r--  1 marcob   marcob    503 mar  7 15:33 gulpfile.js
   -rw-rw-r--  1 marcob   marcob    159 mar  7 15:33 package.json
   -rw-rw-r--  1 marcob   marcob    870 mar  7 15:33 phpunit.xml
   drwxrwxr-x  2 marcob   marcob   4096 mar  7 15:33 public/
   -rw-rw-r--  1 marcob   marcob   1918 mar  7 15:33 readme.md
   drwxrwxr-x  5 marcob   marcob   4096 mar  7 15:33 resources/
   -rw-rw-r--  1 marcob   marcob    567 mar  7 15:33 server.php
   drwxrwxr-x  5 marcob   marcob   4096 mar  7 15:33 storage/
   drwxrwxr-x  2 marcob   marcob   4096 mar  7 15:33 tests/
   drwxrwxr-x 28 marcob   marcob   4096 mar  7 15:33 vendor/

Giriamola un po' questa directory

vendor[modifica]

La directory vendor all'interno dell'applicazione contiene tutti i pacchetti di terze parti, e laravel è tra questi. In sostanza abbiamo un vero e proprio applicativo da linea di comando che però va messo nel PATH per poter essere eseguito senza /usr/bin eccetera davanti. Cosa c'è dentro la diretory vendor della nuova applicazione blog appena creata?

   $ ll vendor/
   totale 116
   drwxrwxr-x 28 marcob marcob 4096 mar  7 15:33 ./
   drwxrwxr-x 12 marcob marcob 4096 mar 10 09:21 ../
   -rw-rw-r--  1 marcob marcob  183 mar  7 15:33 autoload.php
   drwxrwxr-x  2 marcob marcob 4096 mar  7 15:33 bin/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 classpreloader/
   drwxrwxr-x  2 marcob marcob 4096 mar  7 15:33 composer/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 dnoegel/
   drwxrwxr-x  4 marcob marcob 4096 mar  7 15:33 doctrine/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 fzaninotto/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 hamcrest/
   drwxrwxr-x  4 marcob marcob 4096 mar  7 15:33 jakub-onderka/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 jeremeamia/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 laravel/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 league/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 mockery/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 monolog/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 mtdowling/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 nesbot/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 nikic/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 paragonie/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 phpdocumentor/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 phpspec/
   drwxrwxr-x  9 marcob marcob 4096 mar  7 15:33 phpunit/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 psr/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 psy/
   drwxrwxr-x  9 marcob marcob 4096 mar  7 15:33 sebastian/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 swiftmailer/
   drwxrwxr-x 18 marcob marcob 4096 mar  7 15:33 symfony/
   drwxrwxr-x  3 marcob marcob 4096 mar  7 15:33 vlucas/

Quanta roba! Gli eseguibili sono sotto bin/:

   $ ll vendor/bin/
   totale 8
   drwxrwxr-x  2 marcob marcob 4096 mar  7 15:33 ./
   drwxrwxr-x 28 marcob marcob 4096 mar  7 15:33 ../
   lrwxrwxrwx  1 marcob marcob   33 mar  7 15:33 php-parse -> ../nikic/php-parser/bin/php-parse*
   lrwxrwxrwx  1 marcob marcob   26 mar  7 15:33 phpunit -> ../phpunit/phpunit/phpunit*
   lrwxrwxrwx  1 marcob marcob   22 mar  7 15:33 psysh -> ../psy/psysh/bin/psysh*

Questo per quanto riguarda la singla applicazione. Però laravel ci serve non per-project-basis ma globalmente. Per cui quando si installa composer, viene creata una cartella global a livello dell'utente che lancia l'installazione .composer:

   $ which laravel
   /home/marcob/.composer/vendor/bin/laravel

Quindi il comando laravel è disponibile globamente.

Logfile[modifica]

È utilissimo, sempre, leggere i file di errore per avere dettagli sugli errori. La posizione del logo di laravel è:

   /var/www/html/nomeapplicazione/storage/logs/laravel.log

Connessione al database[modifica]

Non basta compilare il file config/database.php.

Occorre anche modificare il file .env (attenzione che se non è abilitata la visibilità di tutti i file nel file explorer (tipo Nautilus) il file non si vede neanche...)

Cominciamo[modifica]

Con Yii eravamo partiti dal database. Stavolta invece partiamo dalla parte opposta: dagli URL. Definiamo prima le routes, le rotte:

Ho in sostanza tre entità nel mio db e le rotte le definisco a partire dalle entità: così costruiremo poco più di uno scaffold ma intanto partiamo:

   POST   /customers                       Handle creation of new customer
   GET    /customers/:id/edit           Form to edit existing customer page
   PUT    /customers/:id                Handle updates to customer page
   GET    /customers/:id/delete         Form to confirm deletion of customer
   DELETE /customers/:id                Handle deletion of customer page

Ora se scrivo nel browser

   http;//localhost/organizer2/customers/1

ho un errore HTTP 400. Il manuale mi dice di attivare il mod_rewrite di Apache. Subito:

Attivare il mod_rewrite[modifica]

   $ sudo a2enmod rewrite
   Enabling module rewrite.
   To activate the new configuration, you need to run:
     service apache2 restart
   $ service apache2 restart
   $

OK. Controllate che il mod_rewrite compaia tra i moduli di Apache 2, lanciando un phpinfo().

Definire un virtualhost[modifica]

Nella directory /etc/apache2/sites-available creare un file laravel.conf

  <VirtualHost *:80>
      DocumentRoot /var/www/html/organizer2/public/
      ServerName www.myfuckinglaravel.com
      Options Indexes FollowSymLinks
        Alias /organizer2/ /var/www/html/organizer2/public/
      <Directory "/var/www/html/organizer2/public/">
          Order Allow,Deny
          Allow from all
      </Directory>
  
        ErrorLog ${APACHE_LOG_DIR}/error_laravel.log
        CustomLog ${APACHE_LOG_DIR}/access_laravel.log combined
  </VirtualHost>

Note[modifica]

  • la document root è la sottocartella public,
   DocumentRoot /var/www/html/organizer2/public/

non la root del progetto laravel creato con composer;

  • è meglio definire un server name fittizio che non lavorare con localhost, è più facile:
   ServerName www.myfuckinglaravel.com

il nome è di fantasia per non collidere con altri nomi reperibili in Internet con DNS. Ad ogni buon conto si può usare anche un nome esistente (www.google.com) a patto di non andare a chiamare il DNS in quel caso:

  • editare il file /etc/hosts e agiungere una riga
   127.0.1.1       www.myfuckinglaravel.com

Alla fine riavviare Apache

   sudo apache2ctl restart

e si ha il risultato:

Errore nella creazione della miniatura: Impossibile salvare la miniatura nella destinazione

Routing[modifica]

La regoletta sembra semplice ma come al solito ci sono tanti di quei caveat che alla fine è un vespaio. Tutti scrivono facile facile: un cazzo!

Ad esempio: in questo sito c'è una guida su come costruire le rotte:

   Let’s open up our app/routes.php file and get our static pages (Home, About, and Work) routed to. These will be the easiest since they don’t really require 
   anything other than their view. No login, no parameters, just good ol’ displaying a page.
   // show a static view for the home page (app/views/home.blade.php)
   Route::get('/', function()
   {
       return View::make('home');
   });

Risultato? NON FUNZIONA UNA MAZZA. Le views nella mia installazione nno si trovano sotto app/views ma sotto app/resources/views come specificato anche nel file di configurazione

   <nomeapp>/config/view.php:
   'paths' => [
       realpath(base_path('resources/views')),
   ],

Routing 2[modifica]

Dopo un po' di tempo riprendo in mano l'argomento. Ho ancora problemi con le rotte. Sto facendo un'applicazione che si chiama cms.

Dentro la cartella WEBROOT/cms/app/Http ho un file routes.php con questo codice

  Route::get('/example', function () {
     return view('welcome');
  });

Ho definito un virtualhost Apache con una istruzione in /etc/apache2/sites-enabled/laravel.conf

   <VirtualHost *:80>
            DocumentRoot /var/www/html/cms/public
            ServerName cms.dev
            <Directory /var/www/publiczoo/public>     <--- big fail! sostituire con /var/www/html/cms/public
                    Options Indexes FollowSymLinks
                    AllowOverride All
                    Require all granted
            </Directory>
            ErrorLog ${APACHE_LOG_DIR}/error_cms.log
            CustomLog ${APACHE_LOG_DIR}/access_cms.log combined
   </VirtualHost>

Il nome csm.dev è correttamente definito in /etc/hosts.

Se al posto di /example scrivo / nella rotta in alto, tutto funziona bene.

Se scrivo /example ho errore 400 di Apache: attenzione, non esce un errore Laravel ma un errore Apache, segno che laravel, nell'invocazione di example/ non viene nemmeno innnescato.

Questo mi ha fatto cercare un attimo in rete e sono state illuminanti queste due risposte:

In particolare quest'utima risposta nel forum di Stackoverflow mi ha fatto notare che, anche con questa direttiva sbagliata, l'URL http://localhost/cms/public/index.php/example funzionava benissimo, ma http://cms.dev/example invece andava in errore.

Con l'altra sono andato a a vedere cos'avevo fatto nello scrivere il virtual host e il file .htaccess all'interno della cartella public/.

In entrambi c'è la direttiva AllowOverride All, OK, ma il big fail è in quel publiczoo segnalato in alto, di cui mi sono accorto solo al momento in cui copiavo la direttiva per metterla qui.

Dopo la correzione del virtualhost funziona tutto!

Sul significato della direttiva AllowOverride ho questo commento sul sito di Apache:

   AllowOverride directive is used to allow the use of .htaccess within the web server to allow overriding of the Apache config on a per directory basis.
   [...]
   When the server finds an .htaccess file (as specified by AccessFileName), it needs to know which directives declared in that file can override earlier configuration directives.

Avevo anche tentato di configurare diversamente il file .htaccess senza alcun risultato. Di fatto in questo caso è ininfluente.

In ogni caso è essenziale dichiarare alcunhché nel file .htaccess, nella sotto tabella è evidente

  apache   htaccess    funziona
  -----------------------------
  NO       net         NO
  NO       laravel     NO
  YES      net         YES
  YES      laravel     YES

htaccess = net è un file che ho compilato su un esempio trovato in rete:

  <IfModule mod_rewrite.c>
      Options +FollowSymLinks
      RewriteEngine On
  
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteRule ^ index.php [L]
  </IfModule>

mentre htaccess = laravel è il file out-of-the-box dell'installazione di laravel fatta con composer:

  <IfModule mod_rewrite.c>
      <IfModule mod_negotiation.c>
          Options -MultiViews
      </IfModule>
  
      RewriteEngine On
  
      # Redirect Trailing Slashes If Not A Folder...
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteRule ^(.*)/$ /$1 [L,R=301]
  
      # Handle Front Controller...
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteRule ^ index.php [L]
  
      # Handle Authorization Header
      RewriteCond %{HTTP:Authorization} .
      RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
  </IfModule>

Dunque, diversamente da quanto visto nello screencast di spiegazione di Laravel che sto seguendo, è essenziale che ci sia la sezione contenente la direttiva AllowOverride All nel virtual host Apache.

Creare le tabelle[modifica]

  php artisan make:migration create_tasks_table --create=tasks

Qui per esempio creiamo nel DB la tabella tasks. Non è come in Yii che prima la creiamo nel DB e poi la facciamo ciucciare al Framework: qui la creiamo prima in PHP definendo tutto (dai nomi dei campi, ai tipi, ai constraint, agli indici, addirittura ai commenti!).

Ad esempio posso definire una tabella così complessa:

  <?php
  
  use Illuminate\Database\Schema\Blueprint;
  use Illuminate\Database\Migrations\Migration;
  
  class CreateDdtTable extends Migration
  {
     /**
      * Run the migrations.
      *
      * @return void
      */
     public function up()
     {
        Schema::create('ddt', function (Blueprint $table) {
           $table->increments('id');
           $table->primary('id');
           $table->dateTime('ddt_date')->default(DB::raw('CURRENT_TIMESTAMP'));
           $table->string('ddt_no', 100);
           $table->integer('supplier_id', 11);
           $table->foreign('supplier_id')->references('id')->on('masterdata');
  
           $table->integer('customer_id', 11);
           $table->foreign('customer_id')->references('id')->on('masterdata');
  
           $table->integer('carrier_id', 11);
           $table->foreign('carrier_id')->references('id')->on('masterdata');
  
           $table->string('delivery_address', 255);
           $table->double('lat', 8, 6);
           $table->double('lng', 8, 6);
           $table->integer('district'); // sestiere
           $table->integer('pallet_no'); // numero dei bancali
           $table->integer('disposable_pallet_no'); // numero dei bancali a perdere
           $table->dateTime('takingcharge_datetime'); // data ora di presa in carico
           
           $table->integer('operator_id', 11);
           $table->foreign('operator_id')->references('id')->on('operator');
  
           $table->double('weight', 5,1); // in kg
           
           $table->integer('packaging_id', 11); // tipo di confezione
           $table->foreign('packaging_id')->references('id')->on('packaging'); // tipo di confezione
  
           $table->string('packages_number', 100);
           $table->dateTime('return_datetime'); // (data di rientro della merce non consegnata al cliente)
           $table->dateTime('return_to_vendor_datetime'); // (data di reso al fornitore)
           $table->integer('status_id');
           
           $table->engine = 'InnoDB';
        });
     }
  
     /**
      * Reverse the migrations.
      *
      * @return void
      */
     public function down()
     {
        Schema::drop('ddt');
     }
  }

Per fare ciucciare da Laravel la definizione della tabella dobbiamo mettere questo script sotto

  <nome applicazione>/database/migrations/

La tabella si chiamerà ddt, come è scritto nella definizione della classe:

  php artisan make:migration create_ddt_table --create=ddt

Una volta fatto girare il comando possiamo vedere la tabella così com'è stata creata in MySQL.

Definire il Modello (Model)[modifica]

Il model è il risultato dell'applicazione di un mapper tra tabella e oggetto relazionale (ORM = Object Relational Mapper) e viene creato utilizzando il comando

  php artisan make:model Task

oppure nel caso della tabella DDT:

  php artisan make:model Ddt

In sostanza non basta creare la tabella nello schema database ma occorre creare la controparte nell'ambiente PHP (l'oggetto corrispondente alla tabella) perché è attraverso questo che Laravel lavora sulla tabella.

L'ORM di default di Laravel si chiama Eloquent.

Il file grezzo viene depositato nella cartella

   <nome applicazione>/app/Ddt.php

e la sua forma è questa

  <?php
  
  namespace App;
  
  use Illuminate\Database\Eloquent\Model;
  
  class Ddt extends Model
  {
      //
  }
  ?>


Definire le Rotte (Routes)[modifica]

Laravel, seguendo l'esempio del Quickstart, procede in un modo interessante. Prima di definire la struttura del modello, mi fa definire quali operazioni voglio farci sopra.

Le rotte vengono usate per redirigere gli URL verso i controllers o le funzioni anonime che devono essere eseguiti quando un utente accede ad una determinata pagina (URL). Le rotte di Laravel sono definite nel file

  app/Http/routes.php 

file che troviamo incluso automaticamente in ogni nuovo progetto Laravel. Per rispondere alla url / (root del nostro sito) definiremo una route rule:

  /**
   * Show Task Dashboard
   */
  Route::get('/', function () {
      return view('tasks');
  });

Definire i Layouts[modifica]

Creo la directory

  $ mkdir <nome applicazione>/resources/views/layouts

Al suo interno creo il nuovo file

  $ touch app.blade.php

e ci scrivo dentro questo

  <!-- resources/views/layouts/app.blade.php -->
  <!DOCTYPE html>
  <html lang="en">
     <head>
         <title>Laravel Quickstart - Basic</title>
     </head>
     <body>
         <div class="container">
             <nav class="navbar navbar-default">
                 <!-- Navbar Contents -->
             </nav>
         </div>
         @yield('content')
     </body>
  </html>

Il content è definito in un altro file sempre sotto resources/views (che è esattamente il file di view che andremo ad invocare!):

 <!-- resources/views/tasks.blade.php -->
 @extends('layouts.app')
 @section('content')
     <!-- Bootstrap Boilerplate... -->
     <div class="panel-body">
         <!-- Display Validation Errors -->
         @include('common.errors')
         <!-- New Task Form -->
         <form action="{{ url('task') }}" method="POST" class="form-horizontal">
             {{ csrf_field() }}
             <!-- Task Name -->
             <div class="form-group">
                 <label for="task" class="col-sm-3 control-label">Task</label>
                 <div class="col-sm-6">
                     <input type="text" name="name" id="task-name" class="form-control">
                 </div>
             </div>
             <!-- Add Task Button -->
             <div class="form-group">
                 <div class="col-sm-offset-3 col-sm-6">
                     <button type="submit" class="btn btn-default">
                          Add Task
                     </button>
                 </div>
             </div>
         </form>
     </div>
     <!-- TODO: Current Tasks -->
 @endsection
 

Alla fine definisco anche il template dell'errore, creando prima il file nella corretta posizione:

  $ mkdir common
  $ cd common/
  $ touch errors.blade.php

e scrivendoci dentro:

 <!-- resources/views/common/errors.blade.php -->
 
 @if (count($errors) > 0)
     <!-- Form Error List -->
     <div class="alert alert-danger">
         <strong>Whoops! Something went wrong!</strong>
 
         <br/><br/>
 
         <ul>
             @foreach ($errors->all() as $error)
                 <li>{{ $error }}</li>
             @endforeach
         </ul>
     </div>
 @endif
  

Notare che la notazione col punto . nelle direttive include viene risolta in un nome di filesystem sostituendo il . con la /

Ad esempio

 @include('common.errors')

cercherà in layouts/common/errors


Relazioni[modifica]

1 a 1, 1 a molti[modifica]

Breve iter per exempla: supponiamo di avere una classe User e una classe Post in una relazione uno a molti, uno User può avere da 1 a molti Post. Con il formalismo degli schemi E/R abbiamo:

Errore nella creazione della miniatura: Impossibile salvare la miniatura nella destinazione

Regola:

  • nella classe dal lato MOLTI usare la funzione belongsTo(),
  • dal lato UNO usare il metodo hasOne()

Molti a molti[modifica]

In entrambe le classi (da entrambe le parti) si usa il metodo belongsToMany() che accede alla tabella di pivot (quella che risolve l'associazione molti a molti)

Errore nella creazione della miniatura: Impossibile salvare la miniatura nella destinazione

La cosa fantastica in questo caso è che sfruttando la convenzione-sopra-configurazione Laravel crea le query giuste individuando la tabella pivot senza bisogno di un modello pivot.

hasManyThrough[modifica]

Questa interessante funzione ORM Eloquent consente di creare delle query che coinvolgono tre tabelle.

Ad esempio ritornare tutti i post scritti da utenti italiani.

Ci sarà un modello Country (la cui tabella sarà creata da artisan), la cui chiave esterna è ospitata dalla tabella User, la cui chiave esterna è ospitata dalla tabella Post

Errore nella creazione della miniatura: Impossibile salvare la miniatura nella destinazione

Per accedere alla tabella Post a partire dal country_id dobbiamo transitare attraverso (through) la tabella User.

Da qui la necessità di creare un metodo posts() all'interno del modello dello Country (che rappresenta la tabella di patrtenza):

   class Country extends Model
   {
       //
       public function posts() {
           return $this->hasManyThrough('App\Post', 'App\User');
       }
   }

dove esplicitiamo nella funzione hasManyThrough la relazione descritta in figura:

  • il primo argomento è la tabella (il modello) di arrivo
  • il secondo è la tabella (il modello) intermedio (through)

In sostanza Laravel costruisce per noi una query di questo tipo:

   select
     p.title
   from 
     posts p, users u, countries c
   where
     1=1
     and c.id = u.country_id
     and u.id = p.user_id
     and c.id = <parameter>

La rotta definita in routes.php ci consente infine di sfruttare il metodo Country::posts() creato sopra:

   Route::get('user/country/{id}', function ($id) {
        $country = Country::find($id);
        foreach ($country->posts as $post) {
            return '-'.$post->title;
        }
   });

Relazioni polimorfiche[modifica]

Sono relazioni che consentono di associare un'entità a più entità attraverso una sola relazione che però viene tipizzata. Questo è l'esempio che si trova nel sito di Laravel

Struttura delle tabelle[modifica]

Le relazioni polimorfiche consentono ad un modello di appartenere a più modelli su una singola associazione. Per esempio immaginiamo che gli utenti della nostra applicazione possano caricare foto sia destinate ai post che al loro profilo.

Verrebbe naturale pensare a due entità photo, quindi due tabelle: quella che contiene le foto dei post (post_photo) e quella che contiene le foto degli utenti (user_photo).

Oppure possiamo gestire tutto da una sola tabella "polimorfica" photo tipizzando la foto per una entità o per l'altra.

Praticamente memorizziamo nella tabella l'id chiave esterna dell'oggetto a cui appartiene la foto ed il tipo. Quindi la vera chiave esterna non sarà il solo id - che può essere uguale per user e photo, ma sarà invece una chiave composta fk id + tipo.

Errore nella creazione della miniatura: Impossibile salvare la miniatura nella destinazione

Vediamo quindi la struttura della tabella richiesta per gestire questo tipo di relazione:

  posts
     id - integer
     title - string
     body - text
  
  users
     id - integer
     name - string
     nickname - string
  
  photos
     id - integer
     title - text
     imageable_id - integer
     imageable_type - string


Le due colonne importanti da notare sono le colonne imageable_id and imageable_type (convenzionalmente chiamate così) nella tabella comment.

Il campo imageable_id conterrà l'id dell'oggetto (post o user) mentre imageable_type conterrà il nome della classe che gestisce il modello (o tabella o entità). Questo campo è quello con il quale l'ORM determina quale modello riferirsi quando accede alla relazione imageable.

Blade[modifica]

Blade è un linguaggio di templating. Per chi ha usato PHP Smarty, esso è molto simile. Si tratta di scrivere HTML e iniettare codice Blade anziché Smarty. Ad esempio, se in Smarty scriviamo:

  <body>
     <h1>Benvenuto {$user}</h1>
     {if ($errors['email'] != null)}
     <span class="help-block">
         <strong>{$errors->first('email')}</strong>
     </span>
     {/if}
  </body>

in Blade scriviamo

  <body>
     <h1>Benvenuto {{$user}}</h1>
     @if ($errors->has('email'))
     <span class="help-block">
         <strong>{{ $errors->first('email') }}</strong>
     </span>
     @endif
  </body>

Tip & Tricks[modifica]

Mai usare il dollaro nelle rotte[modifica]

Mai usare il simbolo $ nelle variabili dentro alla definizione delle rotte! Ad esempio

  • Mai così
  Route::get('post/{$id}/{$name}', 'PostsController@show_post');
  • ma invece così è ok
  Route::get('post/{id}/{name}', 'PostsController@show_post');

Migrations[modifica]

Nel linguaggio di Laravel le migrazioni (migrations) sono da intendersi come "operazioni sul database".

Vendor[modifica]

Normalmente le estensioni PHP nei vendor non sono comprese nel tarball di git perché dotate di vita autonoma e devono quindo essere scaricate ogni volta che si clona un repository git.

Quindi dopo aver eseguito

$ git clone https://myuser@bitbucket.org/myproject/myproject.git

   Cloning into 'myproject'...
   Password for 'https://myuser@bitbucket.org': 
   remote: Counting objects: 315, done.
   remote: Compressing objects: 100% (253/253), done.
   remote: Total 315 (delta 65), reused 271 (delta 45)
   Ricezione degli oggetti: 100% (315/315), 46.98 MiB | 2.92 MiB/s, done.
   Risoluzione dei delta: 100% (65/65), done.
   Checking connectivity... fatto.

occorre scaricare i pacchetti vendor up-to-date. I pacchetti da collegare alla mia applicazione sono scritti nel file composer.json; posizioniamoci nella cartella del progetto e:

$ composer update

et voilà.

Corretta sequenza delle invocazioni dalla rotta al controller[modifica]

    URL: http://cms.dev/post/2
                        |    ___ non ci va il $ !!!
                        v    v
           Route::get('post/{$id}',                  'PostsController@show_post');        (routes.php)
                         |                                      |
                         v                                      v
                 views/post.blade.php  app/Http/Controllers/PostController.php
   
   
   
   URL: http://cms.dev/contact
                         |
                         v
       Route::resource('contact',                              'PostsController@contact');    (routes.php)
                         |                                             |
                         v                                             v
       resources/views/contact.blade.php    app/Http/Controllers/PostController.php