Действия пользователя. Часть 1

Заглянем в профиль пользователя. Сейчас у нас там выводится список тем, которые были размещены пользователем на сайте и больше там ничего нет. Давайте расширим функционал пользовательского профиля и включим в него вывод сообщений, которые пользователь разместил на сайте.

В этой части мы добавим новую модель Activity, которая будет связана полиморфным отношением с моделями: Reply и Thread. Также создадим трейт RecordsActivity, который будут использовать обе модели: Reply и Thread. В трейте в момент создания сообщения/темы будет создаваться новый экземпляр модели Activity, содержащий тип модели (сообщение или тема), id сообщения или темы и id пользователя.

После чего в следующей части мы добавим связь между моделями: Activity и User, благодаря чему, через модель Activity мы сможем выводить созданные данным пользователем сообщения и темы на страницу его профиля.

Схематически связанные между собой модели образуют следующую цепочку: Итак, как говорится, приступим.

Для начала создадим файл модели Activity и файл миграции. Используем команду:
php artisan make:model Activity --migration
Откроем файл миграции и в методе up() добавим следующий код:
Schema::create('activities', function (Blueprint $table) {
    $table->increments('id');
    $table->unsignedInteger('user_id')->index();
    $table->unsignedInteger('subject_id')->index();
    $table->string('subject_type', 50);
    $table->string('type', 50);            
    $table->timestamps();
});
Здесь мы создаем таблицу activities, с полями: Далее, в файл Activity.php пропишем следующий код:
namespace App;
use Illuminate\Database\Eloquent\Model;

class Activity extends Model
{
    protected $guarded = [];

    public function subject()
    {
        return $this->morphTo();
    }

    public static function feed($user, $take = 50)
    {
        return static::where('user_id', $user->id)
            ->latest()
            ->with('subject')
            ->take($take)
            ->get()
            ->groupBy(function ($activity) {
                return $activity->created_at->format('d-m-Y');
            });
    }    
} 
Здесь в методе feed() мы выбираем последние 50 записей из таблицы activities с id данного пользователя, сгруппированные по дате создания темы/сообщения. В результате мы получим объект Collection следующего вида:
Collection {
  #items: array:2 [
    "07-09-2017" => Collection {#279
      #items: array:1 [
        0 => Activity {#295
          #relations: array:1 [
            "subject" => Thread {#334}
          ]
        }
      ]
    }
    "06-09-2017" => Collection {#344}
  ]
} 
Далее создадим трейт RecordsActivity (папка app).
namespace App;

trait RecordsActivity
{
    protected static function bootRecordsActivity()
    {
        if (auth()->guest()) return;

        foreach (static::getActivitiesToRecord() as $event) {
            static::$event(function ($model) use ($event) {
                $model->recordActivity($event);
            });
        }
    }

    protected static function getActivitiesToRecord()
    {
        return ['created'];
    }

    protected function recordActivity($event)
    {
        $this->activity()->create([
            'user_id' => auth()->id(),
            'type' => $this->getActivityType($event)
        ]);
    }

    public function activity()
    {
        return $this->morphMany(Activity::class, 'subject');
    }

    protected function getActivityType($event)
    {
        $type = strtolower((new \ReflectionClass($this))->getShortName());

        return "{$event}_{$type}";
    }
} 
Несколько слов о коде выше. Так как данный трейт мы добавим в модели Reply и Thread, то первый метод bootRecordsActivity() будет запускаться во время события «created» обеих моделей. Данный метод будет запускать метод recordActivity(), который в свою очередь будет создавать новый экземпляр модели Activity, связанный с созданным сообщением или темой.

Давайте теперь добавим созданный трейт в классы моделей Reply и Thread.

Например, для Thread это будет выглядеть так:
class Thread extends Model
{
    use RecordsActivity; 
    …
}
C этой частью мы закончили, продолжим работу в следующей части.