Действия пользователя. Часть 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, с полями:
- id пользователя (user_id);
- id темы/сообщения (subject_id);
- тип (subject_type): тема или сообщение (в таблице будет указано App\Thread или App\Reply);
- и еще одно поле type, которое будет хранить тип события и тип активности (в таблице будет указано created_thread или created_reply). Данное поле пригодится потом при выводе записей на страницу.
Далее, в файл 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 этой частью мы закончили, продолжим работу в следующей части.