makeRelatedPublications()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 43
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 19
nc 6
nop 1
dl 0
loc 43
rs 9.0111
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\Publications\Views\Components;
6
7
use Hyde\Hyde;
8
use Hyde\Publications\Concerns\PublicationFieldTypes;
9
use Hyde\Publications\Models\PublicationFieldDefinition;
10
use Hyde\Publications\Pages\PublicationPage;
11
use Hyde\Publications\Publications;
12
use Illuminate\Contracts\View\View;
13
use Illuminate\Support\Collection;
14
use Illuminate\View\Component;
15
16
use function collect;
17
use function count;
18
use function view;
19
20
class RelatedPublicationsComponent extends Component
21
{
22
    /** @var Collection<string, PublicationPage> */
23
    public Collection $relatedPublications;
24
25
    public string $title = 'Related Publications';
26
27
    public function __construct(string $title = 'Related Publications', int $limit = 5)
28
    {
29
        $this->relatedPublications = collect($this->makeRelatedPublications($limit));
0 ignored issues
show
Bug introduced by
$this->makeRelatedPublications($limit) of type array 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

29
        $this->relatedPublications = collect(/** @scrutinizer ignore-type */ $this->makeRelatedPublications($limit));
Loading history...
30
        $this->title = $title;
31
    }
32
33
    /** @interitDoc */
34
    public function render(): View
35
    {
36
        return view('hyde-publications::components.related-publications');
37
    }
38
39
    protected function makeRelatedPublications(int $limit = 5): array
40
    {
41
        // Get current publicationType from the current page
42
        $currentHydePage = Hyde::currentRoute()->getPage();
0 ignored issues
show
Bug introduced by
The method currentRoute() does not exist on Hyde\Hyde. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

42
        $currentHydePage = Hyde::/** @scrutinizer ignore-call */ currentRoute()->getPage();
Loading history...
43
44
        // If not a publication page, exit early
45
        if (! $currentHydePage instanceof PublicationPage) {
46
            return [];
47
        }
48
49
        $publicationType = $currentHydePage->getType();
50
51
        // Get the tag fields for the current publicationType or exit early if there aren't any
52
        $publicationTypeTagFields = $publicationType->getFields()->filter(function (PublicationFieldDefinition $field): bool {
53
            return $field->type === PublicationFieldTypes::Tag;
0 ignored issues
show
Deprecated Code introduced by
The constant Hyde\Publications\Concer...licationFieldTypes::Tag has been deprecated: May be renamed to Tags to better fit usage ( Ignorable by Annotation )

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

53
            return $field->type === /** @scrutinizer ignore-deprecated */ PublicationFieldTypes::Tag;

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
54
        });
55
        if ($publicationTypeTagFields->isEmpty()) {
56
            return [];
57
        }
58
59
        // Get a list of all pages for this page's publicationType: 1 means we only have current page & no related pages exist
60
        $publicationPages = Publications::getPublicationsForType($publicationType)->keyBy('identifier');
61
        if ($publicationPages->count() <= 1) {
62
            return [];
63
        }
64
65
        // Get all tags for the current page
66
        $currentPageTags = $this->getTagsForPage($publicationPages->get($currentHydePage->getIdentifier()), $publicationTypeTagFields);
67
        if ($currentPageTags->isEmpty()) {
68
            return [];
69
        }
70
71
        // Forget the current page pages since we don't want to show it as a related page against itself
72
        $publicationPages->forget($currentHydePage->getIdentifier());
73
74
        // Get all related pages
75
        $allRelatedPages = $this->getAllRelatedPages($publicationPages, $publicationTypeTagFields, $currentPageTags);
76
        if ($allRelatedPages->isEmpty()) {
77
            return [];
78
        }
79
80
        // Sort them by relevance (count of shared tags & newest dates)
81
        return $this->sortRelatedPagesByRelevance($allRelatedPages, $limit)->all();
82
    }
83
84
    protected function getTagsForPage(PublicationPage $publicationPage, Collection $tagFields): Collection
85
    {
86
        $thisPageTags = collect();
87
88
        // There could be multiple tag fields, but most publication types will only have one
89
        foreach ($tagFields as $tagField) {
90
            $thisPageTags = $thisPageTags->merge($publicationPage->matter->get($tagField->name, []));
91
        }
92
93
        return $thisPageTags;
94
    }
95
96
    protected function getAllRelatedPages(Collection $publicationPages, Collection $tagFields, Collection $currPageTags): Collection
97
    {
98
        $allRelatedPages = collect();
99
100
        foreach ($publicationPages as $publicationPage) {
101
            $publicationPageTags = $this->getTagsForPage($publicationPage, $tagFields);
102
            $matchedTagCount = $publicationPageTags->intersect($currPageTags)->count();
103
104
            // We have shared/matching tags, add this page info to $allRelatedPages
105
            if ($matchedTagCount) {
106
                $allRelatedPages->add(
107
                    collect([
0 ignored issues
show
Bug introduced by
array('count' => $matche...e' => $publicationPage) of type array<string,integer|mixed> 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

107
                    collect(/** @scrutinizer ignore-type */ [
Loading history...
108
                        'count' => $matchedTagCount,
109
                        'identifier' => $publicationPage->identifier,
110
                        'page' => $publicationPage,
111
                    ])
112
                );
113
            }
114
        }
115
116
        return $allRelatedPages;
117
    }
118
119
    protected function sortRelatedPagesByRelevance(Collection $allRelatedPages, int $max): Collection
120
    {
121
        $relatedPages = collect();
122
123
        // Group related pages by the number of shared tags and then sort by keys (number of shared tags) descending
124
        $allRelatedPagesGrouped = $allRelatedPages->groupBy('count')->sortKeysDesc(SORT_NUMERIC);
125
126
        // Iterate over groups
127
        foreach ($allRelatedPagesGrouped as $relatedPagesGroup) {
128
            // Sort group by recency, with the latest pages first
129
            $sortedPageGroup = $relatedPagesGroup->sortByDesc('page.matter.__createdAt');
130
131
            // Now add to $relatedPages, and stop when hitting $max
132
            foreach ($sortedPageGroup as $page) {
133
                $relatedPages->put($page['identifier'], $page['page']);
134
                if (count($relatedPages) >= $max) {
135
                    break 2;
136
                }
137
            }
138
        }
139
140
        return $relatedPages;
141
    }
142
}
143