Here is my migration
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateThreadsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('threads', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('slug')->unique()->nullable();
$table->unsignedInteger('user_id');
$table->unsignedInteger('channel_id');
$table->unsignedInteger('replies_count')->default(0);
$table->unsignedInteger('visits')->default(0);
$table->string('title');
$table->text('body');
$table->boolean('locked')->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('threads');
}
}
And here is my model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use App\Filters\ThreadFilters;
use App\Notifications\ThreadWasUpdated;
use App\Events\ThreadReceviedNewReply;
class Thread extends Model
{
use RecordsActivity;
protected $guarded = [];
protected $with = ['creator', 'channel'];
protected $appends = ['isSubscribedTo'];
protected $casts = [
'locked' => 'boolean'
];
protected static function boot() {
parent::boot();
static::addGlobalScope('replyCount', function ($builder) {
$builder->withCount('replies');
});
static::deleting(function ($thread) {
$thread->replies->each->delete();
});
static::created(function ($thread) {
$thread->update(['slug' => $thread->title]);
});
}
public function path()
{
return "/fréttir/{$this->channel->slug}/{$this->slug}";
}
public function creator()
{
return $this->belongsTo(User::class, 'user_id');
}
public function replies()
{
return $this->hasMany(Reply::class);
}
public function addReply($reply)
{
$reply = $this->replies()->create($reply);
event(new ThreadReceviedNewReply($reply));
return $reply;
}
public function channel()
{
return $this->belongsTo(Channel::class);
}
public function scopeFilter($query, ThreadFilters $filters)
{
return $filters->apply($query);
}
public function subscribe($userId = null)
{
$this->subscriptions()->create([
'user_id' => $userId ?: auth()->id()
]);
return $this;
}
public function unsubscribe($userId = null)
{
$this->subscriptions()
->where('user_id', $userId ?: auth()->id())
->delete();
}
public function subscriptions()
{
return $this->hasMany(ThreadSubscription::class);
}
public function getIsSubscribedToAttribute()
{
return $this->subscriptions()
->where('user_id', auth()->id())
->exists();
}
public function hasUpdatedFor($user = null)
{
$key = $user->visitedThreadCacheKey($this);
return $this->updated_at > cache($key);
}
public function getRouteKeyName()
{
return 'slug';
}
public function setSlugAttribute($value)
{
$slug = str_slug($value);
$original = $slug;
$count = 2;
while (static::whereSlug($slug)->exists()) {
$slug = "{$original}-" . $count++;
}
$this->attributes['slug'] = $slug;
}
public function getBodyAttribute($body)
{
return \Purify::clean($body);
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
class Reply extends Model
{
use Favoritable, RecordsActivity;
protected $guarded = [];
protected $with = ['owner', 'favorites'];
protected $appends = ['favoritesCount', 'isFavorited'];
public function owner()
{
return $this->belongsTo(User::class, 'user_id');
}
public function thread()
{
return $this->belongsTo(Thread::class);
}
public function wasJustPublished()
{
return $this->created_at->gt(Carbon::now()->subMinute());
}
public function mentionedUsers()
{
preg_match_all("/@([\w\-]+)/", $this->body, $matches);
return $matches[1];
}
public function path()
{
return $this->thread->path() . "#athugarsemd-{$this->id}";
}
public function setBodyAttribute($body)
{
$this->attributes['body'] = preg_replace('/@([\w\-]+)/', '<a href="/notendur/">LARACASTS_SNIPPET_PLACEHOLDER</a>', $body);
}
public function getBodyAttribute($body)
{
return \Purify::clean($body);
}
}