diff --git a/app/Entities/Tools/BookContents.php b/app/Entities/Tools/BookContents.php
index 9b2190ca2..96142bb7f 100644
--- a/app/Entities/Tools/BookContents.php
+++ b/app/Entities/Tools/BookContents.php
@@ -107,30 +107,21 @@ class BookContents
     }
 
     /**
-     * Sort the books content using the given map.
-     * The map is a single-dimension collection of objects in the following format:
-     *   {
-     *     +"id": "294" (ID of item)
-     *     +"sort": 1 (Sort order index)
-     *     +"parentChapter": false (ID of parent chapter, as string, or false)
-     *     +"type": "page" (Entity type of item)
-     *     +"book": "1" (Id of book to place item in)
-     *   }.
-     *
+     * Sort the books content using the given sort map.
      * Returns a list of books that were involved in the operation.
      *
      * @throws SortOperationException
      */
-    public function sortUsingMap(Collection $sortMap): Collection
+    public function sortUsingMap(BookSortMap $sortMap): Collection
     {
         // Load models into map
         $this->loadModelsIntoSortMap($sortMap);
         $booksInvolved = $this->getBooksInvolvedInSort($sortMap);
 
         // Perform the sort
-        $sortMap->each(function ($mapItem) {
-            $this->applySortUpdates($mapItem);
-        });
+        foreach ($sortMap->all() as $item) {
+            $this->applySortUpdates($item);
+        }
 
         // Update permissions and activity.
         $booksInvolved->each(function (Book $book) {
@@ -144,26 +135,28 @@ class BookContents
      * Using the given sort map item, detect changes for the related model
      * and update it if required.
      */
-    protected function applySortUpdates(\stdClass $sortMapItem)
+    protected function applySortUpdates(BookSortMapItem $sortMapItem): void
     {
-        /** @var BookChild $model */
         $model = $sortMapItem->model;
+        if (!$model) {
+            return;
+        }
 
-        $priorityChanged = intval($model->priority) !== intval($sortMapItem->sort);
-        $bookChanged = intval($model->book_id) !== intval($sortMapItem->book);
-        $chapterChanged = ($model instanceof Page) && intval($model->chapter_id) !== $sortMapItem->parentChapter;
+        $priorityChanged = $model->priority !== $sortMapItem->sort;
+        $bookChanged = $model->book_id !== $sortMapItem->parentBookId;
+        $chapterChanged = ($model instanceof Page) && $model->chapter_id !== $sortMapItem->parentChapterId;
 
         if ($bookChanged) {
-            $model->changeBook($sortMapItem->book);
+            $model->changeBook($sortMapItem->parentBookId);
         }
 
         if ($chapterChanged) {
-            $model->chapter_id = intval($sortMapItem->parentChapter);
+            $model->chapter_id = intval($sortMapItem->parentChapterId);
             $model->save();
         }
 
         if ($priorityChanged) {
-            $model->priority = intval($sortMapItem->sort);
+            $model->priority = $sortMapItem->sort;
             $model->save();
         }
     }
@@ -171,23 +164,28 @@ class BookContents
     /**
      * Load models from the database into the given sort map.
      */
-    protected function loadModelsIntoSortMap(Collection $sortMap): void
+    protected function loadModelsIntoSortMap(BookSortMap $sortMap): void
     {
-        $keyMap = $sortMap->keyBy(function (\stdClass $sortMapItem) {
+        $collection = collect($sortMap->all());
+
+        $keyMap = $collection->keyBy(function (BookSortMapItem $sortMapItem) {
             return  $sortMapItem->type . ':' . $sortMapItem->id;
         });
-        $pageIds = $sortMap->where('type', '=', 'page')->pluck('id');
-        $chapterIds = $sortMap->where('type', '=', 'chapter')->pluck('id');
+
+        $pageIds = $collection->where('type', '=', 'page')->pluck('id');
+        $chapterIds = $collection->where('type', '=', 'chapter')->pluck('id');
 
         $pages = Page::visible()->whereIn('id', $pageIds)->get();
         $chapters = Chapter::visible()->whereIn('id', $chapterIds)->get();
 
         foreach ($pages as $page) {
+            /** @var BookSortMapItem $sortItem */
             $sortItem = $keyMap->get('page:' . $page->id);
             $sortItem->model = $page;
         }
 
         foreach ($chapters as $chapter) {
+            /** @var BookSortMapItem $sortItem */
             $sortItem = $keyMap->get('chapter:' . $chapter->id);
             $sortItem->model = $chapter;
         }
@@ -199,13 +197,16 @@ class BookContents
      *
      * @throws SortOperationException
      */
-    protected function getBooksInvolvedInSort(Collection $sortMap): Collection
+    protected function getBooksInvolvedInSort(BookSortMap $sortMap): Collection
     {
-        $bookIdsInvolved = collect([$this->book->id]);
-        $bookIdsInvolved = $bookIdsInvolved->concat($sortMap->pluck('book'));
-        $bookIdsInvolved = $bookIdsInvolved->concat($sortMap->pluck('model.book_id'));
-        $bookIdsInvolved = $bookIdsInvolved->unique()->toArray();
+        $collection = collect($sortMap->all());
 
+        $bookIdsInvolved = array_unique(array_merge(
+            [$this->book->id],
+            $collection->pluck('parentBookId')->values()->all(),
+            $collection->pluck('model.book_id')->values()->all(),
+        ));
+        
         $books = Book::hasPermission('update')->whereIn('id', $bookIdsInvolved)->get();
 
         if (count($books) !== count($bookIdsInvolved)) {
diff --git a/app/Entities/Tools/BookSortMap.php b/app/Entities/Tools/BookSortMap.php
new file mode 100644
index 000000000..1ce4905f7
--- /dev/null
+++ b/app/Entities/Tools/BookSortMap.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace BookStack\Entities\Tools;
+
+class BookSortMap
+{
+    /**
+     * @var BookSortMapItem[]
+     */
+    protected $mapData = [];
+
+    public function addItem(BookSortMapItem $mapItem): void
+    {
+        $this->mapData[] = $mapItem;
+    }
+
+    /**
+     * @return BookSortMapItem[]
+     */
+    public function all(): array
+    {
+        return $this->mapData;
+    }
+
+    public static function fromJson(string $json): self
+    {
+        $map = new static();
+        $mapData = json_decode($json);
+
+        foreach ($mapData as $mapDataItem) {
+            $item = new BookSortMapItem(
+                intval($mapDataItem->id),
+                intval($mapDataItem->sort),
+                $mapDataItem->parentChapter ? intval($mapDataItem->parentChapter) : null,
+                $mapDataItem->type,
+                intval($mapDataItem->book)
+            );
+
+            $map->addItem($item);
+        }
+
+        return $map;
+    }
+
+}
\ No newline at end of file
diff --git a/app/Entities/Tools/BookSortMapItem.php b/app/Entities/Tools/BookSortMapItem.php
new file mode 100644
index 000000000..6a2abc422
--- /dev/null
+++ b/app/Entities/Tools/BookSortMapItem.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace BookStack\Entities\Tools;
+
+use BookStack\Entities\Models\BookChild;
+
+class BookSortMapItem
+{
+
+    /**
+     * @var int
+     */
+    public $id;
+
+    /**
+     * @var int
+     */
+    public $sort;
+
+    /**
+     * @var ?int
+     */
+    public $parentChapterId;
+
+    /**
+     * @var string
+     */
+    public $type;
+
+    /**
+     * @var int
+     */
+    public $parentBookId;
+
+    /**
+     * @var ?BookChild
+     */
+    public $model = null;
+
+
+    public function __construct(int $id, int $sort, ?int $parentChapterId, string $type, int $parentBookId)
+    {
+        $this->id = $id;
+        $this->sort = $sort;
+        $this->parentChapterId = $parentChapterId;
+        $this->type = $type;
+        $this->parentBookId = $parentBookId;
+    }
+
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/BookSortController.php b/app/Http/Controllers/BookSortController.php
index 010e74fa4..8fe05a9be 100644
--- a/app/Http/Controllers/BookSortController.php
+++ b/app/Http/Controllers/BookSortController.php
@@ -6,6 +6,7 @@ use BookStack\Actions\ActivityType;
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Repos\BookRepo;
 use BookStack\Entities\Tools\BookContents;
+use BookStack\Entities\Tools\BookSortMap;
 use BookStack\Exceptions\SortOperationException;
 use BookStack\Facades\Activity;
 use Illuminate\Http\Request;
@@ -59,7 +60,7 @@ class BookSortController extends Controller
             return redirect($book->getUrl());
         }
 
-        $sortMap = collect(json_decode($request->get('sort-tree')));
+        $sortMap = BookSortMap::fromJson($request->get('sort-tree'));
         $bookContents = new BookContents($book);
         $booksInvolved = collect();