Datenbankmodell Model-Einsatz (Laravel-Stil)
Das Webman-Modell basiert auf Eloquent ORM. Jede Datenbanktabelle hat ein entsprechendes „Modell“, um mit dieser Tabelle zu interagieren. Du kannst mit dem Modell Daten aus der Datenbank abfragen und neue Datensätze in die Datenbank einfügen.
Bevor du beginnst, stelle sicher, dass die Datenbankverbindung in config/database.php
konfiguriert ist.
Hinweis: Damit Eloquent ORM Modellbeobachter unterstützt, muss zusätzlich
composer require "illuminate/events"
importiert werden. Beispiel
Beispiel für ein Datenbankmodell
<?php
namespace app\model;
use support\Model;
class User extends Model
{
/**
* Der mit dem Modell verknüpfte Tabellenname
*
* @var string
*/
protected $table = 'user';
/**
* Die Primärschlüssel neu definieren, standardmäßig id
*
* @var string
*/
protected $primaryKey = 'uid';
/**
* Gibt an, ob Zeitstempel automatisch verwaltet werden
*
* @var bool
*/
public $timestamps = false;
}
Tabellenname
Du kannst den Tabellenname angeben, indem du die table
-Eigenschaft im Modell definierst:
class User extends Model
{
/**
* Der mit dem Modell verknüpfte Tabellenname
*
* @var string
*/
protected $table = 'user';
}
Primärschlüssel
Eloquent geht auch davon aus, dass jede Datenbanktabelle eine Primärspalte mit dem Namen id
hat. Du kannst eine geschützte $primaryKey
-Eigenschaft definieren, um diese Konvention zu überschreiben.
class User extends Model
{
/**
* Die Primärschlüssel neu definieren, standardmäßig id
*
* @var string
*/
protected $primaryKey = 'uid';
}
Eloquent geht davon aus, dass der Primärschlüssel ein automatisch inkrementierender ganzzahliger Wert ist, was bedeutet, dass der Primärschlüssel standardmäßig automatisch in den int-Typ umgewandelt wird. Wenn du einen nicht inkrementierenden oder nicht numerischen Primärschlüssel verwenden möchtest, musst du die öffentliche $incrementing
-Eigenschaft auf false setzen:
class User extends Model
{
/**
* Gibt an, ob der Primärschlüssel des Modells inkrementiert wird
*
* @var bool
*/
public $incrementing = false;
}
Wenn dein Primärschlüssel kein ganzzahliger Wert ist, musst du die geschützte $keyType
-Eigenschaft im Modell auf string
setzen:
class User extends Model
{
/**
* Der „Typ“ von automatisch inkrementierenden IDs.
*
* @var string
*/
protected $keyType = 'string';
}
Zeitstempel
Standardmäßig erwartet Eloquent, dass in deiner Datenbank die Felder created_at
und updated_at
vorhanden sind. Wenn du nicht möchtest, dass Eloquent diese beiden Spalten automatisch verwaltet, setze die $timestamps
-Eigenschaft im Modell auf false:
class User extends Model
{
/**
* Gibt an, ob Zeitstempel automatisch verwaltet werden
*
* @var bool
*/
public $timestamps = false;
}
Falls du das Format der Zeitstempel anpassen musst, kannst du in deinem Modell die $dateFormat
-Eigenschaft setzen. Diese Eigenschaft bestimmt, wie das Datumsattribut in der Datenbank gespeichert wird, sowie das Format, wenn das Modell in ein Array oder JSON serialisiert wird:
class User extends Model
{
/**
* Speicherformat für Zeitstempel
*
* @var string
*/
protected $dateFormat = 'U';
}
Wenn du die Feldnamen für die Speicherung der Zeitstempel anpassen möchtest, kannst du die Werte der Konstanten CREATED_AT
und UPDATED_AT
im Modell festlegen:
class User extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'last_update';
}
Datenbankverbindung
Standardmäßig verwenden Eloquent-Modelle die standardmäßige Datenbankverbindung, die in deiner Anwendung konfiguriert ist. Wenn du für das Modell eine andere Verbindung angeben möchtest, setze die $connection
-Eigenschaft:
class User extends Model
{
/**
* Der Verbindungsname des Modells
*
* @var string
*/
protected $connection = 'connection-name';
}
Standardwerte für Eigenschaften
Wenn du Standardwerte für einige Eigenschaften des Modells festlegen möchtest, kannst du die $attributes
-Eigenschaft im Modell definieren:
class User extends Model
{
/**
* Standardwerte für die Eigenschaften des Modells.
*
* @var array
*/
protected $attributes = [
'delayed' => false,
];
}
Modellabfragen
Nachdem du ein Modell und die zugehörige Datenbanktabelle erstellt hast, kannst du Daten aus der Datenbank abfragen. Stelle dir jedes Eloquent-Modell als einen leistungsstarken Query Builder
vor, mit dem du schneller auf die damit verknüpfte Datenbanktabelle zugreifen kannst. Zum Beispiel:
$users = app\model\User::all();
foreach ($users as $user) {
echo $user->name;
}
Hinweis: Da Eloquent-Modelle auch Abfrage-Builder sind, solltest du auch die Abfrage-Builder über alle verfügbaren Methoden lesen. Du kannst diese Methoden in Eloquent-Abfragen verwenden.
Zusätzliche Einschränkungen
Die all
-Methode von Eloquent gibt alle Ergebnisse im Modell zurück. Da jedes Eloquent-Modell als ein Query Builder
fungiert, kannst du auch Abfragebedingungen hinzufügen und die Methode get
verwenden, um die Abfrageergebnisse zu erhalten:
$users = app\model\User::where('name', 'like', '%tom')
->orderBy('uid', 'desc')
->limit(10)
->get();
Modell neu laden
Du kannst die Methoden fresh
und refresh
verwenden, um das Modell neu zu laden. Die fresh
-Methode holt das Modell erneut aus der Datenbank. Die vorhandene Modellinstanz bleibt unberührt:
$user = app\model\User::where('name', 'tom')->first();
$fresh_user = $user->fresh();
Die refresh
-Methode aktualisiert das vorhandene Modell mit neuen Daten aus der Datenbank. Außerdem werden bereits geladene Beziehungen neu geladen:
$user = app\model\User::where('name', 'tom')->first();
$user->name = 'jerry';
$user = $user->fresh();
$user->name; // "tom"
Sammlung
Die Methoden all
und get
von Eloquent können mehrere Ergebnisse abfragen und geben eine Instanz von Illuminate\Database\Eloquent\Collection
zurück. Die Collection
-Klasse bietet eine Vielzahl von Hilfsfunktionen zur Verarbeitung der Eloquent-Ergebnisse:
$users = $users->reject(function ($user) {
return $user->disabled;
});
Verwende Cursor
Die Methode cursor
ermöglicht es dir, die Datenbank mit einem Cursor zu durchlaufen, wobei nur eine Abfrage ausgeführt wird. Bei der Verarbeitung großer Datenmengen kann die cursor
-Methode den Speicherverbrauch erheblich reduzieren:
foreach (app\model\User::where('sex', 1)->cursor() as $user) {
//
}
cursor
gibt eine Instanz von Illuminate\Support\LazyCollection
zurück. Lazy Collections ermöglichen die Verwendung der meisten Sammlungsmethoden aus Laravel Collections, wobei jedes Mal nur ein einzelnes Modell in den Speicher geladen wird:
$users = app\model\User::cursor()->filter(function ($user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}
Selects Unterabfragen
Eloquent bietet erweiterte Unterstützung für Unterabfragen. Du kannst Informationen aus den zugehörigen Tabellen mit einer einzigen Abfrage abrufen. Angenommen, wir haben eine Ziel-Datenbanktabelle destinations
und eine Flugtabelle flights
, die ein Feld arrival_at
enthält, das angibt, wann der Flug am Ziel ankommt.
Mit den Funktionen select
und addSelect
, die durch Unterabfragen bereitgestellt werden, können wir mit einer einzigen Abfrage alle Ziele destinations
abfragen und den Namen des letzten Fluges an jedes Ziel abfragen:
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();
Nach Unterabfragen sortieren
Darüber hinaus unterstützt die orderBy
-Funktion des Abfrage-Builders Unterabfragen. Wir können diese Funktion nutzen, um alle Ziele nach der Ankunftszeit des letzten Flugs zu sortieren, wobei wir nur eine Abfrage an die Datenbank senden:
return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
)->get();
Einzelnes Modell / Sammlung abrufen
Neben dem Abrufen aller Datensätze aus der angegebenen Datenbanktabelle kannst du die Methoden find
, first
oder firstWhere
verwenden, um ein einzelnes Modell zu erhalten. Diese Methoden geben eine einzelne Modellinstanz zurück, anstatt eine Modellsammlung zurückzugeben:
// Ein Modell anhand des Primärschlüssels finden...
$flight = app\model\Flight::find(1);
// Das erste Modell, das den Abfragebedingungen entspricht, finden...
$flight = app\model\Flight::where('active', 1)->first();
// Das erste Modell, das den Abfragebedingungen entspricht, schnell erhalten...
$flight = app\model\Flight::firstWhere('active', 1);
Du kannst auch ein Array von Primärschlüsseln als Parameter an die find
-Methode übergeben, das dann eine Sammlung übereinstimmender Datensätze zurückgibt:
$flights = app\model\Flight::find([1, 2, 3]);
Manchmal möchtest du möglicherweise, dass eine andere Aktion ausgeführt wird, wenn bei der Suche nach dem ersten Ergebnis kein Wert gefunden wird. Die Methode firstOr
gibt das erste Ergebnis zurück, wenn es gefunden wird; wenn kein Ergebnis vorliegt, wird der angegebene Callback ausgeführt. Der Rückgabewert des Callbacks wird als Rückgabewert von firstOr
verwendet:
$model = app\model\Flight::where('legs', '>', 100)->firstOr(function () {
// ...
});
Die firstOr
-Methode akzeptiert auch ein Array von Feldern zur Abfrage:
$model = app\model\Flight::where('legs', '>', 100)
->firstOr(['id', 'legs'], function () {
// ...
});
„Nicht gefunden“-Ausnahme
Manchmal möchtest du, dass beim Fehlen eines Modells eine Ausnahme ausgelöst wird. Dies ist in Controllern und Routen sehr nützlich. Die Methoden findOrFail
und firstOrFail
holen das erste Ergebnis der Abfrage. Wenn kein Ergebnis gefunden wird, wird die Ausnahme Illuminate\Database\Eloquent\ModelNotFoundException
ausgelöst:
$model = app\model\Flight::findOrFail(1);
$model = app\model\Flight::where('legs', '>', 100)->firstOrFail();
Sammlung abrufen
Du kannst auch die Methoden count
, sum
und max
des Abfrage-Builders sowie andere Sammlungsmethoden verwenden, um mit Sammlungen zu arbeiten. Diese Methoden geben nur den entsprechenden Skalarewert und nicht eine Modellinstanz zurück:
$count = app\model\Flight::where('active', 1)->count();
$max = app\model\Flight::where('active', 1)->max('price');
Einfügen
Um einen Datensatz in die Datenbank einzufügen, musst du eine neue Modellinstanz erstellen, die Instanz mit Eigenschaften versehen und dann die Methode save
aufrufen:
<?php
namespace app\controller;
use app\model\User;
use support\Request;
use support\Response;
class FooController
{
/**
* Fügt in der Benutzertabelle einen neuen Datensatz hinzu
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
// Anfrage validieren
$user = new User;
$user->name = $request->get('name');
$user->save();
}
}
created_at
und updated_at
Zeitstempel werden automatisch gesetzt (wenn die $timestamps
-Eigenschaft im Modell auf true steht), es ist also keine manuelle Zuweisung erforderlich.
Aktualisieren
Die Methode save
kann ebenfalls verwendet werden, um bereits bestehende Modelle in der Datenbank zu aktualisieren. Um ein Modell zu aktualisieren, musst du es zunächst abrufen, die Eigenschaft setzen, die aktualisiert werden soll, und dann die Methode save
aufrufen. Auch hier wird der updated_at
Zeitstempel automatisch aktualisiert, sodass keine manuelle Zuweisung erforderlich ist:
$user = app\model\User::find(1);
$user->name = 'jerry';
$user->save();
Batchaktualisierung
app\model\User::where('uid', '>', 10)
->update(['name' => 'tom']);
Überprüfung von Eigenschaftenänderungen
Eloquent bietet die Methoden isDirty
, isClean
und wasChanged
, um den internen Zustand des Modells zu überprüfen und festzustellen, wie sich dessen Eigenschaften seit dem ursprünglichen Laden geändert haben.
Die Methode isDirty
bestimmt, ob seit dem Laden des Modells eine der Eigenschaften geändert wurde. Du kannst spezifische Eigenschaftsnamen übergeben, um zu überprüfen, ob eine bestimmte Eigenschaft verändert wurde. isClean
funktioniert umgekehrt und akzeptiert ebenfalls optionale Eigenschaftsparameter:
$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
Die Methode wasChanged
stellt fest, ob während des aktuellen Anforderungszyklus bei der letzten Speicherung des Modells eine der Eigenschaften geändert wurde. Ebenso kannst du Eigenschaftsnamen übergeben, um zu überprüfen, ob eine bestimmte Eigenschaft geändert wurde:
$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
Batch-Zuweisung
Du kannst auch die Methode create
verwenden, um ein neues Modell zu speichern. Diese Methode gibt die Modellinstanz zurück. Wenn du dies tust, musst du vorher die Eigenschaften fillable
oder guarded
im Modell angeben, da alle Eloquent-Modelle standardmäßig nicht für Batch-Zuweisung konfiguriert sind.
Batch-Zuweisungsanfälligkeit entsteht, wenn Benutzer unerwartete HTTP-Parameter über die Anfrage senden und diese Parameter Felder in der Datenbank ändern, die du nicht ändern möchtest. Zum Beispiel könnte ein böswilliger Benutzer den Parameter is_admin
über den HTTP-Anruf senden und ihn an die create
-Methode übergeben, was es dem Benutzer erlaubt, sich selbst zum Administrator zu machen.
Daher solltest du zu Beginn festlegen, welche Eigenschaften im Modell für die Batch-Zuweisung zulässig sind. Du kannst dies über die $fillable
-Eigenschaft im Modell erreichen. Zum Beispiel, um die name
-Eigenschaft des Flight-Modells für die Batch-Zuweisung zu erlauben:
<?php
namespace app\model;
use support\Model;
class Flight extends Model
{
/**
* Eigenschaften, die für die Batch-Zuweisung zulässig sind.
*
* @var array
*/
protected $fillable = ['name'];
}
Sobald wir die zulässigen Eigenschaften für die Batch-Zuweisung festgelegt haben, können wir die create
-Methode verwenden, um neue Daten in die Datenbank einzufügen. Die create
-Methode gibt die gespeicherte Modellinstanz zurück:
$flight = app\model\Flight::create(['name' => 'Flight 10']);
Wenn du bereits eine Modellinstanz hast, kannst du ein Array an die fill
-Methode übergeben, um die Werte zuzuweisen:
$flight->fill(['name' => 'Flight 22']);
$fillable
kann als „Whitelist“ für die Batch-Zuweisung betrachtet werden; du kannst auch die $guarded
-Eigenschaft verwenden. Die $guarded
-Eigenschaft enthält ein Array von Eigenschaften, die nicht für die Batch-Zuweisung zulässig sind. Das bedeutet, dass $guarded
funktional eher einer „Blacklist“ ähnelt. Hinweis: Du kannst entweder $fillable
oder $guarded
verwenden, aber nicht beides gleichzeitig. Im folgenden Beispiel sind alle Eigenschaften, außer price
, für die Batch-Zuweisung zulässig:
<?php
namespace app\model;
use support\Model;
class Flight extends Model
{
/**
* Eigenschaften, die nicht für die Batch-Zuweisung zulässig sind.
*
* @var array
*/
protected $guarded = ['price'];
}
Wenn du möchtest, dass alle Eigenschaften für die Batch-Zuweisung zulässig sind, kannst du $guarded
auf ein leeres Array setzen:
/**
* Eigenschaften, die nicht für die Batch-Zuweisung zulässig sind.
*
* @var array
*/
protected $guarded = [];
Weitere Erstellmethoden
firstOrCreate
/ firstOrNew
Hier sind zwei Methoden, die du zur Batch-Zuweisung verwenden kannst: firstOrCreate
und firstOrNew
. Die firstOrCreate
-Methode sucht in der Datenbank nach den gegebenen Schlüssel/Wert-Paaren. Wenn das Modell in der Datenbank nicht gefunden wird, wird ein Datensatz eingefügt, der die Eigenschaften des ersten Parameters sowie die optionalen Eigenschaften des zweiten Parameters enthält.
Die firstOrNew
-Methode versucht, wie die firstOrCreate
-Methode, einen Datensatz in der Datenbank zu finden, gibt jedoch eine neue Modellinstanz zurück, wenn firstOrNew
kein entsprechendes Modell findet. Beachte, dass die von firstOrNew
zurückgegebene Modellinstanz noch nicht in der Datenbank gespeichert ist; du musst manuell die Methode save
aufrufen, um sie zu speichern:
// Suche nach dem Flug anhand des Namens, wenn nicht vorhanden, dann erstelle...
$flight = app\model\Flight::firstOrCreate(['name' => 'Flight 10']);
// Suche nach dem Flug anhand des Namens oder erstelle ihn mit den Eigenschaften delayed und arrival_time...
$flight = app\model\Flight::firstOrCreate(
['name' => 'Flight 10'],
['delayed' => 1, 'arrival_time' => '11:30']
);
// Suche nach dem Flug anhand des Namens, wenn nicht vorhanden, dann erstelle eine Instanz...
$flight = app\model\Flight::firstOrNew(['name' => 'Flight 10']);
// Suche nach dem Flug anhand des Namens oder spezifizieren erstelle eine Modellinstanz mit den erforderlichen Eigenschaften...
$flight = app\model\Flight::firstOrNew(
['name' => 'Flight 10'],
['delayed' => 1, 'arrival_time' => '11:30']
);
Möglicherweise hast du auch das Bedürfnis, ein vorhandenes Modell zu aktualisieren oder ein neues Modell zu erstellen, wenn es nicht existiert. Die Methode updateOrCreate
kann dies in einem Schritt erreichen. Ähnlich wie die firstOrCreate
-Methode persistiert updateOrCreate
das Modell, sodass keine separate save()
-Methode aufgerufen werden muss:
// Wenn es einen Flug von Oakland nach San Diego gibt, setze den Preis auf 99 Dollar.
// Wenn kein passendes Modell gefunden wird, erstelle eines.
$flight = app\model\Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
Modell löschen
Du kannst die Methode delete
auf einer Modellinstanz aufrufen, um die Instanz zu löschen:
$flight = app\model\Flight::find(1);
$flight->delete();
Modell durch Primärschlüssel löschen
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]));
Modelle durch Abfragen löschen
$deletedRows = app\model\Flight::where('active', 0)->delete();
Modell kopieren
Du kannst die Methode replicate
verwenden, um eine neue, noch nicht in der Datenbank gespeicherte Instanz zu erstellen, die nützlich ist, wenn viele Eigenschaften eines Modellinstances identisch sind.
$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();
Modellvergleich
Manchmal musst du feststellen, ob zwei Modelle „gleich“ sind. Die Methode is
kann verwendet werden, um schnell zu überprüfen, ob zwei Modelle denselben Primärschlüssel, Tabelle und Datenbankverbindung haben:
if ($post->is($anotherPost)) {
//
}
Modellbeobachter
Siehe die Referenz zu Modellereignissen und Observern in Laravel
Hinweis: Damit Eloquent ORM Modellbeobachter unterstützt, muss zusätzlich composer require "illuminate/events"
importiert werden.
<?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);
}
}
Transaktionen
Siehe Datenbanktransaktionen