- Updated name to align, and differentate from new 'XQueries' clases.
- Removed old sketchy base class with app resolving workarounds, to a
proper injection-based approach.
- Also fixed wrong translation text used in PageQueries.
namespace BookStack\Activity\Controllers;
-use BookStack\Entities\Queries\TopFavourites;
+use BookStack\Entities\Queries\QueryTopFavourites;
use BookStack\Entities\Tools\MixedEntityRequestHelper;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
{
$viewCount = 20;
$page = intval($request->get('page', 1));
- $favourites = (new TopFavourites())->run($viewCount + 1, (($page - 1) * $viewCount));
+ $favourites = (new QueryTopFavourites())->run($viewCount + 1, (($page - 1) * $viewCount));
$hasMoreLink = ($favourites->count() > $viewCount) ? url('/favourites?page=' . ($page + 1)) : null;
use BookStack\Activity\ActivityQueries;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Queries\EntityQueries;
-use BookStack\Entities\Queries\RecentlyViewed;
-use BookStack\Entities\Queries\TopFavourites;
+use BookStack\Entities\Queries\QueryRecentlyViewed;
+use BookStack\Entities\Queries\QueryTopFavourites;
use BookStack\Entities\Tools\PageContent;
use BookStack\Http\Controller;
use BookStack\Uploads\FaviconHandler;
/**
* Display the homepage.
*/
- public function index(Request $request, ActivityQueries $activities)
- {
+ public function index(
+ Request $request,
+ ActivityQueries $activities,
+ QueryRecentlyViewed $recentlyViewed,
+ QueryTopFavourites $topFavourites,
+ ) {
$activity = $activities->latest(10);
$draftPages = [];
$recentFactor = count($draftPages) > 0 ? 0.5 : 1;
$recents = $this->isSignedIn() ?
- (new RecentlyViewed())->run(12 * $recentFactor, 1)
+ $recentlyViewed->run(12 * $recentFactor, 1)
: $this->queries->books->visibleForList()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get();
- $favourites = (new TopFavourites())->run(6);
+ $favourites = $topFavourites->run(6);
$recentlyUpdatedPages = $this->queries->pages->visibleForList()
->where('draft', false)
->orderBy('updated_at', 'desc')
+++ /dev/null
-<?php
-
-namespace BookStack\Entities\Queries;
-
-use BookStack\Entities\EntityProvider;
-use BookStack\Entities\Tools\MixedEntityListLoader;
-use BookStack\Permissions\PermissionApplicator;
-
-abstract class EntityQuery
-{
- protected function mixedEntityListLoader(): MixedEntityListLoader
- {
- return app()->make(MixedEntityListLoader::class);
- }
-
- protected function permissionService(): PermissionApplicator
- {
- return app()->make(PermissionApplicator::class);
- }
-
- protected function entityProvider(): EntityProvider
- {
- return app()->make(EntityProvider::class);
- }
-}
->first();
if (is_null($page)) {
- throw new NotFoundException(trans('errors.chapter_not_found'));
+ throw new NotFoundException(trans('errors.page_not_found'));
}
return $page;
namespace BookStack\Entities\Queries;
use BookStack\Activity\Models\View;
+use BookStack\Entities\EntityProvider;
use BookStack\Entities\Models\BookChild;
use BookStack\Entities\Models\Entity;
+use BookStack\Permissions\PermissionApplicator;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
-class Popular extends EntityQuery
+class QueryPopular
{
+ public function __construct(
+ protected PermissionApplicator $permissions,
+ protected EntityProvider $entityProvider,
+ ) {
+ }
+
public function run(int $count, int $page, array $filterModels = null)
{
- $query = $this->permissionService()
+ $query = $this->permissions
->restrictEntityRelationQuery(View::query(), 'views', 'viewable_id', 'viewable_type')
->select('*', 'viewable_id', 'viewable_type', DB::raw('SUM(views) as view_count'))
->groupBy('viewable_id', 'viewable_type')
->orderBy('view_count', 'desc');
if ($filterModels) {
- $query->whereIn('viewable_type', $this->entityProvider()->getMorphClasses($filterModels));
+ $query->whereIn('viewable_type', $this->entityProvider->getMorphClasses($filterModels));
}
$entities = $query->with('viewable')
return $entities;
}
- protected function loadBooksForChildren(Collection $entities)
+ protected function loadBooksForChildren(Collection $entities): void
{
$bookChildren = $entities->filter(fn(Entity $entity) => $entity instanceof BookChild);
$eloquent = (new \Illuminate\Database\Eloquent\Collection($bookChildren));
namespace BookStack\Entities\Queries;
use BookStack\Activity\Models\View;
+use BookStack\Entities\Tools\MixedEntityListLoader;
+use BookStack\Permissions\PermissionApplicator;
use Illuminate\Support\Collection;
-class RecentlyViewed extends EntityQuery
+class QueryRecentlyViewed
{
+ public function __construct(
+ protected PermissionApplicator $permissions,
+ protected MixedEntityListLoader $listLoader,
+ ) {
+ }
+
public function run(int $count, int $page): Collection
{
$user = user();
return collect();
}
- $query = $this->permissionService()->restrictEntityRelationQuery(
+ $query = $this->permissions->restrictEntityRelationQuery(
View::query(),
'views',
'viewable_id',
->take($count)
->get();
- $this->mixedEntityListLoader()->loadIntoRelations($views->all(), 'viewable', false);
+ $this->listLoader->loadIntoRelations($views->all(), 'viewable', false);
return $views->pluck('viewable')->filter();
}
namespace BookStack\Entities\Queries;
use BookStack\Activity\Models\Favourite;
+use BookStack\Entities\Tools\MixedEntityListLoader;
+use BookStack\Permissions\PermissionApplicator;
use Illuminate\Database\Query\JoinClause;
-class TopFavourites extends EntityQuery
+class QueryTopFavourites
{
+ public function __construct(
+ protected PermissionApplicator $permissions,
+ protected MixedEntityListLoader $listLoader,
+ ) {
+ }
+
public function run(int $count, int $skip = 0)
{
$user = user();
return collect();
}
- $query = $this->permissionService()
+ $query = $this->permissions
->restrictEntityRelationQuery(Favourite::query(), 'favourites', 'favouritable_id', 'favouritable_type')
->select('favourites.*')
->leftJoin('views', function (JoinClause $join) {
->take($count)
->get();
- $this->mixedEntityListLoader()->loadIntoRelations($favourites->all(), 'favouritable', false);
+ $this->listLoader->loadIntoRelations($favourites->all(), 'favouritable', false);
return $favourites->pluck('favouritable')->filter();
}
namespace BookStack\Entities\Tools;
use BookStack\App\Model;
-use BookStack\Entities\EntityProvider;
+use BookStack\Entities\Queries\EntityQueries;
use Illuminate\Database\Eloquent\Relations\Relation;
class MixedEntityListLoader
{
- protected array $listAttributes = [
- 'page' => ['id', 'name', 'slug', 'book_id', 'chapter_id', 'text', 'draft'],
- 'chapter' => ['id', 'name', 'slug', 'book_id', 'description'],
- 'book' => ['id', 'name', 'slug', 'description'],
- 'bookshelf' => ['id', 'name', 'slug', 'description'],
- ];
-
public function __construct(
- protected EntityProvider $entityProvider
+ protected EntityQueries $queries,
) {
}
$modelMap = [];
foreach ($idsByType as $type => $ids) {
- if (!isset($this->listAttributes[$type])) {
- continue;
- }
-
- $instance = $this->entityProvider->get($type);
- $models = $instance->newQuery()
- ->select(array_merge($this->listAttributes[$type], $this->getSubSelectsForQuery($type)))
- ->scopes('visible')
+ $models = $this->queries->visibleForList($type)
->whereIn('id', $ids)
->with($eagerLoadParents ? $this->getRelationsToEagerLoad($type) : [])
->get();
return $toLoad;
}
-
- protected function getSubSelectsForQuery(string $type): array
- {
- $subSelects = [];
-
- if ($type === 'chapter' || $type === 'page') {
- $subSelects['book_slug'] = function ($builder) {
- $builder->select('slug')
- ->from('books')
- ->whereColumn('books.id', '=', 'book_id');
- };
- }
-
- return $subSelects;
- }
}
namespace BookStack\Search;
use BookStack\Entities\Queries\PageQueries;
-use BookStack\Entities\Queries\Popular;
+use BookStack\Entities\Queries\QueryPopular;
use BookStack\Entities\Tools\SiblingFetcher;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
* Search for a list of entities and return a partial HTML response of matching entities.
* Returns the most popular entities if no search is provided.
*/
- public function searchForSelector(Request $request)
+ public function searchForSelector(Request $request, QueryPopular $queryPopular)
{
$entityTypes = $request->filled('types') ? explode(',', $request->get('types')) : ['page', 'chapter', 'book'];
$searchTerm = $request->get('term', false);
$searchTerm .= ' {type:' . implode('|', $entityTypes) . '}';
$entities = $this->searchRunner->searchEntities(SearchOptions::fromString($searchTerm), 'all', 1, 20)['results'];
} else {
- $entities = (new Popular())->run(20, 0, $entityTypes);
+ $entities = $queryPopular->run(20, 0, $entityTypes);
}
return view('search.parts.entity-selector-list', ['entities' => $entities, 'permission' => $permission]);
@extends('layouts.simple')
-
+@inject('popular', \BookStack\Entities\Queries\QueryPopular::class)
@section('content')
<div class="container mt-l">
<div class="card mb-xl">
<h3 class="card-title">{{ trans('entities.pages_popular') }}</h3>
<div class="px-m">
- @include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['page']), 'style' => 'compact'])
+ @include('entities.list', ['entities' => $popular->run(10, 0, ['page']), 'style' => 'compact'])
</div>
</div>
</div>
<div class="card mb-xl">
<h3 class="card-title">{{ trans('entities.books_popular') }}</h3>
<div class="px-m">
- @include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['book']), 'style' => 'compact'])
+ @include('entities.list', ['entities' => $popular->run(10, 0, ['book']), 'style' => 'compact'])
</div>
</div>
</div>
<div class="card mb-xl">
<h3 class="card-title">{{ trans('entities.chapters_popular') }}</h3>
<div class="px-m">
- @include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['chapter']), 'style' => 'compact'])
+ @include('entities.list', ['entities' => $popular->run(10, 0, ['chapter']), 'style' => 'compact'])
</div>
</div>
</div>