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:
parent
b77ab6f3af
commit
ed21a6d798
10 changed files with 57 additions and 83 deletions
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace BookStack\Activity\Controllers;
|
namespace BookStack\Activity\Controllers;
|
||||||
|
|
||||||
use BookStack\Entities\Queries\TopFavourites;
|
use BookStack\Entities\Queries\QueryTopFavourites;
|
||||||
use BookStack\Entities\Tools\MixedEntityRequestHelper;
|
use BookStack\Entities\Tools\MixedEntityRequestHelper;
|
||||||
use BookStack\Http\Controller;
|
use BookStack\Http\Controller;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
@ -21,7 +21,7 @@ class FavouriteController extends Controller
|
||||||
{
|
{
|
||||||
$viewCount = 20;
|
$viewCount = 20;
|
||||||
$page = intval($request->get('page', 1));
|
$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;
|
$hasMoreLink = ($favourites->count() > $viewCount) ? url('/favourites?page=' . ($page + 1)) : null;
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,8 @@ namespace BookStack\App;
|
||||||
use BookStack\Activity\ActivityQueries;
|
use BookStack\Activity\ActivityQueries;
|
||||||
use BookStack\Entities\Models\Page;
|
use BookStack\Entities\Models\Page;
|
||||||
use BookStack\Entities\Queries\EntityQueries;
|
use BookStack\Entities\Queries\EntityQueries;
|
||||||
use BookStack\Entities\Queries\RecentlyViewed;
|
use BookStack\Entities\Queries\QueryRecentlyViewed;
|
||||||
use BookStack\Entities\Queries\TopFavourites;
|
use BookStack\Entities\Queries\QueryTopFavourites;
|
||||||
use BookStack\Entities\Tools\PageContent;
|
use BookStack\Entities\Tools\PageContent;
|
||||||
use BookStack\Http\Controller;
|
use BookStack\Http\Controller;
|
||||||
use BookStack\Uploads\FaviconHandler;
|
use BookStack\Uploads\FaviconHandler;
|
||||||
|
@ -23,8 +23,12 @@ class HomeController extends Controller
|
||||||
/**
|
/**
|
||||||
* Display the homepage.
|
* 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);
|
$activity = $activities->latest(10);
|
||||||
$draftPages = [];
|
$draftPages = [];
|
||||||
|
|
||||||
|
@ -38,9 +42,9 @@ class HomeController extends Controller
|
||||||
|
|
||||||
$recentFactor = count($draftPages) > 0 ? 0.5 : 1;
|
$recentFactor = count($draftPages) > 0 ? 0.5 : 1;
|
||||||
$recents = $this->isSignedIn() ?
|
$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();
|
: $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()
|
$recentlyUpdatedPages = $this->queries->pages->visibleForList()
|
||||||
->where('draft', false)
|
->where('draft', false)
|
||||||
->orderBy('updated_at', 'desc')
|
->orderBy('updated_at', 'desc')
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -50,7 +50,7 @@ class PageQueries implements ProvidesEntityQueries
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if (is_null($page)) {
|
if (is_null($page)) {
|
||||||
throw new NotFoundException(trans('errors.chapter_not_found'));
|
throw new NotFoundException(trans('errors.page_not_found'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $page;
|
return $page;
|
||||||
|
|
|
@ -3,24 +3,32 @@
|
||||||
namespace BookStack\Entities\Queries;
|
namespace BookStack\Entities\Queries;
|
||||||
|
|
||||||
use BookStack\Activity\Models\View;
|
use BookStack\Activity\Models\View;
|
||||||
|
use BookStack\Entities\EntityProvider;
|
||||||
use BookStack\Entities\Models\BookChild;
|
use BookStack\Entities\Models\BookChild;
|
||||||
use BookStack\Entities\Models\Entity;
|
use BookStack\Entities\Models\Entity;
|
||||||
|
use BookStack\Permissions\PermissionApplicator;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
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)
|
public function run(int $count, int $page, array $filterModels = null)
|
||||||
{
|
{
|
||||||
$query = $this->permissionService()
|
$query = $this->permissions
|
||||||
->restrictEntityRelationQuery(View::query(), 'views', 'viewable_id', 'viewable_type')
|
->restrictEntityRelationQuery(View::query(), 'views', 'viewable_id', 'viewable_type')
|
||||||
->select('*', 'viewable_id', 'viewable_type', DB::raw('SUM(views) as view_count'))
|
->select('*', 'viewable_id', 'viewable_type', DB::raw('SUM(views) as view_count'))
|
||||||
->groupBy('viewable_id', 'viewable_type')
|
->groupBy('viewable_id', 'viewable_type')
|
||||||
->orderBy('view_count', 'desc');
|
->orderBy('view_count', 'desc');
|
||||||
|
|
||||||
if ($filterModels) {
|
if ($filterModels) {
|
||||||
$query->whereIn('viewable_type', $this->entityProvider()->getMorphClasses($filterModels));
|
$query->whereIn('viewable_type', $this->entityProvider->getMorphClasses($filterModels));
|
||||||
}
|
}
|
||||||
|
|
||||||
$entities = $query->with('viewable')
|
$entities = $query->with('viewable')
|
||||||
|
@ -35,7 +43,7 @@ class Popular extends EntityQuery
|
||||||
return $entities;
|
return $entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function loadBooksForChildren(Collection $entities)
|
protected function loadBooksForChildren(Collection $entities): void
|
||||||
{
|
{
|
||||||
$bookChildren = $entities->filter(fn(Entity $entity) => $entity instanceof BookChild);
|
$bookChildren = $entities->filter(fn(Entity $entity) => $entity instanceof BookChild);
|
||||||
$eloquent = (new \Illuminate\Database\Eloquent\Collection($bookChildren));
|
$eloquent = (new \Illuminate\Database\Eloquent\Collection($bookChildren));
|
|
@ -3,10 +3,18 @@
|
||||||
namespace BookStack\Entities\Queries;
|
namespace BookStack\Entities\Queries;
|
||||||
|
|
||||||
use BookStack\Activity\Models\View;
|
use BookStack\Activity\Models\View;
|
||||||
|
use BookStack\Entities\Tools\MixedEntityListLoader;
|
||||||
|
use BookStack\Permissions\PermissionApplicator;
|
||||||
use Illuminate\Support\Collection;
|
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
|
public function run(int $count, int $page): Collection
|
||||||
{
|
{
|
||||||
$user = user();
|
$user = user();
|
||||||
|
@ -14,7 +22,7 @@ class RecentlyViewed extends EntityQuery
|
||||||
return collect();
|
return collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = $this->permissionService()->restrictEntityRelationQuery(
|
$query = $this->permissions->restrictEntityRelationQuery(
|
||||||
View::query(),
|
View::query(),
|
||||||
'views',
|
'views',
|
||||||
'viewable_id',
|
'viewable_id',
|
||||||
|
@ -28,7 +36,7 @@ class RecentlyViewed extends EntityQuery
|
||||||
->take($count)
|
->take($count)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$this->mixedEntityListLoader()->loadIntoRelations($views->all(), 'viewable', false);
|
$this->listLoader->loadIntoRelations($views->all(), 'viewable', false);
|
||||||
|
|
||||||
return $views->pluck('viewable')->filter();
|
return $views->pluck('viewable')->filter();
|
||||||
}
|
}
|
|
@ -3,10 +3,18 @@
|
||||||
namespace BookStack\Entities\Queries;
|
namespace BookStack\Entities\Queries;
|
||||||
|
|
||||||
use BookStack\Activity\Models\Favourite;
|
use BookStack\Activity\Models\Favourite;
|
||||||
|
use BookStack\Entities\Tools\MixedEntityListLoader;
|
||||||
|
use BookStack\Permissions\PermissionApplicator;
|
||||||
use Illuminate\Database\Query\JoinClause;
|
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)
|
public function run(int $count, int $skip = 0)
|
||||||
{
|
{
|
||||||
$user = user();
|
$user = user();
|
||||||
|
@ -14,7 +22,7 @@ class TopFavourites extends EntityQuery
|
||||||
return collect();
|
return collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = $this->permissionService()
|
$query = $this->permissions
|
||||||
->restrictEntityRelationQuery(Favourite::query(), 'favourites', 'favouritable_id', 'favouritable_type')
|
->restrictEntityRelationQuery(Favourite::query(), 'favourites', 'favouritable_id', 'favouritable_type')
|
||||||
->select('favourites.*')
|
->select('favourites.*')
|
||||||
->leftJoin('views', function (JoinClause $join) {
|
->leftJoin('views', function (JoinClause $join) {
|
||||||
|
@ -30,7 +38,7 @@ class TopFavourites extends EntityQuery
|
||||||
->take($count)
|
->take($count)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$this->mixedEntityListLoader()->loadIntoRelations($favourites->all(), 'favouritable', false);
|
$this->listLoader->loadIntoRelations($favourites->all(), 'favouritable', false);
|
||||||
|
|
||||||
return $favourites->pluck('favouritable')->filter();
|
return $favourites->pluck('favouritable')->filter();
|
||||||
}
|
}
|
|
@ -3,20 +3,13 @@
|
||||||
namespace BookStack\Entities\Tools;
|
namespace BookStack\Entities\Tools;
|
||||||
|
|
||||||
use BookStack\App\Model;
|
use BookStack\App\Model;
|
||||||
use BookStack\Entities\EntityProvider;
|
use BookStack\Entities\Queries\EntityQueries;
|
||||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||||
|
|
||||||
class MixedEntityListLoader
|
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(
|
public function __construct(
|
||||||
protected EntityProvider $entityProvider
|
protected EntityQueries $queries,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,14 +54,7 @@ class MixedEntityListLoader
|
||||||
$modelMap = [];
|
$modelMap = [];
|
||||||
|
|
||||||
foreach ($idsByType as $type => $ids) {
|
foreach ($idsByType as $type => $ids) {
|
||||||
if (!isset($this->listAttributes[$type])) {
|
$models = $this->queries->visibleForList($type)
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$instance = $this->entityProvider->get($type);
|
|
||||||
$models = $instance->newQuery()
|
|
||||||
->select(array_merge($this->listAttributes[$type], $this->getSubSelectsForQuery($type)))
|
|
||||||
->scopes('visible')
|
|
||||||
->whereIn('id', $ids)
|
->whereIn('id', $ids)
|
||||||
->with($eagerLoadParents ? $this->getRelationsToEagerLoad($type) : [])
|
->with($eagerLoadParents ? $this->getRelationsToEagerLoad($type) : [])
|
||||||
->get();
|
->get();
|
||||||
|
@ -100,19 +86,4 @@ class MixedEntityListLoader
|
||||||
|
|
||||||
return $toLoad;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
namespace BookStack\Search;
|
namespace BookStack\Search;
|
||||||
|
|
||||||
use BookStack\Entities\Queries\PageQueries;
|
use BookStack\Entities\Queries\PageQueries;
|
||||||
use BookStack\Entities\Queries\Popular;
|
use BookStack\Entities\Queries\QueryPopular;
|
||||||
use BookStack\Entities\Tools\SiblingFetcher;
|
use BookStack\Entities\Tools\SiblingFetcher;
|
||||||
use BookStack\Http\Controller;
|
use BookStack\Http\Controller;
|
||||||
use Illuminate\Http\Request;
|
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.
|
* 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.
|
* 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'];
|
$entityTypes = $request->filled('types') ? explode(',', $request->get('types')) : ['page', 'chapter', 'book'];
|
||||||
$searchTerm = $request->get('term', false);
|
$searchTerm = $request->get('term', false);
|
||||||
|
@ -78,7 +78,7 @@ class SearchController extends Controller
|
||||||
$searchTerm .= ' {type:' . implode('|', $entityTypes) . '}';
|
$searchTerm .= ' {type:' . implode('|', $entityTypes) . '}';
|
||||||
$entities = $this->searchRunner->searchEntities(SearchOptions::fromString($searchTerm), 'all', 1, 20)['results'];
|
$entities = $this->searchRunner->searchEntities(SearchOptions::fromString($searchTerm), 'all', 1, 20)['results'];
|
||||||
} else {
|
} 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]);
|
return view('search.parts.entity-selector-list', ['entities' => $entities, 'permission' => $permission]);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
@extends('layouts.simple')
|
@extends('layouts.simple')
|
||||||
|
@inject('popular', \BookStack\Entities\Queries\QueryPopular::class)
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="container mt-l">
|
<div class="container mt-l">
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
<div class="card mb-xl">
|
<div class="card mb-xl">
|
||||||
<h3 class="card-title">{{ trans('entities.pages_popular') }}</h3>
|
<h3 class="card-title">{{ trans('entities.pages_popular') }}</h3>
|
||||||
<div class="px-m">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
<div class="card mb-xl">
|
<div class="card mb-xl">
|
||||||
<h3 class="card-title">{{ trans('entities.books_popular') }}</h3>
|
<h3 class="card-title">{{ trans('entities.books_popular') }}</h3>
|
||||||
<div class="px-m">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
<div class="card mb-xl">
|
<div class="card mb-xl">
|
||||||
<h3 class="card-title">{{ trans('entities.chapters_popular') }}</h3>
|
<h3 class="card-title">{{ trans('entities.chapters_popular') }}</h3>
|
||||||
<div class="px-m">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue