it-swarm.com.ru

Laravel Model Events - я немного озадачен тем, куда они должны идти

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

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

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

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

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

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

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

48
Jack

Недавно я столкнулся с той же проблемой в одном из моих проектов 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 Модель.

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

Теперь создайте Class 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 способа обработки событий модели. Существуют также пользовательские события, которые можно создавать в папке «События», обрабатывать в папке «Обработчик» и отправлять из разных мест. Я надеюсь, что это помогает.

25
pinkal vansia

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

// 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 для обработки этого события. Например, обработчик 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...
}
32
The Alpha

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

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

}

8
Borjante

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

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

2
user2094178

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 (мастер) и этот другой ответ

2) События модели - это просто события, предоставляемые 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

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

0
Martin Bean

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

Решение Laravel 5.X

Осторожно, папка, которую вы выбираете для получения моделей, должна содержать только модели, чтобы это решение работало

Не забудьте добавить 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()
    {
        //
    }
}

Надеюсь, это поможет вам написать меньше кода!

0
Anwar