web-dev-qa-db-fra.com

Laravel Model Events - Je ne sais pas trop où ils doivent aller

Donc, à mon avis, une bonne application Laravel devrait être très axée sur les modèles et les événements.

J'ai un modèle appelé Article. Je souhaite envoyer des alertes par courrier électronique lorsque les événements suivants se produisent:

  • Quand un article est créé
  • Quand un article est mis à jour
  • Lorsqu'un article est supprimé

Les documents dis que je peux utiliser les événements de modèle et les enregistrer dans la fonction boot() de App\Providers\EventServiceProvider.

Mais cela me trouble parce que ...

  • Que se passe-t-il lorsque j'ajoute d'autres modèles comme Comment ou Author qui nécessitent des ensembles complets de tous leurs propres événements de modèle? La fonction boot() de EventServiceProvider sera-t-elle absolument énorme?
  • Quel est le but de les "autres" événements de Laravel ? Pourquoi aurais-je besoin de les utiliser si, de manière réaliste, mes événements ne répondent qu'aux actions de Model CRUD?

Je suis un débutant chez Laravel, étant issu de CodeIgniter. J'essaie donc de comprendre ce que Laravel a fait de bien. Merci pour vos conseils!

48
Jack

Récemment, j'ai rencontré le même problème dans l'un de mes projets Laravel 5, où je devais enregistrer tous les événements de modèle. J'ai décidé d'utiliser Traits. J'ai créé ModelEventLogger Trait et je l'ai simplement utilisé dans toutes les classes de modèles devant être consignées. Je vais le changer selon votre besoin qui est donné ci-dessous.

<?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',
        ];
    }
} 

Vous pouvez maintenant utiliser ce trait dans n'importe quel modèle pour lequel vous souhaitez générer des événements. Dans votre cas, dans le modèle Article.

<?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'];

}

Maintenant, dans votre méthode app/Providers/EventServiceProvider.php, dans la méthode boot(), enregistrez le gestionnaire d’événements pour Article.

 public function boot(DispatcherContract $events)
 {
     parent::boot($events);
     $events->subscribe('App\Handlers\Events\ArticleEventHandler');
 }

Maintenant, créez la classe ArticleEventHandler dans le répertoire app/Handlers/Events comme ci-dessous,

<?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\ArticleEventHandler@created');
     $events->listen('article.updated',
            'App\Handlers\Events\ArticleEventHandler@updated');
     $events->listen('article.deleted',
            'App\Handlers\Events\ArticleEventHandler@deleted');
 }

}

Comme vous pouvez le constater à partir de réponses différentes, d'utilisateurs différents, il existe plus d'une façon de gérer les événements de modèle. Il existe également des événements personnalisés pouvant être créés dans le dossier Evénements, gérés dans le dossier Handler et distribués depuis différents emplacements. J'espère que ça aide.

25
pinkal vansia

Dans votre cas, vous pouvez également utiliser l'approche suivante:

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

De plus, vous devez enregistrer les écouteurs dans App\Providers\EventServiceProvider:

protected $listen = [
    'article.created' => [
        'App\Handlers\Events\ArticleEvents@articleCreated',
    ],
    'article.updated' => [
        'App\Handlers\Events\ArticleEvents@articleUpdated',
    ],
    'article.deleted' => [
        'App\Handlers\Events\ArticleEvents@articleDeleted',
    ],
];

Assurez-vous également que vous avez créé les gestionnaires dans le dossier/répertoire App\Handlers\Events pour gérer cet événement. Par exemple, article.created handler pourrait être comme ceci:

<?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

J'ai trouvé que c'était la façon la plus propre de faire ce que vous voulez.

1.- Créer un observateur pour le modèle (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.- Créez un nouveau fournisseur de services (ObserversServiceProvider), n'oubliez pas de l'ajouter à vous config/app.php

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

Comme vous avez qualifié cette question de Laravel 5, je vous suggère donc de ne pas utiliser d'événements de modèle, car vous obtiendrez beaucoup de code supplémentaire dans vos modèles, ce qui risque de compliquer la gestion à l'avenir. Au lieu de cela, ma recommandation serait d'utiliser le bus de commande et les événements.

Voici la documentation pour ces fonctionnalités:

http://laravel.com/docs/5.0/bus

http://laravel.com/docs/5.0/events

Ma recommandation serait d'utiliser le modèle suivant.

  • Vous créez un formulaire qui est soumis à votre contrôleur.
  • Votre contrôleur envoie les données de la demande générée à une commande.
  • Votre commande effectue le gros travail - c’est-à-dire crée une entrée dans la base de données.
  • Votre commande déclenche ensuite un événement qui peut être capturé par un gestionnaire d'événements.
  • Votre gestionnaire d’événement fait quelque chose comme envoyer un email ou mettre à jour autre chose.

Ce motif me plait pour plusieurs raisons: conceptuellement, vos commandes gèrent les événements en cours et les événements, les événements qui viennent de se produire. En outre, vous pouvez facilement placer les gestionnaires de commandes et d’événements dans une file d’attente pour pouvoir les traiter ultérieurement. C’est un excellent moyen d’envoyer des courriers électroniques, car vous ne souhaitez pas le faire en temps réel, car ils ralentissent un peu la requête HTTP. Vous pouvez également avoir plusieurs gestionnaires d'événements pour un seul événement, ce qui est idéal pour séparer les problèmes.

Il serait difficile de fournir un code réel ici car votre question porte davantage sur les concepts de Laravel, je vous recommande donc de visionner ces vidéos pour vous donner une bonne idée du fonctionnement de ce modèle:

Celui-ci décrit le bus de commande:

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

Celui-ci décrit le fonctionnement des événements:

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

2
edcs

Vous pouvez opter pour l’observateur pour gérer les événements modèles. Par exemple, voici ma 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) {}
}

Maintenant, si je veux créer un modèle de produit, son observateur ressemblerait à ceci:

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

En ce qui concerne les auditeurs, je crée un fichier observers.php dans Observers dir et je l'inclut à partir de AppServiceProvider. Voici un extrait du fichier observers.php:

<?php

\App\Models\Support\Ticket::observe(new \App\Observers\Support\TicketObserver);
\App\Models\Support\TicketReply::observe(new \App\Observers\Support\TicketReplyObserver);

Tout cela concerne Model Events.

Si vous devez envoyer un courrier électronique après la création d'un enregistrement, il serait préférable d'utiliser les "autres" événements de Laravel, car vous aurez une classe dédiée pour le gérer, et le déclencher, quand vous le souhaitez, depuis le controlle.

Les «autres» événements auront beaucoup plus d’utilité à mesure que votre application deviendra plus automatisée, pensez à tous les tâches quotidiennes dont vous aurez besoin à un moment donné. Il n'y aura pas de moyen plus propre de gérer cet événement autre que les «autres» événements.

2
user2094178

1) Vous pouvez créer un écouteur d'événement pour chaque nouveau modèle (ArticleEventSubscriber, CommentEventSubscriber) à la méthode de démarrage:

EventServiceProvider.php 

public function boot(DispatcherContract $events)
{
    parent::boot($events);

    $events->subscribe('App\Listeners\ArticleEventListener');
    $events->subscribe('App\Listeners\CommentEventListener');
}

ou vous pouvez également utiliser la propriété $subscribe

protected $subscribe = [
        'App\Listeners\ArticleEventListener',
        'App\Listeners\CommentEventListener',
    ];

Il y a plusieurs façons d'écouter et de gérer les événements. Jetez un coup d’œil à la documentation principale actuelle pour découvrir d’autres moyens (comme les fermetures d’usings) de le faire: Laravel Docs (master) et cette autre réponse

2) Les événements de modèle ne sont que des événements fournis par défaut par Eloquent. 

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

Vous pouvez avoir plusieurs auditeurs sur un événement. Vous pouvez donc avoir un auditeur qui envoie un email lorsqu'un article est mis à jour, mais vous pouvez avoir un auditeur totalement différent qui fait quelque chose de totalement différent: ils seront tous les deux exécutés.

0
Martin Bean

Je pourrais venir après la bataille, mais si vous ne voulez pas tout le travail nécessaire pour étendre les classes ou créer des traits, essayez plutôt cette solution d'exploration de fichiers.

Solution Laravel 5.X

Attention, le dossier que vous choisissez pour récupérer les modèles ne doit contenir que des modèles pour que cette solution fonctionne

N'oubliez pas d'ajouter le use File

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()
    {
        //
    }
}

J'espère que cela vous aidera à écrire le moins de code possible!

0
Anwar