Utilizzo del Modello del Database in Webman (Stile Laravel)

Il modello di webman è basato su Eloquent ORM. Ogni tabella del database ha un corrispondente "modello" che interagisce con essa. Puoi utilizzare il modello per interrogare i dati nella tabella e per inserire nuovi record.

Prima di iniziare, assicurati di aver configurato la connessione al database nel file config/database.php.

Attenzione: Per supportare gli osservatori del modello in Eloquent ORM, è necessario importare adicionalmente composer require "illuminate/events" esempio

Esempio di Modello del Database

<?php
namespace app\model;

use support\Model;

class User extends Model
{
    /**
     * Il nome della tabella associata al modello
     *
     * @var string
     */
    protected $table = 'user';

    /**
     * Ridefinire la chiave primaria, di default è id
     *
     * @var string
     */
    protected $primaryKey = 'uid';

    /**
     * Indica se mantenere automaticamente i timestamp
     *
     * @var bool
     */
    public $timestamps = false;
}

Nome della Tabella

Puoi specificare una tabella del database personalizzata definendo la proprietà table nel modello:

class User extends Model
{
    /**
     * Il nome della tabella associata al modello
     *
     * @var string
     */
    protected $table = 'user';
}

Chiave Primaria

Eloquent presume anche che ogni tabella del database abbia una colonna chiave primaria chiamata id. Puoi definire una proprietà protetta $primaryKey per sovrascrivere questa convenzione.

class User extends Model
{
    /**
     * Ridefinire la chiave primaria, di default è id
     *
     * @var string
     */
    protected $primaryKey = 'uid';
}

Eloquent presume che la chiave primaria sia un valore intero autoincrementato, il che significa che per impostazione predefinita la chiave primaria sarà automaticamente convertita in tipo int. Se desideri utilizzare una chiave primaria non autoincrementata o non numerica, devi impostare la proprietà pubblica $incrementing su false.

class User extends Model
{
    /**
     * Indica se la chiave primaria del modello è autoincrementante
     *
     * @var bool
     */
    public $incrementing = false;
}

Se la tua chiave primaria non è un intero, devi impostare la proprietà protetta $keyType del modello su string:

class User extends Model
{
    /**
     * "Tipo" dell'ID autoincrementante.
     *
     * @var string
     */
    protected $keyType = 'string';
}

Timestamp

Per impostazione predefinita, Eloquent si aspetta che le colonne created_at e updated_at siano presenti nella tua tabella. Se non desideri che Eloquent gestisca automaticamente queste due colonne, imposta la proprietà $timestamps nel modello su false:

class User extends Model
{
    /**
     * Indica se mantenere automaticamente i timestamp
     *
     * @var bool
     */
    public $timestamps = false;
}

Se hai bisogno di personalizzare il formato dei timestamp, imposta la proprietà $dateFormat nel tuo modello. Questa proprietà determina il modo in cui le proprietà della data vengono memorizzate nel database e il formato con cui il modello viene serializzato in un array o JSON:

class User extends Model
{
    /**
     * Formato di memorizzazione dei timestamp
     *
     * @var string
     */
    protected $dateFormat = 'U';
}

Se hai bisogno di personalizzare i nomi dei campi utilizzati per memorizzare i timestamp, puoi farlo impostando i valori delle costanti CREATED_AT e UPDATED_AT nel modello:

class User extends Model
{
    const CREATED_AT = 'creation_date';
    const UPDATED_AT = 'last_update';
}

Connessione al Database

Per impostazione predefinita, i modelli Eloquent utilizzeranno la connessione al database configurata nell'applicazione. Se desideri specificare una connessione diversa per il modello, imposta la proprietà $connection:

class User extends Model
{
    /**
     * Nome della connessione del modello
     *
     * @var string
     */
    protected $connection = 'connection-name';
}

Valori Predefiniti per le Proprietà

Se desideri definire valori predefiniti per alcune proprietà del modello, puoi farlo definendo la proprietà $attributes nel modello:

class User extends Model
{
    /**
     * Valori predefiniti per le proprietà del modello.
     *
     * @var array
     */
    protected $attributes = [
        'delayed' => false,
    ];
}

Recupero del Modello

Dopo aver creato il modello e la relativa tabella del database, puoi iniziare a interrogare i dati dal database. Immagina ogni modello Eloquent come un potente costruttore di query che puoi utilizzare per interrogare più rapidamente i dati associati nella tabella a essa. Ad esempio:

$users = app\model\User::all();

foreach ($users as $user) {
    echo $user->name;
}

Suggerimento: Poiché i modelli Eloquent sono anche costruttori di query, dovresti anche leggere tutti i metodi disponibili in Query Builder. Puoi utilizzare questi metodi nelle query Eloquent.

Vincoli Aggiuntivi

Il metodo all di Eloquent restituirà tutti i risultati dal modello. Poiché ogni modello Eloquent funge da costruttore di query, puoi anche aggiungere condizioni di query e quindi utilizzare il metodo get per ottenere i risultati:

$users = app\model\User::where('name', 'like', '%tom')
               ->orderBy('uid', 'desc')
               ->limit(10)
               ->get();

Ricaricare il Modello

Puoi utilizzare i metodi fresh e refresh per ricaricare il modello. Il metodo fresh ricercherà nuovamente il modello nel database. Le istanze di modello esistenti non verranno influenzate:

$user = app\model\User::where('name', 'tom')->first();

$fresh_user = $user->fresh();

Il metodo refresh riassocia i dati ricevuti dal database al modello esistente. Inoltre, le relazioni già caricate verranno ricaricate:

$user = app\model\User::where('name', 'tom')->first();

$user->name = 'jerry';

$user = $user->fresh();

$user->name; // "tom"

Collezioni

I metodi all e get di Eloquent possono restituire più risultati, restituendo un'istanza di Illuminate\Database\Eloquent\Collection. La classe Collection fornisce molte funzioni di supporto per gestire i risultati di Eloquent:

$users = $users->reject(function ($user) {
    return $user->disabled;
});

Utilizzare I Cursori

Il metodo cursor ti permette di utilizzare un cursore per scorrere il database, eseguendo solo una query una volta. Quando lavori con grandi quantità di dati, il metodo cursor può ridurre significativamente l'uso della memoria:

foreach (app\model\User::where('sex', 1)->cursor() as $user) {
    //
}

Il cursore restituisce un'istanza di Illuminate\Support\LazyCollection. Lazy collections ti permettono di utilizzare la maggior parte dei metodi di raccolta disponibili nelle collezioni di Laravel, caricando solo un singolo modello in memoria alla volta:

$users = app\model\User::cursor()->filter(function ($user) {
    return $user->id > 500;
});

foreach ($users as $user) {
    echo $user->id;
}

Sottoquery Selettive

Eloquent offre un supporto avanzato per le sottoquery, permettendoti di estrarre informazioni dalle tabelle correlate con una singola query. Ad esempio, supponiamo di avere una tabella di destinazione destinations e una tabella di voli flights per raggiungere tali destinazioni. La tabella flights contiene un campo arrival_at che indica quando un volo arriva a destinazione.

Utilizzando i metodi select e addSelect forniti dalla funzionalità di sottoquery, possiamo interrogare tutte le destinazioni destinations e il nome dell'ultimo volo per arrivare a ciascuna:

use app\model\Destination;
use app\model\Flight;

return Destination::addSelect(['last_flight' => Flight::select('name')
    ->whereColumn('destination_id', 'destinations.id')
    ->orderBy('arrived_at', 'desc')
    ->limit(1)
])->get();

Ordinamento in base alle Sottoquery

Inoltre, la funzione orderBy del costruttore di query supporta anche le sottoquery. Possiamo usare questa funzionalità per ordinare tutte le destinazioni in base all'orario dell'ultimo volo in arrivo a ciascuna. Anche in questo caso, questo può essere fatto eseguendo una singola query nel database:

return Destination::orderByDesc(
    Flight::select('arrived_at')
        ->whereColumn('destination_id', 'destinations.id')
        ->orderBy('arrived_at', 'desc')
        ->limit(1)
)->get();

Recupero di un Singolo Modello / Collezione

Oltre a recuperare tutti i record da una tabella specifica, puoi utilizzare i metodi find, first o firstWhere per recuperare un singolo record. Questi metodi restituiscono una singola istanza di modello, invece di una collezione di modelli:

// Trova un modello per chiave primaria...
$flight = app\model\Flight::find(1);

// Trova il primo modello che soddisfa i criteri di ricerca...
$flight = app\model\Flight::where('active', 1)->first();

// Trova il primo modello che soddisfa i criteri di ricerca con un'implementazione rapida...
$flight = app\model\Flight::firstWhere('active', 1);

Puoi anche utilizzare un array di chiavi primarie come parametro per il metodo find, che restituirà una collezione di record corrispondenti:

$flights = app\model\Flight::find([1, 2, 3]);

A volte potresti voler eseguire un'azione alternativa quando non riesci a trovare un valore mentre cerchi il primo risultato. Il metodo firstOr restituirà il primo risultato trovato, e se non ci sono risultati, eseguirà il callback fornito. Il valore di ritorno del callback sarà il valore restituito dal metodo firstOr:

$model = app\model\Flight::where('legs', '>', 100)->firstOr(function () {
        // ...
});

Il metodo firstOr accetta anche un array di colonne per la ricerca:

$model = app\model\Flight::where('legs', '>', 100)
            ->firstOr(['id', 'legs'], function () {
                // ...
            });

Eccezione "Non Trovato"

A volte potresti voler sollevare un'eccezione quando un modello non viene trovato. Questo è molto utile nei controller e nelle rotte. I metodi findOrFail e firstOrFail recuperano il primo risultato della query e, se non viene trovato, sollevano l'eccezione Illuminate\Database\Eloquent\ModelNotFoundException:

$model = app\model\Flight::findOrFail(1);
$model = app\model\Flight::where('legs', '>', 100)->firstOrFail();

Recupero di Collezioni

Puoi anche utilizzare i metodi count, sum e max forniti dal costruttore di query e altre funzioni di collezione per operare sulle collezioni. Questi metodi restituiranno solo i valori scalari appropriati, anziché un'istanza di modello:

$count = app\model\Flight::where('active', 1)->count();

$max = app\model\Flight::where('active', 1)->max('price');

Inserimento

Per aggiungere un nuovo record al database, prima crea una nuova istanza del modello, imposta le proprietà dell'istanza e poi chiama il metodo save:

<?php

namespace app\controller;

use app\model\User;
use support\Request;
use support\Response;

class FooController
{
    /**
     * Aggiungi un nuovo record alla tabella utenti
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // Verifica la richiesta

        $user = new User;

        $user->name = $request->get('name');

        $user->save();
    }
}

Le colonne created_at e updated_at verranno impostate automaticamente (quando la proprietà $timestamps nel modello è impostata su true), senza bisogno di impostare manualmente i valori.

Aggiornamento

Il metodo save può essere utilizzato anche per aggiornare i modelli esistenti nel database. Per aggiornare un modello, devi prima recuperarlo, impostare le proprietà da aggiornare e poi chiamare il metodo save. Analogamente, il timestamp updated_at verrà aggiornato automaticamente, quindi non è necessario impostare manualmente il valore:

$user = app\model\User::find(1);
$user->name = 'jerry';
$user->save();

Aggiornamento in Massa

app\model\User::where('uid', '>', 10)
          ->update(['name' => 'tom']);

Controllare le Variazioni di Proprietà

Eloquent fornisce i metodi isDirty, isClean e wasChanged per controllare lo stato interno del modello e determinare come le sue proprietà sono cambiate rispetto al caricamento iniziale.
Il metodo isDirty determina se qualche proprietà è stata modificata dall'ultimo caricamento del modello. Puoi passare un nome di proprietà specifico per determinare se una proprietà specifica è stata modificata. Il metodo isClean è l'opposto di isDirty e accetta anche un parametro di proprietà opzionale:

$user = User::create([
    'first_name' => 'Taylor',
    'last_name' => 'Otwell',
    'title' => 'Developer',
]);

$user->title = 'Painter';

$user->isDirty(); // true
$user->isDirty('title'); // true
$user->isDirty('first_name'); // false

$user->isClean(); // false
$user->isClean('title'); // false
$user->isClean('first_name'); // true

$user->save();

$user->isDirty(); // false
$user->isClean(); // true

Il metodo wasChanged determina se qualche proprietà è stata cambiata nell'ultimo ciclo di richiesta durante l'ultima operazione di salvataggio del modello. Puoi anche passare il nome di una proprietà per vedere se una proprietà specifica è stata cambiata:

$user = User::create([
    'first_name' => 'Taylor',
    'last_name' => 'Otwell',
    'title' => 'Developer',
]);

$user->title = 'Painter';
$user->save();

$user->wasChanged(); // true
$user->wasChanged('title'); // true
$user->wasChanged('first_name'); // false

Assegnazione in Massa

Puoi anche utilizzare il metodo create per salvare un nuovo modello. Questo metodo restituisce un'istanza di modello. Tuttavia, prima di utilizzarlo, devi specificare le proprietà fillable o guarded sul modello, poiché tutti i modelli Eloquent sono per impostazione predefinita non predisposti per l'assegnazione in massa.

L'assegnazione in massa può comportare vulnerabilità quando un utente invia parametri HTTP inaspettati e modifica i campi nel database che non intendi cambiare. Ad esempio: un utente malintenzionato potrebbe inviare il parametro is_admin attraverso una richiesta HTTP e poi passarlo al metodo create, consentendo così all'utente di elevare il proprio privilegi a amministratore.

Pertanto, prima di iniziare, dovresti definire quali proprietà nel modello possono essere assegnate in massa. Puoi farlo mediante la proprietà $fillable sul modello. Ad esempio: permetti alla proprietà name del modello Flight di essere assegnata in massa:

<?php

namespace app\model;

use support\Model;

class Flight extends Model
{
    /**
     * Proprietà assegnabili in massa.
     *
     * @var array
     */
    protected $fillable = ['name'];
}

Una volta impostate le proprietà assegnabili in massa, puoi inserire nuovi dati nel database mediante il metodo create. Il metodo create restituirà l'istanza del modello salvato:

$flight = app\model\Flight::create(['name' => 'Flight 10']);

Se hai già un'istanza di modello, puoi passare un array al metodo fill per assegnare valori:

$flight->fill(['name' => 'Flight 22']);

La proprietà $fillable può essere vista come una "whitelist" per l'assegnazione in massa, e puoi anche utilizzare la proprietà $guarded per implementare una blacklist. La proprietà $guarded contiene gli array non consentiti per l'assegnazione in massa. Cioè, $guarded funziona come una "blacklist". Nota: puoi utilizzare solo uno tra $fillable o $guarded, non entrambi contemporaneamente. Nell'esempio seguente, tutte le proprietà sono assegnabili tranne la proprietà price:

<?php

namespace app\model;

use support\Model;

class Flight extends Model
{
    /**
     * Proprietà non assegnabili in massa.
     *
     * @var array
     */
    protected $guarded = ['price'];
}

Se desideri permettere a tutte le proprietà di essere assegnate in massa, puoi definire $guarded come un array vuoto:

/**
 * Proprietà non assegnabili in massa.
 *
 * @var array
 */
protected $guarded = [];

Altri Metodi di Creazione

firstOrCreate/ firstOrNew
Ecco due metodi che puoi usare per l'assegnazione in massa: firstOrCreate e firstOrNew. Il metodo firstOrCreate tenterà di abbinare i dati nel database utilizzando le coppie chiave / valore fornite. Se non trova un modello nel database, inserirà un record con le proprietà del primo parametro e eventuali proprietà opzionali fornite nel secondo parametro.

Il metodo firstOrNew funziona in modo simile al metodo firstOrCreate, cercando un record nel database con le proprietà fornite. Tuttavia, se il metodo firstOrNew non trova il modello corrispondente, restituirà una nuova istanza di modello. Nota che l'istanza di modello restituita da firstOrNew non è ancora salvata nel database, è necessario chiamare manualmente il metodo save per salvarla:

// Ricerca il volo tramite name, se non esiste lo crea...
$flight = app\model\Flight::firstOrCreate(['name' => 'Flight 10']);

// Ricerca il volo tramite name, o utilizza name e delayed e arrival_time per creare...
$flight = app\model\Flight::firstOrCreate(
    ['name' => 'Flight 10'],
    ['delayed' => 1, 'arrival_time' => '11:30']
);

// Ricerca il volo tramite name, se non esiste crea un'istanza...
$flight = app\model\Flight::firstOrNew(['name' => 'Flight 10']);

// Ricerca il volo tramite name, o utilizza name e delayed e arrival_time per creare un'istanza...
$flight = app\model\Flight::firstOrNew(
    ['name' => 'Flight 10'],
    ['delayed' => 1, 'arrival_time' => '11:30']
);

Potresti anche trovarti nella situazione in cui desideri aggiornare un modello esistente o crearne uno nuovo in caso contrario. Il metodo updateOrCreate lo fa in un colpo solo. Simile al metodo firstOrCreate, updateOrCreate persiste il modello, quindi non è necessario chiamare save():

// Se ci sono voli da Oakland a San Diego, imposta il prezzo a 99 dollari.
// Se non viene trovato un modello esistente, verrà creata una nuova registrazione.
$flight = app\model\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99, 'discounted' => 1]
);

Eliminazione di Modelli

Puoi chiamare il metodo delete su un'istanza di modello per eliminarla:

$flight = app\model\Flight::find(1);
$flight->delete();

Eliminazione di Modelli tramite Chiave Primaria

app\model\Flight::destroy(1);

app\model\Flight::destroy(1, 2, 3);

app\model\Flight::destroy([1, 2, 3]);

app\model\Flight::destroy(collect([1, 2, 3]));

Eliminazione di Modelli tramite Query

$deletedRows = app\model\Flight::where('active', 0)->delete();

Copia di Modelli

Puoi utilizzare il metodo replicate per copiare un'istanza nuova non ancora salvata nel database, questo metodo è molto utile quando diverse istanze di modello condividono molte proprietà simili.

$shipping = App\Address::create([
    'type' => 'shipping',
    'line_1' => '123 Example Street',
    'city' => 'Victorville',
    'state' => 'CA',
    'postcode' => '90001',
]);

$billing = $shipping->replicate()->fill([
    'type' => 'billing'
]);

$billing->save();

Confronto di Modelli

A volte potrebbe essere necessario verificare se due modelli sono "uguali". Il metodo is può essere utilizzato per verificare rapidamente se due modelli hanno la stessa chiave primaria, tabella e connessione al database:

if ($post->is($anotherPost)) {
    //
}

Osservatori del Modello

Per riferimento, consulta Eventi e Osservatori del Modello in Laravel

Nota: Per supportare gli osservatori del modello in Eloquent ORM, è necessario importare adicionalmente composer require "illuminate/events"

<?php
namespace app\model;

use support\Model;
use app\observer\UserObserver;

class User extends Model
{
    public static function boot()
    {
        parent::boot();
        static::observe(UserObserver::class);
    }
}

Transazioni

Consulta Transazioni del Database