]> BookStack Code Mirror - bookstack/blob - app/Activity/ActivityQueries.php
DB: Started update of entity loading to avoid global selects
[bookstack] / app / Activity / ActivityQueries.php
1 <?php
2
3 namespace BookStack\Activity;
4
5 use BookStack\Activity\Models\Activity;
6 use BookStack\Entities\Models\Book;
7 use BookStack\Entities\Models\Chapter;
8 use BookStack\Entities\Models\Entity;
9 use BookStack\Entities\Models\Page;
10 use BookStack\Entities\Tools\MixedEntityListLoader;
11 use BookStack\Permissions\PermissionApplicator;
12 use BookStack\Users\Models\User;
13 use Illuminate\Database\Eloquent\Builder;
14 use Illuminate\Database\Eloquent\Relations\Relation;
15
16 class ActivityQueries
17 {
18     public function __construct(
19         protected PermissionApplicator $permissions,
20         protected MixedEntityListLoader $listLoader,
21     ) {
22     }
23
24     /**
25      * Gets the latest activity.
26      */
27     public function latest(int $count = 20, int $page = 0): array
28     {
29         $activityList = $this->permissions
30             ->restrictEntityRelationQuery(Activity::query(), 'activities', 'entity_id', 'entity_type')
31             ->orderBy('created_at', 'desc')
32             ->with(['user'])
33             ->skip($count * $page)
34             ->take($count)
35             ->get();
36
37         $this->listLoader->loadIntoRelations($activityList->all(), 'entity', false);
38
39         return $this->filterSimilar($activityList);
40     }
41
42     /**
43      * Gets the latest activity for an entity, Filtering out similar
44      * items to prevent a message activity list.
45      */
46     public function entityActivity(Entity $entity, int $count = 20, int $page = 1): array
47     {
48         /** @var array<string, int[]> $queryIds */
49         $queryIds = [$entity->getMorphClass() => [$entity->id]];
50
51         if ($entity instanceof Book) {
52             $queryIds[(new Chapter())->getMorphClass()] = $entity->chapters()->scopes('visible')->pluck('id');
53         }
54         if ($entity instanceof Book || $entity instanceof Chapter) {
55             $queryIds[(new Page())->getMorphClass()] = $entity->pages()->scopes('visible')->pluck('id');
56         }
57
58         $query = Activity::query();
59         $query->where(function (Builder $query) use ($queryIds) {
60             foreach ($queryIds as $morphClass => $idArr) {
61                 $query->orWhere(function (Builder $innerQuery) use ($morphClass, $idArr) {
62                     $innerQuery->where('entity_type', '=', $morphClass)
63                         ->whereIn('entity_id', $idArr);
64                 });
65             }
66         });
67
68         $activity = $query->orderBy('created_at', 'desc')
69             ->with(['entity' => function (Relation $query) {
70                 $query->withTrashed();
71             }, 'user.avatar'])
72             ->skip($count * ($page - 1))
73             ->take($count)
74             ->get();
75
76         return $this->filterSimilar($activity);
77     }
78
79     /**
80      * Get the latest activity for a user, Filtering out similar items.
81      */
82     public function userActivity(User $user, int $count = 20, int $page = 0): array
83     {
84         $activityList = $this->permissions
85             ->restrictEntityRelationQuery(Activity::query(), 'activities', 'entity_id', 'entity_type')
86             ->orderBy('created_at', 'desc')
87             ->where('user_id', '=', $user->id)
88             ->skip($count * $page)
89             ->take($count)
90             ->get();
91
92         return $this->filterSimilar($activityList);
93     }
94
95     /**
96      * Filters out similar activity.
97      *
98      * @param Activity[] $activities
99      */
100     protected function filterSimilar(iterable $activities): array
101     {
102         $newActivity = [];
103         $previousItem = null;
104
105         foreach ($activities as $activityItem) {
106             if (!$previousItem || !$activityItem->isSimilarTo($previousItem)) {
107                 $newActivity[] = $activityItem;
108             }
109
110             $previousItem = $activityItem;
111         }
112
113         return $newActivity;
114     }
115 }