. */ namespace Poniverse\Ponyfm\Models; use Illuminate\Database\Eloquent\Model; /** * Poniverse\Ponyfm\Models\Activity * * @property integer $id * @property \Carbon\Carbon $created_at * @property integer $user_id * @property boolean $activity_type * @property boolean $resource_type * @property integer $resource_id * @property-read \Illuminate\Database\Eloquent\Collection|\Poniverse\Ponyfm\Models\Notification[] $notifications * @property-read \Poniverse\Ponyfm\Models\User $initiatingUser * @property-read \Poniverse\Ponyfm\Models\Activity $resource * @property-read mixed $url * @property-read mixed $thumbnail_url * @property-read mixed $text */ class Activity extends Model { public $timestamps = false; protected $dates = ['created_at']; protected $fillable = ['created_at', 'user_id', 'activity_type', 'resource_type', 'resource_id']; protected $appends = ['url', 'thumbnail_url', 'human_friendly_resource_type']; protected $casts = [ 'id' => 'integer', 'created_at' => 'datetime', 'user_id' => 'integer', 'activity_type' => 'integer', // resource_type has its own accessor and mutator 'resource_id' => 'integer', ]; const TYPE_NEWS = 1; const TYPE_PUBLISHED_TRACK = 2; const TYPE_PUBLISHED_ALBUM = 3; const TYPE_PUBLISHED_PLAYLIST = 4; const TYPE_NEW_FOLLOWER = 5; const TYPE_NEW_COMMENT = 6; const TYPE_CONTENT_FAVOURITED = 7; /** * These "target" constants are an implementation detail of this model and * should not be used directly in other classes. They're used to efficiently * store the type of resource this notification is about in the database. * * The "resource_type" attribute is transformed into a class name at runtime * so that the use of an integer in the database to represent this info * remains an implementation detail of this model. Outside of this class, * the resource_type attribute should be treated as a fully-qualified class * name. */ const TARGET_USER = 1; const TARGET_TRACK = 2; const TARGET_ALBUM = 3; const TARGET_PLAYLIST = 4; const TARGET_COMMENT = 5; public function initiatingUser() { return $this->belongsTo(User::class, 'user_id', 'id'); } public function notifications() { return $this->hasMany(Notification::class, 'activity_id', 'id'); } public function notificationRecipients() { return $this->hasManyThrough(User::class, Notification::class, 'activity_id', 'user_id', 'id'); } public function resource() { return $this->morphTo('resource', 'resource_type', 'resource_id'); } public function getUrlAttribute() { return $this->resource->url; } public function getResourceTypeAttribute($value) { switch ($value) { case static::TARGET_USER: return User::class; case static::TARGET_TRACK: return Track::class; case static::TARGET_ALBUM: return Album::class; case static::TARGET_PLAYLIST: return Playlist::class; case static::TARGET_COMMENT: return Comment::class; default: // Null must be returned here for Eloquent's eager-loading // of the polymorphic relation to work. return NULL; } } public function setResourceTypeAttribute($value) { switch ($value) { case User::class: $this->attributes['resource_type'] = static::TARGET_USER; break; case Track::class: $this->attributes['resource_type'] = static::TARGET_TRACK; break; case Album::class: $this->attributes['resource_type'] = static::TARGET_ALBUM; break; case Playlist::class: $this->attributes['resource_type'] = static::TARGET_PLAYLIST; break; case Comment::class: $this->attributes['resource_type'] = static::TARGET_COMMENT; break; } } public function getThumbnailUrlAttribute() { switch ($this->resource_type) { case User::class: return $this->resource->getAvatarUrl(Image::THUMBNAIL); case Track::class: case Album::class: case Playlist::class: return $this->resource->getCoverUrl(Image::THUMBNAIL); case Comment::class: return $this->resource->user->getAvatarUrl(Image::THUMBNAIL); default: throw new \Exception('This activity\'s resource is of an unknown type!'); } } /** * @return string human-readable Markdown string describing this notification * @throws \Exception */ public function getTextAttribute() { switch ($this->activity_type) { case static::TYPE_NEWS: // not implemented yet throw new \InvalidArgumentException('This type of activity has not been implemented yet!'); case static::TYPE_PUBLISHED_TRACK: return "{$this->resource->user->display_name} published a new track, __{$this->resource->title}__!"; case static::TYPE_PUBLISHED_PLAYLIST: return "{$this->resource->user->display_name} published a new playlist, __{$this->resource->title}__!"; case static::TYPE_NEW_FOLLOWER: return "{$this->initiatingUser->display_name} is now following you!"; case static::TYPE_NEW_COMMENT: // Is this a profile comment? if ($this->resource_type === User::class) { return "{$this->initiatingUser->display_name} left a comment on your profile!"; // Must be a content comment. } else { return "{$this->initiatingUser->display_name} left a comment on your {$this->resource->resource->getResourceType()}, __{$this->resource->resource->title}__!"; } case static::TYPE_CONTENT_FAVOURITED: return "{$this->initiatingUser->display_name} favourited your {$this->resource->type}, __{$this->resource->title}__!"; default: throw new \Exception('This activity\'s activity type is unknown!'); } } }