So wie ich es sehe, sollte eine gute Laravel-Anwendung sehr modell- und ereignisgesteuert sein.
Ich habe ein Modell namens Article
. Ich möchte per E-Mail benachrichtigt werden, wenn folgende Ereignisse eintreten:
Die docs sage ich kann Model Events verwenden und sie in der boot()
Funktion von App\Providers\EventServiceProvider
registrieren.
Aber das verwirrt mich, weil ...
Comment
oder Author
hinzufüge, die vollständige Sätze aller eigenen Modellereignisse benötigen? Ist die Funktion boot()
von EventServiceProvider
einfach absolut groß?Ich bin ein Anfänger in Laravel, weil ich von CodeIgniter gekommen bin, und versuche, meinen Kopf um die richtige Art und Weise der Laravel-Arbeit zu wickeln. Danke für deinen Rat!
Kürzlich kam ich in einem meiner Laravel 5-Projekte zum selben Problem, bei dem ich alle Modellereignisse protokollieren musste. Ich entschied mich für Traits
. Ich habe ModelEventLogger
Trait erstellt und einfach in allen Model-Klassen verwendet, die protokolliert werden mussten. Ich werde es je nach Bedarf ändern.
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Event;
/**
* Class ModelEventThrower
* @package App\Traits
*
* Automatically throw Add, Update, Delete events of Model.
*/
trait ModelEventThrower {
/**
* Automatically boot with Model, and register Events handler.
*/
protected static function bootModelEventThrower()
{
foreach (static::getModelEvents() as $eventName) {
static::$eventName(function (Model $model) use ($eventName) {
try {
$reflect = new \ReflectionClass($model);
Event::fire(strtolower($reflect->getShortName()).'.'.$eventName, $model);
} catch (\Exception $e) {
return true;
}
});
}
}
/**
* Set the default events to be recorded if the $recordEvents
* property does not exist on the model.
*
* @return array
*/
protected static function getModelEvents()
{
if (isset(static::$recordEvents)) {
return static::$recordEvents;
}
return [
'created',
'updated',
'deleted',
];
}
}
Jetzt können Sie diese Eigenschaft in jedem Modell verwenden, für das Sie Ereignisse auslösen möchten. In Ihrem Fall in Article
Model.
<?php namespace App;
use App\Traits\ModelEventThrower;
use Illuminate\Database\Eloquent\Model;
class Article extends Model {
use ModelEventThrower;
//Just in case you want specific events to be fired for Article model
//uncomment following line of code
// protected static $recordEvents = ['created'];
}
Jetzt in Ihrem app/Providers/EventServiceProvider.php
, in der boot()
-Methode, registrieren Sie den Event Handler für Article
.
public function boot(DispatcherContract $events)
{
parent::boot($events);
$events->subscribe('App\Handlers\Events\ArticleEventHandler');
}
Erstellen Sie nun die Klasse ArticleEventHandler
im Verzeichnis app/Handlers/Events
wie folgt:
<?php namespace App\Handlers\Events;
use App\Article;
class ArticleEventHandler{
/**
* Create the event handler.
*
* @return \App\Handlers\Events\ArticleEventHandler
*/
public function __construct()
{
//
}
/**
* Handle article.created event
*/
public function created(Article $article)
{
//Implement logic
}
/**
* Handle article.updated event
*/
public function updated(Article $article)
{
//Implement logic
}
/**
* Handle article.deleted event
*/
public function deleted(Article $article)
{
//Implement logic
}
/**
* @param $events
*/
public function subscribe($events)
{
$events->listen('article.created',
'App\Handlers\Events\[email protected]');
$events->listen('article.updated',
'App\Handlers\Events\[email protected]');
$events->listen('article.deleted',
'App\Handlers\Events\[email protected]');
}
}
Wie Sie anhand verschiedener Antworten von verschiedenen Benutzern sehen können, gibt es mehr als eine Möglichkeit, mit Modellereignissen umzugehen. Es gibt auch benutzerdefinierte Ereignisse, die im Ordner "Ereignisse" erstellt und im Ordner "Handler" behandelt werden können und von verschiedenen Orten aus gesendet werden können. Ich hoffe, es hilft.
In Ihrem Fall können Sie auch den folgenden Ansatz verwenden:
// Put this code in your Article Model
public static function boot() {
parent::boot();
static::created(function($article) {
Event::fire('article.created', $article);
});
static::updated(function($article) {
Event::fire('article.updated', $article);
});
static::deleted(function($article) {
Event::fire('article.deleted', $article);
});
}
Außerdem müssen Sie Listener in App\Providers\EventServiceProvider
registrieren:
protected $listen = [
'article.created' => [
'App\Handlers\Events\[email protected]',
],
'article.updated' => [
'App\Handlers\Events\[email protected]',
],
'article.deleted' => [
'App\Handlers\Events\[email protected]',
],
];
Stellen Sie außerdem sicher, dass Sie die Handler in App\Handlers\Events
Ordner/Verzeichnis erstellt haben, um dieses Ereignis zu behandeln. Zum Beispiel könnte article.created
-Handler so aussehen:
<?php namespace App\Handlers\Events;
use App\Article;
use App\Services\Email\Mailer; // This one I use to email as a service class
class ArticleEvents {
protected $mailer = null;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
public function articleCreated(Article $article)
{
// Implement mailer or use laravel mailer directly
$this->mailer->notifyArticleCreated($article);
}
// Other Handlers/Methods...
}
Ich fand das der sauberste Weg, um das zu tun, was Sie wollen.
1. Erstellen Sie einen Beobachter für das Modell (ArticleObserver)
use App\Article;
class ArticleObserver{
public function __construct(Article $articles){
$this->articles = $articles
}
public function created(Article $article){
// Do anything you want to do, $article is the newly created article
}
}
2. Erstellen Sie einen neuen ServiceProvider (ObserversServiceProvider) und fügen Sie ihn Ihrer config/app.php hinzu
use App\Observers\ArticleObserver;
use App\Article;
use Illuminate\Support\ServiceProvider;
class ObserversServiceProvider extends ServiceProvider
{
public function boot()
{
Article::observe($this->app->make(ArticleObserver::class));
}
public function register()
{
$this->app->bindShared(ArticleObserver::class, function()
{
return new ArticleObserver(new Article());
});
}
}
Sie haben diese Frage als Laravel 5 markiert. Daher würde ich vorschlagen, keine Modellereignisse zu verwenden, da in Ihren Modellen viel zusätzlicher Code vorhanden ist, der die Verwaltung in Zukunft möglicherweise erschwert. Stattdessen würde ich empfehlen, den Befehlsbus und die Ereignisse zu verwenden.
Hier sind die Dokumente für diese Funktionen:
http://laravel.com/docs/5.0/bus
http://laravel.com/docs/5.0/events
Meine Empfehlung wäre, das folgende Muster zu verwenden.
Es gibt einige Gründe, warum ich dieses Muster mag: Konzeptionell behandeln Ihre Befehle die Dinge, die gerade geschehen, und Ereignisse behandeln die Dinge, die gerade passiert sind. Außerdem können Sie Befehls- und Ereignishandler problemlos in eine Warteschlange stellen, um sie später bearbeiten zu können. Dies ist ideal für das Senden von E-Mails, da Sie dies normalerweise nicht in Echtzeit tun möchten, da sie die HTTP-Anforderung etwas verlangsamen. Sie können auch mehrere Ereignishandler für ein einzelnes Ereignis einrichten, was die Trennung von Bedenken erleichtert.
Es wäre schwierig, hier einen tatsächlichen Code anzugeben, da Sie mehr über die Konzepte von Laravel erfahren. Daher würde ich empfehlen, diese Videos anzuschauen, damit Sie eine Vorstellung davon bekommen, wie dieses Muster funktioniert:
Dieser beschreibt den Befehlsbus:
https://laracasts.com/lessons/laravel-5-events
Dieses beschreibt, wie Ereignisse funktionieren:
Sie können sich für den Observer-Ansatz entscheiden, um mit Modellereignissen umzugehen. Zum Beispiel ist hier meine BaseObserver
:
<?php
namespace App\Observers;
use Illuminate\Database\Eloquent\Model as Eloquent;
class BaseObserver {
public function saving(Eloquent $model) {}
public function saved(Eloquent $model) {}
public function updating(Eloquent $model) {}
public function updated(Eloquent $model) {}
public function creating(Eloquent $model) {}
public function created(Eloquent $model) {}
public function deleting(Eloquent $model) {}
public function deleted(Eloquent $model) {}
public function restoring(Eloquent $model) {}
public function restored(Eloquent $model) {}
}
Wenn ich nun ein Produktmodell erstellen möchte, würde sein Observer folgendermaßen aussehen:
<?php
namespace App\Observers;
use App\Observers\BaseObserver;
class ProductObserver extends BaseObserver {
public function creating(Eloquent $model)
{
$model->author_id = Sentry::getUser()->id;
}
public function created(Eloquent $model)
{
if(Input::hasFile('logo')) Image::make(Input::file('logo')->getRealPath())->save(public_path() ."/gfx/product/logo_{$model->id}.png");
}
public function updating(Eloquent $model)
{
$model->author_id = Sentry::getUser()->id;
}
public function updated(Eloquent $model)
{
if(Input::has('payment_types')) $model->paymentTypes()->attach(Input::get('payment_types'));
//Upload logo
$this->created($model);
}
}
In Bezug auf Listener erstelle ich eine observers.php
-Datei in Observers
dir und füge sie aus der AppServiceProvider
ein. Hier ist ein Ausschnitt aus der observers.php
-Datei:
<?php
\App\Models\Support\Ticket::observe(new \App\Observers\Support\TicketObserver);
\App\Models\Support\TicketReply::observe(new \App\Observers\Support\TicketReplyObserver);
All dies betrifft Model Events
.
Wenn Sie nach dem Erstellen eines Datensatzes eine E-Mail senden müssen, ist es sinnvoller, die "anderen" Laravel-Events zu verwenden, da Sie über eine dedizierte Klasse verfügen, die sich genau damit auseinandersetzen und sie auslösen kann, wann Sie möchten Der Controller.
Die „anderen“ Events haben viel mehr Sinn, je automatisierter Ihre App wird, denken Sie an all die täglichen Cronjobs, die Sie irgendwann brauchen werden. Es gibt keinen saubereren Weg, um mit anderen als "anderen" Ereignissen umzugehen.
1) Sie können einen Ereignis-Listener für jedes neue Modell (ArticleEventSubscriber, CommentEventSubscriber) bei der Boot-Methode erstellen:
EventServiceProvider.php
public function boot(DispatcherContract $events)
{
parent::boot($events);
$events->subscribe('App\Listeners\ArticleEventListener');
$events->subscribe('App\Listeners\CommentEventListener');
}
oder Sie können auch die $subscribe
-Eigenschaft verwenden
protected $subscribe = [
'App\Listeners\ArticleEventListener',
'App\Listeners\CommentEventListener',
];
Es gibt viele Möglichkeiten, Ereignisse zuzuhören und mit ihnen umzugehen. In der aktuellen Master-Dokumentation finden Sie weitere Möglichkeiten (wie Schließungen von Usings): Laravel Docs (Master) und diese andere Antwort
2) Modellereignisse sind nur Ereignisse, die standardmäßig von Eloquent bereitgestellt werden.
Sie können zu einem Ereignis mehrere Listener haben. Es kann also sein, dass Sie einen Listener haben, der eine E-Mail sendet, wenn ein Artikel aktualisiert wird. Möglicherweise haben Sie jedoch einen völlig anderen Listener, der etwas völlig anderes ausführt.
Ich komme vielleicht nach dem Kampf, aber wenn Sie nicht den ganzen Aufwand benötigen, um Klassen zu erweitern oder Merkmale zu erstellen, möchten Sie vielleicht diese Dateierkundungslösung ausprobieren.
Laravel 5.X Lösung
Hüten Sie sich vor dem Ordner, den Sie zum Abrufen der Modelle ausgewählt haben, und sollten nur Modelle enthalten, damit diese Lösung funktioniert
Vergessen Sie nicht, den use File
hinzuzufügen
app/Providers/AppServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use File;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$model_location = base_path() . '/app'; // Change to wherever your models are located at
$files = File::files( $model_location );
foreach( $files as $data ) {
$model_name = "App\\" . pathinfo($data)['filename'];
$model_name::creating(function($model) {
// ...
});
$model_name::created(function($model) {
// ...
});
$model_name::updating(function($model) {
// ...
});
$model_name::updated(function($model) {
// ...
});
$model_name::deleting(function($model) {
// ...
});
$model_name::deleted(function($model) {
// ...
});
$model_name::saving(function($model) {
// ...
});
$model_name::saved(function($model) {
// ...
});
}
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}
Ich hoffe, es hilft Ihnen, weniger Code zu schreiben!