2015-05-19 3 views
40

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

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

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

The docs сказать, что я могу использовать Model Events и зарегистрировать их в функции boot() от App\Providers\EventServiceProvider.

Но это сбивает с толку меня, потому что ...

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

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

ответ

22

Недавно я пришел к той же проблеме в одном из моих проектов Laravel 5, где я должен был регистрировать все события Model. Я решил использовать 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 Модель.

<?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() методе зарегистрировать обработчик событий для 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]'); 
} 

} 

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

+1

Я думаю, что вы можете смело перемещать '$ reflect = new \ ReflectionClass ($ model);' в 'bootModelEventThrower()' вне '' foreach' '' static :: class' вместо '$ model'. Вы также можете получить короткое имя с помощью 'substr (static :: class, strrpos (static :: class, '\\') + 1)'. – Parziphal

+0

@ pinkel-vansia элегантный способ генерации общих событий модели. Я также соглашаюсь на @renocor, я использовал функцию function (Model $ model) ($ reflection, $ eventName) '. – raphael

9

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

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), не забудьте добавить его к вам конфигурации/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()); 
 
     }); 
 
    } 
 

 
}

0

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

1

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) и this other answer

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

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

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

23

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

// 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 /, чтобы обработать это событие. Например, 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... 
} 
2

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

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

2

Вы отметили этот вопрос как 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

+2

Шина больше не существует для 5.1. Laravel слишком сильно меняется. Каким будет подход в 5.1 или 5.2? – Parziphal

+0

Он по-прежнему существует, команды называются теперь поставленными в очередь заданиями. Они не должны запускаться в очереди, хотя они могут просто работать синхронно. – edcs

+0

вы воспитываете хорошую точку неблокирующих событий, но это сделает вашу вставку неблокирующей, если вы ее поставили в очередь. Сами события должны быть неблокируемыми. Я думаю, что все немного разозлились о Commands and Services на секунду с Laravel ... они полезны, но обязательно просто для добавления события –