Подтвердить что ты не робот

События модели Laravel - я немного смущен тем, где они предназначены

Таким образом, я вижу, что хорошее приложение Laravel должно быть очень ориентировано на модели и события.

У меня есть модель под названием Article. Я хочу отправить уведомления по электронной почте, когда произойдут следующие события:

  • Когда создается статья.
  • Когда обновляется статья
  • Когда статья удалена

Документы говорят, что я могу использовать события модели и зарегистрировать их в функции boot() App\Providers\EventServiceProvider.

Но это меня путает, потому что...

  • Что происходит, когда я добавляю дополнительные модели, такие как Comment или Author, которые нуждаются в полных наборах всех своих собственных событий модели? Будет ли единственная функция boot() EventServiceProvider просто огромной?
  • Какова цель Laravel 'other' Events? Почему я должен когда-либо использовать их, если реально мои события будут реагировать только на действия Model CRUD?

Я начинающий в Laravel, пришедший из CodeIgniter, поэтому пытаюсь обернуть голову вокруг правильного способа Laravel делать вещи. Спасибо за ваш совет!

4b9b3361

Ответ 1

Недавно я столкнулся с той же проблемой в одном из моих проектов Laravel 5, где мне пришлось регистрировать все события модели. Я решил использовать Traits. Я создал ModelEventLogger Trait и просто использовался во всех классах Model, которые необходимо было зарегистрировать. Я собираюсь изменить его в соответствии с вашими потребностями, которые приведены ниже.

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

Теперь вы можете использовать этот признак в любой модели, для которой хотите вывести события. В вашем случае в 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'];

}

Теперь в вашем app/Providers/EventServiceProvider.php, в boot() методе register обработчик событий для Article.

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

Теперь создайте класс ArticleEventHandler в каталоге app/Handlers/Events, как показано ниже,

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

}

Как вы можете видеть из разных ответов, у разных пользователей существует более одного способа обработки событий модели. Существуют также пользовательские события, которые могут быть созданы в папке "События" и могут обрабатываться в папке "Обработчик" и могут отправляться из разных мест. Надеюсь, это поможет.

Ответ 2

В вашем случае вы также можете использовать следующий подход:

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

Кроме того, вам необходимо зарегистрировать слушателей в App\Providers\EventServiceProvider:

protected $listen = [
    'article.created' => [
        'App\Handlers\Events\[email protected]',
    ],
    'article.updated' => [
        'App\Handlers\Events\[email protected]',
    ],
    'article.deleted' => [
        'App\Handlers\Events\[email protected]',
    ],
];

Также убедитесь, что вы создали обработчики в каталоге App\Handlers\Events folder/directory для обработки этого события. Например, обработчик article.created может быть таким:

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

Ответ 3

Я нашел это самым чистым способом сделать то, что вы хотите.

1.- Создайте наблюдателя для модели (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.- Создайте новый ServiceProvider (ObserversServiceProvider), не забудьте добавить его в 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());
        });
  }

}

Ответ 4

Вы можете выбрать подход Observer для работы с событиями модели. Например, вот мой 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) {}
}

Теперь, если я создаю модель продукта, ее наблюдатель будет выглядеть так:

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

Что касается слушателей, я создаю файл observers.php внутри Observers dir, и я включаю его из AppServiceProvider. Вот фрагмент из файла observers.php:

<?php

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

Все это относится к Model Events.

Если вам нужно отправить электронное письмо после создания записи, было бы более чистым использовать "другие" события Laravel, так как у вас будет выделенный класс, чтобы справиться с этим и уволить его, когда вы из контроллера.

"Другие" события будут иметь гораздо больший смысл, так как чем более автоматизировано ваше приложение, подумайте обо всех ежедневных cronjobs, которые вам понадобятся в какой-то момент. Не будет более чистого способа справиться с этим, кроме других событий.

Ответ 5

Вы отметили этот вопрос как Laravel 5, поэтому я бы предложил не использовать модельные события, так как у вас будет много дополнительного кода в ваших моделях, что может затруднить управление в будущем. Вместо этого моя рекомендация заключалась бы в использовании командной шины и событий.

Здесь представлены документы для этих функций:

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

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

Моя рекомендация заключалась бы в использовании следующего шаблона.

  • Вы создаете форму, которая отправляется на ваш контроллер.
  • Контроллер отправляет данные из запроса, сгенерированного командой.
  • Ваша команда делает тяжелую работу - т.е. создает запись в базе данных.
  • Затем ваша команда запускает событие, которое может быть обработано обработчиком событий.
  • Ваш обработчик событий делает что-то вроде отправки электронной почты или обновления чего-то другого.

Есть несколько причин, почему мне нравится этот шаблон: Концептуально ваши команды обрабатывают вещи, которые происходят прямо сейчас, и события обрабатывают вещи, которые только что произошли. Кроме того, вы можете легко поместить обработчики команд и событий в очередь для последующей обработки - это отлично подходит для отправки электронных писем, поскольку вы, как правило, не хотите делать это в режиме реального времени, поскольку они замедляют HTTP-запрос по справедливому биту. Вы также можете иметь несколько обработчиков событий для одного события, которое отлично подходит для разделения проблем.

Трудно представить какой-либо фактический код здесь, как ваш вопрос, более подробно о концепциях Laravel, поэтому я бы рекомендовал просмотреть эти видеоролики, чтобы вы получили представление о том, как работает этот шаблон:

Здесь описывается командная шина:

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

В этом разделе описывается, как работают события:

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

Ответ 6

1) Вы можете создать прослушиватель событий для каждой новой модели (ArticleEventSubscriber, CommentEventSubscriber) при загрузке:

EventServiceProvider.php

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

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

или вы также можете использовать свойство $subscribe

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

Существует множество способов прослушивания и обработки событий. Взгляните на текущую основную документацию, чтобы узнать больше способов (например, закрытие соединений): Laravel Docs (master) и этот другой ответ

2) События модели - это просто события, предоставленные по умолчанию Eloquent.

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

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

Ответ 7

У вас может быть несколько слушателей на событии. Таким образом, у вас может быть слушатель, который отправляет электронное письмо при обновлении статьи, но у вас может быть совершенно другой слушатель, который делает что-то совершенно другое - они оба исполняются.