Queries: Updated old use-specific entity query classes

- 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.
This commit is contained in:
Dan Brown 2024-02-08 16:39:59 +00:00
parent b77ab6f3af
commit ed21a6d798
No known key found for this signature in database
GPG key ID: 46D9F943C24A2EF9
10 changed files with 57 additions and 83 deletions

View file

@ -2,7 +2,7 @@
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;
@ -21,7 +21,7 @@ class FavouriteController extends Controller
{
$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;

View file

@ -5,8 +5,8 @@ namespace BookStack\App;
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;
@ -23,8 +23,12 @@ class HomeController extends Controller
/**
* 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 = [];
@ -38,9 +42,9 @@ class HomeController extends Controller
$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')

View file

@ -1,25 +0,0 @@
<?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);
}
}

View file

@ -50,7 +50,7 @@ class PageQueries implements ProvidesEntityQueries
->first();
if (is_null($page)) {
throw new NotFoundException(trans('errors.chapter_not_found'));
throw new NotFoundException(trans('errors.page_not_found'));
}
return $page;

View file

@ -3,24 +3,32 @@
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')
@ -35,7 +43,7 @@ class Popular extends EntityQuery
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));

View file

@ -3,10 +3,18 @@
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();
@ -14,7 +22,7 @@ class RecentlyViewed extends EntityQuery
return collect();
}
$query = $this->permissionService()->restrictEntityRelationQuery(
$query = $this->permissions->restrictEntityRelationQuery(
View::query(),
'views',
'viewable_id',
@ -28,7 +36,7 @@ class RecentlyViewed extends EntityQuery
->take($count)
->get();
$this->mixedEntityListLoader()->loadIntoRelations($views->all(), 'viewable', false);
$this->listLoader->loadIntoRelations($views->all(), 'viewable', false);
return $views->pluck('viewable')->filter();
}

View file

@ -3,10 +3,18 @@
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();
@ -14,7 +22,7 @@ class TopFavourites extends EntityQuery
return collect();
}
$query = $this->permissionService()
$query = $this->permissions
->restrictEntityRelationQuery(Favourite::query(), 'favourites', 'favouritable_id', 'favouritable_type')
->select('favourites.*')
->leftJoin('views', function (JoinClause $join) {
@ -30,7 +38,7 @@ class TopFavourites extends EntityQuery
->take($count)
->get();
$this->mixedEntityListLoader()->loadIntoRelations($favourites->all(), 'favouritable', false);
$this->listLoader->loadIntoRelations($favourites->all(), 'favouritable', false);
return $favourites->pluck('favouritable')->filter();
}

View file

@ -3,20 +3,13 @@
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,
) {
}
@ -61,14 +54,7 @@ class MixedEntityListLoader
$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();
@ -100,19 +86,4 @@ class MixedEntityListLoader
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;
}
}

View file

@ -3,7 +3,7 @@
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;
@ -67,7 +67,7 @@ class SearchController extends Controller
* 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);
@ -78,7 +78,7 @@ class SearchController extends Controller
$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]);

View file

@ -1,5 +1,5 @@
@extends('layouts.simple')
@inject('popular', \BookStack\Entities\Queries\QueryPopular::class)
@section('content')
<div class="container mt-l">
@ -28,7 +28,7 @@
<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>
@ -36,7 +36,7 @@
<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>
@ -44,7 +44,7 @@
<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>