webentwicklung-frage-antwort-db.com.de

Laravel Model Events - Ich bin etwas verwirrt, wohin sie gehen sollen

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:

  • Wenn ein Artikel erstellt wird
  • Wenn ein Artikel aktualisiert wird
  • Wenn ein Artikel gelöscht wird

Die docs sage ich kann Model Events verwenden und sie in der boot() Funktion von App\Providers\EventServiceProvider registrieren.

Aber das verwirrt mich, weil ...

  • Was passiert, wenn ich weitere Modelle wie Comment oder Author hinzufüge, die vollständige Sätze aller eigenen Modellereignisse benötigen? Ist die Funktion boot() von EventServiceProvider einfach absolut groß?
  • Was ist der Zweck von Laravel 'anderen' Events ? Warum sollte ich sie jemals verwenden müssen, wenn meine Ereignisse realistischerweise nur auf Modell-CRUD-Aktionen reagieren?

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!

48
Jack

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.

25
pinkal vansia

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...
}
32
The Alpha

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());
        });
  }

}

8
Borjante

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.

  • Sie erstellen ein Formular, das an Ihren Controller übermittelt wird.
  • Ihr Controller sendet die Daten aus der generierten Anforderung an einen Befehl.
  • Ihr Befehl erledigt das schwere Anheben - d. H. Erstellt einen Eintrag in der Datenbank.
  • Ihr Befehl löst dann ein Ereignis aus, das can von einem Eventhandler abgerufen werden kann.
  • Ihr Event-Handler sendet eine E-Mail oder aktualisiert etwas anderes.

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:

https://laracasts.com/lessons/laravel-5-commands

2
edcs

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.

2
user2094178

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. 

https://github.com/illuminate/database/blob/491d58b5cc4149fa73cf93d499efb292cd11c88d/Eloquent/Model.php#L1171

https://github.com/illuminate/database/blob/491d58b5cc4149fa73cf93d499efb292cd11c88d/Eloquent/Model.php#L1273

1
Ezequiel Moreno

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.

0
Martin Bean

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!

0
Anwar