Laravel - globale Variable ausgeben

Werner S

Neues Mitglied
Grüß euch,

ich Arbeite mich gerade in Laravel ein und schaue mir gerade Middleware an weil ich eine globale Variable setzen will.
Leider komme ich nicht weiter da alle Versuche di Variable auszugeben fehlschlagen.

Es geht um folgende Zeile. Diese gibt mir die Anzahl aller neuen Mails aus.

PHP:
$inboxcounts = Mail::where('status', '=', '0')->where('read', '=', '0')->where('user_id', Auth::user()->id)->count();
Bis jetzt habe ich diese Zeile in jedem Controller drinne damit dies immer im Header (layout/app.blade.php) ausgegeben wird.
Gerne würde ich das nur in eine Einzelner Datei auslagern in der später auch andere globale Variablen stehen.

Wie kann ich das am besten lösen?
 

JR Cologne

Administrator
Teammitglied
Gute Frage. Da gibt es theoretisch viele verschiedene Möglichkeiten.
Auf die Idee, eine Middleware dafür einzusetzen, wäre ich jetzt eher nicht gekommen.
Da würde ich dir spontan also eher von abraten. Ist aber sicher auch irgendwie möglich.

Folgend stelle ich mal die wesentlichen Optionen dar. Je nach deinen konkreten Anforderungen kannst du dir dann die passende Variante raussuchen.

Option 1: Eloquent Accessor definieren

Ein Eloquent Accessor ist im Prinzip eine spezielle Methode, die du deinem entsprechenden Eloquent-Model hinzufügen kannst.
Das besondere an der Methode ist, dass sich dadurch praktisch ein Datenbankattribut faken lässt, was sich bei jeder Abfrage ändern kann bzw. neu berechnet wird und daher nicht gewöhnlich als Feld in der Datenbank eingetragen ist.
Wenn du dein Eloquent Model also sowieso schon an die Views, wo du die Anzahl aller neuen Mails jetzt benötigst, weitergibst, dann kannst du das Model so ganz einfach erweitern bzw. mit "on the fly" berechneten/abgefragten Werten bestücken.

Dein Fall geht allerdings etwas über den klassischen Anwendungsfall hinaus, wie er in der Doku beschrieben ist.
Mein Laravel-Wissen ist leider schon wieder ein bisschen eingestaubt, ich denke aber trotzdem, dass das folgende Code-Beispiel (auch im Kontext des Eloquent Accessor's) funktioniert.

PHP:
public function getNewMailsCountAttribute()
{
    return self::where('status', '=', '0')->where('read', '=', '0')->where('user_id', Auth::user()->id)->count();
}
Wenn du das Ganze dann noch als Attribut an dein Model anhängen lässt, kannst du das ganz normal, wie bei anderen Attributen, abrufen.

PHP:
protected $appends = [
    'new_mails_count',
];
Link zur Dokumentation: https://laravel.com/docs/7.x/eloquent-mutators#defining-an-accessor

Option 2: View::share() in einem Service Provider

Am Nächsten an deine Idee einer globalen Variable kommt wohl diese Option ran.
In Laravel sind ja gewisse Sache grundsätzlich für Views freigegeben, sodass du beispielsweise immer auf den jeweiligen authentifizierten User Zugriff hast.

Das gleiche Prinzip kannst du dir ebenfalls zunutze machen.
In einem beliebigen/eigenen Service Provider in der boot()-Methode, bspw. AppServiceProvider, kannst du über die View-Facade die Methode View::share() aufrufen und so beliebige Werte/Daten an alle Views weitergeben.

PHP:
public function boot()
{
    View::share('newMailsCount', Mail::where('status', '=', '0')->where('read', '=', '0')->where('user_id', Auth::user()->id)->count());
}
Edit: Nachträgliche Korrektur: Ein Service Provider ist bei Daten, die auf dem authentifizierten Nutzer oder sonstigen Session-Infos beruhen, nicht einsetzbar.
Diese Option funktioniert also im konkreten Beispielfall nicht, sonst ist es aber ein recht guter Weg.

Wenn der Zugriff auf Session-Daten erforderlich ist, kann stattdessen eine Middleware als Ort für den View::share()-Aufruf eingesetzt werden.
Nähere Infos im weiteren Verlauf des Themas.

Link zur Doku: https://laravel.com/docs/7.x/views#passing-data-to-views

Option 3: View Composer

View Composer ermöglichen es dir, alle benötigten Daten an einem zentralen Ort für die jeweiligen Views bereitzustellen.
Der View Composer wird entsprechend immer beim Rendern einer View aufgerufen.
Das sorgt dafür, dass die dafür nötige Logik nicht im Controller geschrieben werden muss und es gleichzeitig deutlich flexiblere und umfangreichere Möglichkeiten des Umgangs mit der Datenweitergabe an Views im Vergleich zu Option 1 und 2 gibt.

Auch hier brauchen wir erstmal wieder einen Service Provider (ViewServiceProvider), worüber wir unsere View Composer registrieren und bestimmten (oder auch allen) Views zuordnen.

PHP:
public function boot() {
    View::composer(
        [ 'view-1', 'view-2', ... ],
        MailViewComposer::class
    );
}
Die eigentliche Logik wird dann in diesem Fall als Klasse MailViewComposer abstrahiert. Alternativ wäre auch eine Closure/anonyme Funktion möglich.

PHP:
namespace App\Http\View\Composers;

class MailViewComposer
{
    protected $mail;

    public function __construct(Mail $mail)
    {
        $this->mail = $user;
    }

    public function compose(View $view)
    {
        $view->with('newMailCount', Mail::where('status', '=', '0')->where('read', '=', '0')->where('user_id', Auth::user()->id)->count());
    }
}
Link zur Doku: https://laravel.com/docs/7.x/views#view-composers


Gut, das sind, glaube ich, die wesentlichen Optionen.
Ich hoffe, ich habe keine groben Schnitzer eingebaut. Ist, wie gesagt, schon länger her, dass ich intensiver mit Laravel zu tun hatte.
Der Code versteht sich zudem natürlich nur als Ausschnitt und ist nicht vollständig.

Zum Ende noch ein kleiner Tipp, um deine Abfrage für die Anzahl der neuen Mails ein bisschen schöner zu gestalten:

Wenn du mittels where() einen Vergleich mit dem Operator = anstellst, musst du diesen nicht explizit angeben.
Dadurch, dass du die Attribute status und read wahrscheinlich als einstelliger tinyint (in Laravel-Migrations-Sprache boolean) in der DB speicherst, solltest du zudem auch ein Integer-Wert übergeben (Anführungszeichen weg).
Genauso würde wohl auch ein bool funktionieren.

PHP:
Mail::where('status', 0)->where('read', 0)->where('user_id', Auth::user()->id)->count()
Ansonsten ist die Abfrage, wenn man das an mehreren Stellen schreiben muss, recht lang.
Da bietet es sich an, eine gesonderte Methode im Eloquent Model anzulegen, z.B. so:

PHP:
public function newMailsCount(User $user) : int {
    self::where('status', 0)->where('read', 0)->where('user_id', $user->id)->count();
}
 
Zuletzt bearbeitet:

Werner S

Neues Mitglied
Morgen,

danke für die Ausführliche Antwort.

Mein Englisch ist etwas eingerostet, aber nach deinem Post wusste ich wonach ich Googeln muss was vorher zwar auch klappte aber wohl nach den Falschen begriffen.
Mein Code passt wohl am besten in die AppServiceProvider bzw. dafür scheint die wohl gemacht.

Soweit ist das alles kein Problem, allerdings wird das Auslesen der aktuellen User_id nicht unterstützt. Auth:: dies macht wohl Probleme in der AppServiceProvider und so wie es aussieht wird es keine vernünftige Lösung dafür geben.

Ich habe das ganze jetzt erst mal in eine Middleware gepackt damit die Datei nicht so überladen ist was auch funktioniert und werde jetzt deine anderen Ansätze ein mal durch gehen.

Wenn du mittels where() einen Vergleich mit dem Operator = anstellst, musst du diesen nicht explizit angeben.
Dadurch, dass du die Attribute status und read wahrscheinlich als einstelliger tinyint (in Laravel-Migrations-Sprache boolean) in der DB speicherst, solltest du zudem auch ein Integer-Wert übergeben (Anführungszeichen weg).
Genauso würde wohl auch ein bool funktionieren.

PHP:
Mail::where('status', 0)->where('read', 0)->where('user_id', Auth::user()->id)->count()
Ja, wenn man nach Jahren das erste mal Laravel benutzt, da muss man sich erst mal dran gewöhnen :D
Jetzt sind die raus.

PHP:
public function handle($request, Closure $next)
    {
        if (Auth::check()) {
            $inboxcounts = Mail::where('status', '0')->where('read', '0')->where('user_id', Auth::user()->id)->count();
            $draftscounts = Mail::where('status', '2')->where('user_id', Auth::user()->id)->count();

            View::share('inboxcounts', $inboxcounts);
            View::share('draftscounts', $draftscounts);
        }
        return $next($request);
    }
 
Zuletzt bearbeitet:

JR Cologne

Administrator
Teammitglied
Soweit ist das alles kein Problem, allerdings wird das Auslesen der aktuellen User_id nicht unterstützt. Auth:: dies macht wohl Probleme in der AppServiceProvider und so wie es aussieht wird es keine vernünftige Lösung dafür geben.
Ah, ja. Der AppServiceProvider wird natürlich ausgeführt, bevor Session-Informationen zur Verfügung stehen.

Option 2 fällt damit weg.
Für die Nutzung von View::share() und der Weitergabe von Daten, auf Basis von Session-Infos, an Views ist in dem Fall dann tatsächlich eine Middleware zu nutzen.
Habe ich so, glaube ich, selbst noch nicht verwendet. Sieht aber auch ganz gut aus und funktioniert.

Wenn die Anforderungen dann spezifischer und umfangreicher werden, würde ich allerdings definitiv auf View Composer zurückgreifen.
 
Oben Unten