diff --git a/app/Actions/View.php b/app/Actions/View.php index c5ec6a38d..62e03d9f4 100644 --- a/app/Actions/View.php +++ b/app/Actions/View.php @@ -1,8 +1,19 @@ <?php namespace BookStack\Actions; +use BookStack\Interfaces\Viewable; use BookStack\Model; use Illuminate\Database\Eloquent\Relations\MorphTo; +/** + * Class View + * Views are stored per-item per-person within the database. + * They can be used to find popular items or recently viewed items + * at a per-person level. They do not record every view instance as an + * activity. Only the latest and original view times could be recognised. + * + * @property int $views + * @property int $user_id + */ class View extends Model { @@ -15,4 +26,32 @@ class View extends Model { return $this->morphTo(); } + + /** + * Increment the current user's view count for the given viewable model. + */ + public static function incrementFor(Viewable $viewable): int + { + $user = user(); + if (is_null($user) || $user->isDefault()) { + return 0; + } + + /** @var View $view */ + $view = $viewable->views()->firstOrNew([ + 'user_id' => $user->id, + ], ['views' => 0]); + + $view->save(['views' => $view->views + 1]); + + return $view->views; + } + + /** + * Clear all views from the system. + */ + public static function clearAll() + { + static::query()->truncate(); + } } diff --git a/app/Actions/ViewService.php b/app/Actions/ViewService.php index a4e620d4b..febc93af3 100644 --- a/app/Actions/ViewService.php +++ b/app/Actions/ViewService.php @@ -1,7 +1,6 @@ <?php namespace BookStack\Actions; use BookStack\Auth\Permissions\PermissionService; -use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Entity; use BookStack\Entities\EntityProvider; use DB; @@ -26,33 +25,6 @@ class ViewService $this->entityProvider = $entityProvider; } - /** - * Add a view to the given entity. - * @param \BookStack\Entities\Models\Entity $entity - * @return int - */ - public function add(Entity $entity) - { - $user = user(); - if ($user === null || $user->isDefault()) { - return 0; - } - $view = $entity->views()->where('user_id', '=', $user->id)->first(); - // Add view if model exists - if ($view) { - $view->increment('views'); - return $view->views; - } - - // Otherwise create new view count - $entity->views()->save($this->view->newInstance([ - 'user_id' => $user->id, - 'views' => 1 - ])); - - return 1; - } - /** * Get the entities with the most views. * @param int $count @@ -106,12 +78,4 @@ class ViewService return $all->sortByDesc('last_viewed_at')->slice(0, $count); } - - /** - * Reset all view counts by deleting all views. - */ - public function resetAll() - { - $this->view->truncate(); - } } diff --git a/app/Console/Commands/ClearViews.php b/app/Console/Commands/ClearViews.php index 35356210b..693d93639 100644 --- a/app/Console/Commands/ClearViews.php +++ b/app/Console/Commands/ClearViews.php @@ -2,6 +2,7 @@ namespace BookStack\Console\Commands; +use BookStack\Actions\View; use Illuminate\Console\Command; class ClearViews extends Command @@ -36,7 +37,7 @@ class ClearViews extends Command */ public function handle() { - \Views::resetAll(); + View::clearAll(); $this->comment('Views cleared'); } } diff --git a/app/Entities/Models/Entity.php b/app/Entities/Models/Entity.php index be63cec2b..561876769 100644 --- a/app/Entities/Models/Entity.php +++ b/app/Entities/Models/Entity.php @@ -12,6 +12,7 @@ use BookStack\Entities\Tools\SlugGenerator; use BookStack\Facades\Permissions; use BookStack\Interfaces\Favouritable; use BookStack\Interfaces\Sluggable; +use BookStack\Interfaces\Viewable; use BookStack\Model; use BookStack\Traits\HasCreatorAndUpdater; use BookStack\Traits\HasOwner; @@ -40,7 +41,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @method static Builder withLastView() * @method static Builder withViewCount() */ -abstract class Entity extends Model implements Sluggable, Favouritable +abstract class Entity extends Model implements Sluggable, Favouritable, Viewable { use SoftDeletes; use HasCreatorAndUpdater; diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php index 59c205d0a..d111f9f0a 100644 --- a/app/Http/Controllers/BookController.php +++ b/app/Http/Controllers/BookController.php @@ -2,6 +2,7 @@ use Activity; use BookStack\Actions\ActivityType; +use BookStack\Actions\View; use BookStack\Entities\Tools\BookContents; use BookStack\Entities\Models\Bookshelf; use BookStack\Entities\Tools\PermissionsUpdater; @@ -112,7 +113,7 @@ class BookController extends Controller $bookChildren = (new BookContents($book))->getTree(true); $bookParentShelves = $book->shelves()->visible()->get(); - Views::add($book); + View::incrementFor($book); if ($request->has('shelf')) { $this->entityContextManager->setShelfContext(intval($request->get('shelf'))); } diff --git a/app/Http/Controllers/BookshelfController.php b/app/Http/Controllers/BookshelfController.php index 03b3cad54..b4795db09 100644 --- a/app/Http/Controllers/BookshelfController.php +++ b/app/Http/Controllers/BookshelfController.php @@ -1,6 +1,7 @@ <?php namespace BookStack\Http\Controllers; use Activity; +use BookStack\Actions\View; use BookStack\Entities\Models\Book; use BookStack\Entities\Tools\PermissionsUpdater; use BookStack\Entities\Tools\ShelfContext; @@ -109,7 +110,7 @@ class BookshelfController extends Controller ->values() ->all(); - Views::add($shelf); + View::incrementFor($shelf); $this->entityContextManager->setShelfContext($shelf->id); $view = setting()->getForCurrentUser('bookshelf_view_type'); diff --git a/app/Http/Controllers/ChapterController.php b/app/Http/Controllers/ChapterController.php index 1d69df2a2..fbef81582 100644 --- a/app/Http/Controllers/ChapterController.php +++ b/app/Http/Controllers/ChapterController.php @@ -1,5 +1,6 @@ <?php namespace BookStack\Http\Controllers; +use BookStack\Actions\View; use BookStack\Entities\Models\Book; use BookStack\Entities\Tools\BookContents; use BookStack\Entities\Repos\ChapterRepo; @@ -64,7 +65,7 @@ class ChapterController extends Controller $sidebarTree = (new BookContents($chapter->book))->getTree(); $pages = $chapter->getVisiblePages(); - Views::add($chapter); + View::incrementFor($chapter); $this->setPageTitle($chapter->getShortName()); return view('chapters.show', [ diff --git a/app/Http/Controllers/PageController.php b/app/Http/Controllers/PageController.php index 30d33ad48..769ea8e69 100644 --- a/app/Http/Controllers/PageController.php +++ b/app/Http/Controllers/PageController.php @@ -1,5 +1,6 @@ <?php namespace BookStack\Http\Controllers; +use BookStack\Actions\View; use BookStack\Entities\Tools\BookContents; use BookStack\Entities\Tools\PageContent; use BookStack\Entities\Tools\PageEditActivity; @@ -141,7 +142,7 @@ class PageController extends Controller $page->load(['comments.createdBy']); } - Views::add($page); + View::incrementFor($page); $this->setPageTitle($page->getShortName()); return view('pages.show', [ 'page' => $page, diff --git a/app/Interfaces/Viewable.php b/app/Interfaces/Viewable.php new file mode 100644 index 000000000..4a7b6a013 --- /dev/null +++ b/app/Interfaces/Viewable.php @@ -0,0 +1,11 @@ +<?php namespace BookStack\Interfaces; + +use Illuminate\Database\Eloquent\Relations\MorphMany; + +interface Viewable +{ + /** + * Get all view instances for this viewable model. + */ + public function views(): MorphMany; +} \ No newline at end of file