]> BookStack Code Mirror - bookstack/commitdiff
Queries: Migrated BookRepo queries to new query class
authorDan Brown <redacted>
Sun, 4 Feb 2024 17:35:16 +0000 (17:35 +0000)
committerDan Brown <redacted>
Sun, 4 Feb 2024 17:35:16 +0000 (17:35 +0000)
Also moved to a non-static approach, and added a high-level class to
allow easy access to all other entity queries, for use in mixed-entity
scenarios and easier/simpler injection.

app/App/HomeController.php
app/Entities/Controllers/BookController.php
app/Entities/Controllers/BookExportController.php
app/Entities/Controllers/BookSortController.php
app/Entities/Queries/BookQueries.php [new file with mode: 0644]
app/Entities/Queries/EntityQueries.php [new file with mode: 0644]
app/Entities/Queries/PageQueries.php
app/Entities/Repos/BookRepo.php

index 48d60b8e4907049fd4c8b0cf2445f8892a8c0b88..dacd4bcc2c8d20f8323aa13b3e56b58e9d0b03bd 100644 (file)
@@ -5,10 +5,9 @@ namespace BookStack\App;
 use BookStack\Activity\ActivityQueries;
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Models\Page;
-use BookStack\Entities\Queries\PageQueries;
+use BookStack\Entities\Queries\EntityQueries;
 use BookStack\Entities\Queries\RecentlyViewed;
 use BookStack\Entities\Queries\TopFavourites;
-use BookStack\Entities\Repos\BookRepo;
 use BookStack\Entities\Repos\BookshelfRepo;
 use BookStack\Entities\Tools\PageContent;
 use BookStack\Http\Controller;
@@ -18,6 +17,11 @@ use Illuminate\Http\Request;
 
 class HomeController extends Controller
 {
+    public function __construct(
+        protected EntityQueries $queries,
+    ) {
+    }
+
     /**
      * Display the homepage.
      */
@@ -27,7 +31,7 @@ class HomeController extends Controller
         $draftPages = [];
 
         if ($this->isSignedIn()) {
-            $draftPages = PageQueries::currentUserDraftsForList()
+            $draftPages = $this->queries->pages->currentUserDraftsForList()
                 ->orderBy('updated_at', 'desc')
                 ->with('book')
                 ->take(6)
@@ -39,7 +43,7 @@ class HomeController extends Controller
             (new RecentlyViewed())->run(12 * $recentFactor, 1)
             : Book::visible()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get();
         $favourites = (new TopFavourites())->run(6);
-        $recentlyUpdatedPages = PageQueries::visibleForList()
+        $recentlyUpdatedPages = $this->queries->pages->visibleForList()
             ->where('draft', false)
             ->orderBy('updated_at', 'desc')
             ->take($favourites->count() > 0 ? 5 : 10)
@@ -83,7 +87,9 @@ class HomeController extends Controller
         }
 
         if ($homepageOption === 'books') {
-            $books = app()->make(BookRepo::class)->getAllPaginated(18, $commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder());
+            $books = $this->queries->books->visibleForListWithCover()
+                ->orderBy($commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder())
+                ->paginate(18);
             $data = array_merge($commonData, ['books' => $books]);
 
             return view('home.books', $data);
@@ -93,7 +99,7 @@ class HomeController extends Controller
             $homepageSetting = setting('app-homepage', '0:');
             $id = intval(explode(':', $homepageSetting)[0]);
             /** @var Page $customHomepage */
-            $customHomepage = PageQueries::start()->where('draft', '=', false)->findOrFail($id);
+            $customHomepage = $this->queries->pages->start()->where('draft', '=', false)->findOrFail($id);
             $pageContent = new PageContent($customHomepage);
             $customHomepage->html = $pageContent->render(false);
 
index 412feca2fe5b24fee76cc1068ca3583dbcccae04..a956a47d64ffc6659a20888549dde9cb27f4c999 100644 (file)
@@ -7,6 +7,7 @@ use BookStack\Activity\ActivityType;
 use BookStack\Activity\Models\View;
 use BookStack\Activity\Tools\UserEntityWatchOptions;
 use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Queries\BookQueries;
 use BookStack\Entities\Repos\BookRepo;
 use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Tools\Cloner;
@@ -27,7 +28,8 @@ class BookController extends Controller
     public function __construct(
         protected ShelfContext $shelfContext,
         protected BookRepo $bookRepo,
-        protected ReferenceFetcher $referenceFetcher
+        protected BookQueries $queries,
+        protected ReferenceFetcher $referenceFetcher,
     ) {
     }
 
@@ -43,10 +45,12 @@ class BookController extends Controller
             'updated_at' => trans('common.sort_updated_at'),
         ]);
 
-        $books = $this->bookRepo->getAllPaginated(18, $listOptions->getSort(), $listOptions->getOrder());
-        $recents = $this->isSignedIn() ? $this->bookRepo->getRecentlyViewed(4) : false;
-        $popular = $this->bookRepo->getPopular(4);
-        $new = $this->bookRepo->getRecentlyCreated(4);
+        $books = $this->queries->visibleForListWithCover()
+            ->orderBy($listOptions->getSort(), $listOptions->getOrder())
+            ->paginate(18);
+        $recents = $this->isSignedIn() ? $this->queries->recentlyViewedForCurrentUser()->take(4)->get() : false;
+        $popular = $this->queries->popularForList()->take(4)->get();
+        $new = $this->queries->visibleForList()->orderBy('created_at', 'desc')->take(4)->get();
 
         $this->shelfContext->clearShelfContext();
 
@@ -120,7 +124,7 @@ class BookController extends Controller
      */
     public function show(Request $request, ActivityQueries $activities, string $slug)
     {
-        $book = $this->bookRepo->getBySlug($slug);
+        $book = $this->queries->findVisibleBySlug($slug);
         $bookChildren = (new BookContents($book))->getTree(true);
         $bookParentShelves = $book->shelves()->scopes('visible')->get();
 
@@ -147,7 +151,7 @@ class BookController extends Controller
      */
     public function edit(string $slug)
     {
-        $book = $this->bookRepo->getBySlug($slug);
+        $book = $this->queries->findVisibleBySlug($slug);
         $this->checkOwnablePermission('book-update', $book);
         $this->setPageTitle(trans('entities.books_edit_named', ['bookName' => $book->getShortName()]));
 
@@ -163,7 +167,7 @@ class BookController extends Controller
      */
     public function update(Request $request, string $slug)
     {
-        $book = $this->bookRepo->getBySlug($slug);
+        $book = $this->queries->findVisibleBySlug($slug);
         $this->checkOwnablePermission('book-update', $book);
 
         $validated = $this->validate($request, [
@@ -190,7 +194,7 @@ class BookController extends Controller
      */
     public function showDelete(string $bookSlug)
     {
-        $book = $this->bookRepo->getBySlug($bookSlug);
+        $book = $this->queries->findVisibleBySlug($bookSlug);
         $this->checkOwnablePermission('book-delete', $book);
         $this->setPageTitle(trans('entities.books_delete_named', ['bookName' => $book->getShortName()]));
 
@@ -204,7 +208,7 @@ class BookController extends Controller
      */
     public function destroy(string $bookSlug)
     {
-        $book = $this->bookRepo->getBySlug($bookSlug);
+        $book = $this->queries->findVisibleBySlug($bookSlug);
         $this->checkOwnablePermission('book-delete', $book);
 
         $this->bookRepo->destroy($book);
@@ -219,7 +223,7 @@ class BookController extends Controller
      */
     public function showCopy(string $bookSlug)
     {
-        $book = $this->bookRepo->getBySlug($bookSlug);
+        $book = $this->queries->findVisibleBySlug($bookSlug);
         $this->checkOwnablePermission('book-view', $book);
 
         session()->flashInput(['name' => $book->name]);
@@ -236,7 +240,7 @@ class BookController extends Controller
      */
     public function copy(Request $request, Cloner $cloner, string $bookSlug)
     {
-        $book = $this->bookRepo->getBySlug($bookSlug);
+        $book = $this->queries->findVisibleBySlug($bookSlug);
         $this->checkOwnablePermission('book-view', $book);
         $this->checkPermission('book-create-all');
 
@@ -252,7 +256,7 @@ class BookController extends Controller
      */
     public function convertToShelf(HierarchyTransformer $transformer, string $bookSlug)
     {
-        $book = $this->bookRepo->getBySlug($bookSlug);
+        $book = $this->queries->findVisibleBySlug($bookSlug);
         $this->checkOwnablePermission('book-update', $book);
         $this->checkOwnablePermission('book-delete', $book);
         $this->checkPermission('bookshelf-create-all');
index 1a6b20db966e085455a84727a9af61e6f261aad3..6540df97808cf6f3f81179f13aa382fe300fe7b4 100644 (file)
@@ -2,23 +2,17 @@
 
 namespace BookStack\Entities\Controllers;
 
-use BookStack\Entities\Repos\BookRepo;
+use BookStack\Entities\Queries\BookQueries;
 use BookStack\Entities\Tools\ExportFormatter;
 use BookStack\Http\Controller;
 use Throwable;
 
 class BookExportController extends Controller
 {
-    protected $bookRepo;
-    protected $exportFormatter;
-
-    /**
-     * BookExportController constructor.
-     */
-    public function __construct(BookRepo $bookRepo, ExportFormatter $exportFormatter)
-    {
-        $this->bookRepo = $bookRepo;
-        $this->exportFormatter = $exportFormatter;
+    public function __construct(
+        protected BookQueries $queries,
+        protected ExportFormatter $exportFormatter,
+    ) {
         $this->middleware('can:content-export');
     }
 
@@ -29,7 +23,7 @@ class BookExportController extends Controller
      */
     public function pdf(string $bookSlug)
     {
-        $book = $this->bookRepo->getBySlug($bookSlug);
+        $book = $this->queries->findVisibleBySlug($bookSlug);
         $pdfContent = $this->exportFormatter->bookToPdf($book);
 
         return $this->download()->directly($pdfContent, $bookSlug . '.pdf');
@@ -42,7 +36,7 @@ class BookExportController extends Controller
      */
     public function html(string $bookSlug)
     {
-        $book = $this->bookRepo->getBySlug($bookSlug);
+        $book = $this->queries->findVisibleBySlug($bookSlug);
         $htmlContent = $this->exportFormatter->bookToContainedHtml($book);
 
         return $this->download()->directly($htmlContent, $bookSlug . '.html');
@@ -53,7 +47,7 @@ class BookExportController extends Controller
      */
     public function plainText(string $bookSlug)
     {
-        $book = $this->bookRepo->getBySlug($bookSlug);
+        $book = $this->queries->findVisibleBySlug($bookSlug);
         $textContent = $this->exportFormatter->bookToPlainText($book);
 
         return $this->download()->directly($textContent, $bookSlug . '.txt');
@@ -64,7 +58,7 @@ class BookExportController extends Controller
      */
     public function markdown(string $bookSlug)
     {
-        $book = $this->bookRepo->getBySlug($bookSlug);
+        $book = $this->queries->findVisibleBySlug($bookSlug);
         $textContent = $this->exportFormatter->bookToMarkdown($book);
 
         return $this->download()->directly($textContent, $bookSlug . '.md');
index f2310e2055dd24f76301eec1abd4ac0859c2a483..5d7024952cd2c81d897d74b4eac1bc72f1a3bd1a 100644 (file)
@@ -3,7 +3,7 @@
 namespace BookStack\Entities\Controllers;
 
 use BookStack\Activity\ActivityType;
-use BookStack\Entities\Repos\BookRepo;
+use BookStack\Entities\Queries\BookQueries;
 use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Tools\BookSortMap;
 use BookStack\Facades\Activity;
@@ -12,11 +12,9 @@ use Illuminate\Http\Request;
 
 class BookSortController extends Controller
 {
-    protected $bookRepo;
-
-    public function __construct(BookRepo $bookRepo)
-    {
-        $this->bookRepo = $bookRepo;
+    public function __construct(
+        protected BookQueries $queries,
+    ) {
     }
 
     /**
@@ -24,7 +22,7 @@ class BookSortController extends Controller
      */
     public function show(string $bookSlug)
     {
-        $book = $this->bookRepo->getBySlug($bookSlug);
+        $book = $this->queries->findVisibleBySlug($bookSlug);
         $this->checkOwnablePermission('book-update', $book);
 
         $bookChildren = (new BookContents($book))->getTree(false);
@@ -40,7 +38,7 @@ class BookSortController extends Controller
      */
     public function showItem(string $bookSlug)
     {
-        $book = $this->bookRepo->getBySlug($bookSlug);
+        $book = $this->queries->findVisibleBySlug($bookSlug);
         $bookChildren = (new BookContents($book))->getTree();
 
         return view('books.parts.sort-box', ['book' => $book, 'bookChildren' => $bookChildren]);
@@ -51,7 +49,7 @@ class BookSortController extends Controller
      */
     public function update(Request $request, string $bookSlug)
     {
-        $book = $this->bookRepo->getBySlug($bookSlug);
+        $book = $this->queries->findVisibleBySlug($bookSlug);
         $this->checkOwnablePermission('book-update', $book);
 
         // Return if no map sent
diff --git a/app/Entities/Queries/BookQueries.php b/app/Entities/Queries/BookQueries.php
new file mode 100644 (file)
index 0000000..6de28f0
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+namespace BookStack\Entities\Queries;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Exceptions\NotFoundException;
+use Illuminate\Database\Eloquent\Builder;
+
+class BookQueries
+{
+    public function start(): Builder
+    {
+        return Book::query();
+    }
+
+    public function findVisibleBySlug(string $slug): Book
+    {
+        /** @var ?Book $book */
+        $book = $this->start()
+            ->scopes('visible')
+            ->where('slug', '=', $slug)
+            ->first();
+
+        if ($book === null) {
+            throw new NotFoundException(trans('errors.book_not_found'));
+        }
+
+        return $book;
+    }
+
+    public function visibleForList(): Builder
+    {
+        return $this->start()->scopes('visible');
+    }
+
+    public function visibleForListWithCover(): Builder
+    {
+        return $this->visibleForList()->with('cover');
+    }
+
+    public function recentlyViewedForCurrentUser(): Builder
+    {
+        return $this->visibleForList()
+            ->scopes('withLastView')
+            ->having('last_viewed_at', '>', 0)
+            ->orderBy('last_viewed_at', 'desc');
+    }
+
+    public function popularForList(): Builder
+    {
+        return $this->visibleForList()
+            ->scopes('withViewCount')
+            ->having('view_count', '>', 0)
+            ->orderBy('view_count', 'desc');
+    }
+}
diff --git a/app/Entities/Queries/EntityQueries.php b/app/Entities/Queries/EntityQueries.php
new file mode 100644 (file)
index 0000000..417db45
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+namespace BookStack\Entities\Queries;
+
+class EntityQueries
+{
+    public function __construct(
+        public BookQueries $books,
+        public PageQueries $pages,
+    ) {
+    }
+}
index 7b7ac1e5e70cfb1fd1050b2fb7e51cbf3f195061..b9c64f63ce81962c3d96c6aeb179abdfccd017b1 100644 (file)
@@ -7,14 +7,14 @@ use Illuminate\Database\Eloquent\Builder;
 
 class PageQueries
 {
-    public static function start(): Builder
+    public function start(): Builder
     {
         return Page::query();
     }
 
-    public static function visibleForList(): Builder
+    public function visibleForList(): Builder
     {
-        return Page::visible()
+        return $this->start()
             ->select(array_merge(Page::$listAttributes, ['book_slug' => function ($builder) {
                 $builder->select('slug')
                     ->from('books')
@@ -22,9 +22,9 @@ class PageQueries
             }]));
     }
 
-    public static function currentUserDraftsForList(): Builder
+    public function currentUserDraftsForList(): Builder
     {
-        return static::visibleForList()
+        return $this->visibleForList()
             ->where('draft', '=', true)
             ->where('created_by', '=', user()->id);
     }
index bf765b22d158f43524c4b51f077d5f5a8f109430..26b9414fbac24ebd74cb7db1d9d604e422029a42 100644 (file)
@@ -5,16 +5,12 @@ namespace BookStack\Entities\Repos;
 use BookStack\Activity\ActivityType;
 use BookStack\Activity\TagRepo;
 use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Page;
 use BookStack\Entities\Tools\TrashCan;
 use BookStack\Exceptions\ImageUploadException;
-use BookStack\Exceptions\NotFoundException;
 use BookStack\Facades\Activity;
 use BookStack\Uploads\ImageRepo;
 use Exception;
-use Illuminate\Contracts\Pagination\LengthAwarePaginator;
 use Illuminate\Http\UploadedFile;
-use Illuminate\Support\Collection;
 
 class BookRepo
 {
@@ -25,59 +21,6 @@ class BookRepo
     ) {
     }
 
-    /**
-     * Get all books in a paginated format.
-     */
-    public function getAllPaginated(int $count = 20, string $sort = 'name', string $order = 'asc'): LengthAwarePaginator
-    {
-        return Book::visible()->with('cover')->orderBy($sort, $order)->paginate($count);
-    }
-
-    /**
-     * Get the books that were most recently viewed by this user.
-     */
-    public function getRecentlyViewed(int $count = 20): Collection
-    {
-        return Book::visible()->withLastView()
-            ->having('last_viewed_at', '>', 0)
-            ->orderBy('last_viewed_at', 'desc')
-            ->take($count)->get();
-    }
-
-    /**
-     * Get the most popular books in the system.
-     */
-    public function getPopular(int $count = 20): Collection
-    {
-        return Book::visible()->withViewCount()
-            ->having('view_count', '>', 0)
-            ->orderBy('view_count', 'desc')
-            ->take($count)->get();
-    }
-
-    /**
-     * Get the most recently created books from the system.
-     */
-    public function getRecentlyCreated(int $count = 20): Collection
-    {
-        return Book::visible()->orderBy('created_at', 'desc')
-            ->take($count)->get();
-    }
-
-    /**
-     * Get a book by its slug.
-     */
-    public function getBySlug(string $slug): Book
-    {
-        $book = Book::visible()->where('slug', '=', $slug)->first();
-
-        if ($book === null) {
-            throw new NotFoundException(trans('errors.book_not_found'));
-        }
-
-        return $book;
-    }
-
     /**
      * Create a new book in the system.
      */