Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/Helpers/HasAuthor.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public function author(): User
public function authoredBy(User $author)
{
$this->authorRelation()->associate($author);

$this->unsetRelation('authorRelation');
}

public function authorRelation(): BelongsTo
Expand Down
37 changes: 26 additions & 11 deletions app/Helpers/HasLikes.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,50 @@

trait HasLikes
{
/**
* @return \Illuminate\Database\Eloquent\Collection
*/
public function likes()
{
return $this->likesRelation;
}

protected static function bootHasLikes()
{
static::deleting(function ($model) {
$model->likes()->delete();
$model->likesRelation()->delete();

$model->unsetRelation('likesRelation');
});
}

public function likedBy(User $user)
{
$this->likes()->create(['user_id' => $user->id()]);
$this->likesRelation()->create(['user_id' => $user->id()]);

$this->unsetRelation('likesRelation');
}

public function dislikedBy(User $user)
{
optional($this->likes()->where('user_id', $user->id())->first())->delete();
}
optional($this->likesRelation()->where('user_id', $user->id())->first())->delete();

public function likes(): MorphMany
{
return $this->morphMany(Like::class, 'likeable');
$this->unsetRelation('likesRelation');
}

public function isLikedBy(User $user): bool
/**
* It's important to name the relationship the same as the method because otherwise
* eager loading of the polymorphic relationship will fail on queued jobs.
*
* @see https://github.com/laravelio/laravel.io/issues/350
*/
public function likesRelation(): MorphMany
{
return $this->likes()->where('user_id', $user->id())->exists();
return $this->morphMany(Like::class, 'likesRelation', 'likeable_type', 'likeable_id');
}

public function likesCount(): int
public function isLikedBy(User $user): bool
{
return $this->likes()->count();
return $this->likesRelation()->where('user_id', $user->id())->exists();
}
}
9 changes: 4 additions & 5 deletions app/Helpers/HasTags.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,19 @@ public function syncTags(array $tags)
{
$this->save();
$this->tagsRelation()->sync($tags);

$this->unsetRelation('tagsRelation');
}

public function removeTags()
{
$this->tagsRelation()->detach();

$this->unsetRelation('tagsRelation');
}

public function tagsRelation(): MorphToMany
{
return $this->morphToMany(Tag::class, 'taggable')->withTimestamps();
}

public function hasTags(): bool
{
return $this->tagsRelation()->count() > 0;
}
}
7 changes: 2 additions & 5 deletions app/Helpers/ReceivesReplies.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public function deleteReplies()
foreach ($this->repliesRelation()->get() as $reply) {
$reply->delete();
}

$this->unsetRelation('repliesRelation');
}

/**
Expand All @@ -53,9 +55,4 @@ public function isConversationOld(): bool

return $this->createdAt()->lt($sixMonthsAgo);
}

public function repliesCount(): int
{
return $this->repliesRelation()->count();
}
}
17 changes: 13 additions & 4 deletions app/Models/Article.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ final class Article extends Model
'shared_at',
];

/**
* {@inheritdoc}
*/
protected $with = [
'authorRelation',
'likesRelation',
'tagsRelation',
];

public function id(): int
{
return $this->id;
Expand Down Expand Up @@ -242,17 +251,17 @@ public function scopeRecent(Builder $query): Builder

public function scopePopular(Builder $query): Builder
{
return $query->withCount('likes')
->orderBy('likes_count', 'desc')
return $query->withCount('likesRelation')
->orderBy('likes_relation_count', 'desc')
->orderBy('submitted_at', 'desc');
}

public function scopeTrending(Builder $query): Builder
{
return $query->withCount(['likes' => function ($query) {
return $query->withCount(['likesRelation' => function ($query) {
$query->where('created_at', '>=', now()->subWeek());
}])
->orderBy('likes_count', 'desc')
->orderBy('likes_relation_count', 'desc')
->orderBy('submitted_at', 'desc');
}

Expand Down
2 changes: 1 addition & 1 deletion app/Models/Reply.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ final class Reply extends Model
* {@inheritdoc}
*/
protected $with = [
'likes',
'likesRelation',
];

public function id(): int
Expand Down
11 changes: 11 additions & 0 deletions app/Models/Thread.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ final class Thread extends Model implements ReplyAble, SubscriptionAble, Feedabl
'subject',
];

/**
* {@inheritdoc}
*/
protected $with = [
'authorRelation',
'likesRelation',
'repliesRelation',
'tagsRelation',
];

public function id(): int
{
return $this->id;
Expand Down Expand Up @@ -184,6 +194,7 @@ public static function feedQuery(): Builder
{
return static::with([
'solutionReplyRelation',
'likesRelation',
'repliesRelation',
'repliesRelation.authorRelation',
'tagsRelation',
Expand Down
28 changes: 14 additions & 14 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,20 +214,6 @@ public function series(): HasMany
return $this->hasMany(Series::class, 'author_id');
}

/**
* @todo Make this work with Eloquent instead of a collection
*/
public function countSolutions(): int
{
return $this->replies()->filter(function (Reply $reply) {
if ($reply->replyAble() instanceof Thread) {
return $reply->replyAble()->isSolutionReply($reply);
}

return false;
})->count();
}

public static function findByUsername(string $username): self
{
return static::where('username', $username)->firstOrFail();
Expand All @@ -251,6 +237,20 @@ public function delete()
parent::delete();
}

/**
* @todo Make this work with Eloquent instead of a collection
*/
public function countSolutions(): int
{
return $this->replies()->filter(function (Reply $reply) {
if ($reply->replyAble() instanceof Thread) {
return $reply->replyAble()->isSolutionReply($reply);
}

return false;
})->count();
}

public function scopeMostSolutions(Builder $query)
{
return $query->withCount(['replyAble as most_solutions' => function ($query) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddIndexToReplyableType extends Migration
{
public function up()
{
Schema::table('replies', function (Blueprint $table) {
$table->index('replyable_type');
});
}
}
2 changes: 2 additions & 0 deletions database/schema/mysql-schema.dump
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ CREATE TABLE `replies` (
PRIMARY KEY (`id`),
KEY `replies_author_id_index` (`author_id`),
KEY `replies_replyable_id_index` (`replyable_id`),
KEY `replies_replyable_type_index` (`replyable_type`),
CONSTRAINT `replies_author_id_foreign` FOREIGN KEY (`author_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
Expand Down Expand Up @@ -323,3 +324,4 @@ INSERT INTO `migrations` VALUES (55,'2020_07_16_185353_add_twitter_columns',1);
INSERT INTO `migrations` VALUES (56,'2020_10_01_093001_add_email_verified_at_column_to_users',1);
INSERT INTO `migrations` VALUES (57,'2020_11_03_205735_add_uuid_to_failed_jobs_table',1);
INSERT INTO `migrations` VALUES (58,'2020_11_22_194212_create_schedule_monitor_tables',1);
INSERT INTO `migrations` VALUES (59,'2021_03_10_161050_add_index_to_replyable_type',2);
9 changes: 5 additions & 4 deletions resources/views/forum/_thread.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
</a>

<div class="md:flex-shrink-0 md:self-center md:flex mt-2 md:mt-0">
@if ($thread->hasTags())
@if (count($tags = $thread->tags()))
<div class="flex text-sm space-x-2">
@foreach ($thread->tags() as $tag)
@foreach ($tags as $tag)
<x-badges.badge>
{{ $tag->name() }}
</x-badges.badge>
Expand All @@ -35,6 +35,7 @@
<x-avatar :user="$thread->author()" class="h-10 w-10 sm:h-5 sm:w-5 rounded-full" />
</a>
</div>

<div class="min-w-0 flex-1">
<p class="text-sm text-gray-900">
<a href="{{ route('profile', $thread->author()->username()) }}" class="font-medium hover:underline">
Expand Down Expand Up @@ -68,15 +69,15 @@ class="rounded-full p-1 bg-lio-100 text-lio-500"
<span class="inline-flex items-center text-sm">
<div class="inline-flex space-x-2 text-gray-400">
<x-heroicon-s-thumb-up class="h-5 w-5" />
<span class="font-medium text-gray-900">{{ $thread->likesCount() }}</span>
<span class="font-medium text-gray-900">{{ count($thread->likes()) }}</span>
<span class="sr-only">Likes</span>
</div>
</span>

<span class="inline-flex items-center text-sm">
<div class="inline-flex space-x-2 text-gray-400">
<x-heroicon-s-chat-alt class="h-5 w-5" />
<span class="font-medium text-gray-900">{{ $thread->repliesCount() }}</span>
<span class="font-medium text-gray-900">{{ count($thread->replies()) }}</span>
<span class="sr-only">Replies</span>
</div>
</span>
Expand Down
Loading