Passed
Pull Request — main (#100)
by
unknown
05:33
created

PostService::getSingleNavigationPost()   B

Complexity

Conditions 6
Paths 14

Size

Total Lines 47
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 30
c 1
b 0
f 0
nc 14
nop 4
dl 0
loc 47
rs 8.8177
1
<?php
2
3
namespace CSlant\Blog\Api\Services;
4
5
use CSlant\Blog\Api\Supports\Queries\QueryPost;
6
use CSlant\Blog\Core\Http\Responses\Base\BaseHttpResponse;
7
use CSlant\Blog\Core\Models\Post;
8
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
9
use Illuminate\Support\Arr;
10
11
/**
12
 * Class PostService
13
 *
14
 * @package CSlant\Blog\Api\Services
15
 *
16
 * @method BaseHttpResponse httpResponse()
17
 */
18
class PostService
19
{
20
    /**
21
     * @param  array<string, mixed>  $filters
22
     *
23
     * @return LengthAwarePaginator<int, Post>
24
     */
25
    public function getCustomFilters(array $filters): LengthAwarePaginator
26
    {
27
        $query = Post::query()->withCount(['comments', 'likes'])->with(['comments', 'likes']);
28
29
        $query = QueryPost::setBaseCustomFilterQuery($query, $filters);
30
31
        $query = $query
32
            ->wherePublished()
33
            ->orderBy(
34
                Arr::get($filters, 'order_by', 'updated_at'),
35
                Arr::get($filters, 'order', 'desc')
36
            );
37
38
        return $query->paginate((int) $filters['per_page']);
39
    }
40
41
    /**
42
     * Get navigation posts based on content relevance
43
     * Optimized to get individual posts without fetching collections
44
     *
45
     * @param Post $currentPost
46
     * @return array{previous: null|Post, next: null|Post}
47
     */
48
    public function getNavigationPosts(Post $currentPost): array
49
    {
50
        // Load relationships if not already loaded
51
        if (!$currentPost->relationLoaded('categories')) {
52
            $currentPost->load('categories');
53
        }
54
        if (!$currentPost->relationLoaded('tags')) {
55
            $currentPost->load('tags');
56
        }
57
58
        $categoryIds = collect($currentPost->categories)->pluck('id');
0 ignored issues
show
Bug introduced by
$currentPost->categories of type CSlant\Blog\Core\Models\Category[] is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

58
        $categoryIds = collect(/** @scrutinizer ignore-type */ $currentPost->categories)->pluck('id');
Loading history...
59
        $tagIds = collect($currentPost->tags)->pluck('id');
0 ignored issues
show
Bug introduced by
$currentPost->tags of type CSlant\Blog\Core\Models\Tag[] is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

59
        $tagIds = collect(/** @scrutinizer ignore-type */ $currentPost->tags)->pluck('id');
Loading history...
60
61
        // Get previous post
62
        $previous = $this->getSingleNavigationPost($currentPost->id, $categoryIds, $tagIds);
63
64
        // Get next post (exclude the previous post if found)
65
        $excludeIds = [$currentPost->id];
66
        if ($previous) {
0 ignored issues
show
introduced by
$previous is of type CSlant\Blog\Core\Models\Post, thus it always evaluated to true.
Loading history...
67
            $excludeIds[] = $previous->id;
68
        }
69
        $next = $this->getSingleNavigationPost($currentPost->id, $categoryIds, $tagIds, $excludeIds);
70
71
        return [
72
            'previous' => $previous,
73
            'next' => $next,
74
        ];
75
    }
76
77
    /**
78
     * Get a single navigation post
79
     *
80
     * @param int $currentPostId
81
     * @param \Illuminate\Support\Collection $categoryIds
82
     * @param \Illuminate\Support\Collection $tagIds
83
     * @param array $excludeIds
84
     * @return null|Post
85
     */
86
    private function getSingleNavigationPost(int $currentPostId, $categoryIds, $tagIds, array $excludeIds = []): ?Post
87
    {
88
        if (empty($excludeIds)) {
89
            $excludeIds = [$currentPostId];
90
        }
91
92
        // Try category match first
93
        if ($categoryIds->isNotEmpty()) {
94
            $post = Post::query()
95
                ->wherePublished()
96
                ->whereNotIn('id', $excludeIds)
97
                ->whereHas('categories', function ($query) use ($categoryIds) {
98
                    $query->whereIn('categories.id', $categoryIds);
99
                })
100
                ->with(['slugable', 'categories', 'tags', 'author'])
101
                ->inRandomOrder()
102
                ->first();
103
104
            if ($post) {
105
                return $post;
106
            }
107
        }
108
109
        // Try tag match if no category match
110
        if ($tagIds->isNotEmpty()) {
111
            $post = Post::query()
112
                ->wherePublished()
113
                ->whereNotIn('id', $excludeIds)
114
                ->whereHas('tags', function ($query) use ($tagIds) {
115
                    $query->whereIn('tags.id', $tagIds);
116
                })
117
                ->with(['slugable', 'categories', 'tags', 'author'])
118
                ->inRandomOrder()
119
                ->first();
120
121
            if ($post) {
122
                return $post;
123
            }
124
        }
125
126
        // Fallback to any random post
127
        return Post::query()
128
            ->wherePublished()
129
            ->whereNotIn('id', $excludeIds)
130
            ->with(['slugable', 'categories', 'tags', 'author'])
131
            ->inRandomOrder()
132
            ->first();
133
    }
134
}
135