]> BookStack Code Mirror - bookstack/commitdiff
Reduced the memory usage, db queries and cache hits loading revisions
authorDan Brown <redacted>
Wed, 10 Aug 2022 16:50:35 +0000 (17:50 +0100)
committerDan Brown <redacted>
Wed, 10 Aug 2022 16:50:35 +0000 (17:50 +0100)
Updated revision listing to only fetch required fields, massively
reducing memory usage by not loading content.
This also updates user avatar handling to effectively cache the avatar
url within request to avoid re-searching from cache, which may improve
performance of others areas of the application.
This also upates handling of the revisions list view to extract table
row to its own view to break things down a bit.

For #3633

app/Auth/User.php
app/Http/Controllers/PageRevisionController.php
resources/views/pages/parts/revision-table-row.blade.php [new file with mode: 0644]
resources/views/pages/revisions.blade.php

index c060d5ec8e65ad724e2eff8e8dee8e901e931d9f..393965e0d8608950520418f0dfd39727ea0f2e97 100644 (file)
@@ -80,6 +80,11 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
      */
     protected ?Collection $permissions;
 
+    /**
+     * This holds the user's avatar URL when loaded to prevent re-calculating within the same request.
+     */
+    protected string $avatarUrl = '';
+
     /**
      * This holds the default user when loaded.
      *
@@ -233,12 +238,17 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
             return $default;
         }
 
+        if (!empty($this->avatarUrl)) {
+            return $this->avatarUrl;
+        }
+
         try {
             $avatar = $this->avatar ? url($this->avatar->getThumb($size, $size, false)) : $default;
         } catch (Exception $err) {
             $avatar = $default;
         }
 
+        $this->avatarUrl = $avatar;
         return $avatar;
     }
 
index 08651896069910ca8ac132b66ee6efb0e917c3bc..ea80e13cd67ceff1daed211861de9ab3bff97d9c 100644 (file)
@@ -11,11 +11,8 @@ use Ssddanbrown\HtmlDiff\Diff;
 
 class PageRevisionController extends Controller
 {
-    protected $pageRepo;
+    protected PageRepo $pageRepo;
 
-    /**
-     * PageRevisionController constructor.
-     */
     public function __construct(PageRepo $pageRepo)
     {
         $this->pageRepo = $pageRepo;
@@ -29,11 +26,19 @@ class PageRevisionController extends Controller
     public function index(string $bookSlug, string $pageSlug)
     {
         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
-        $this->setPageTitle(trans('entities.pages_revisions_named', ['pageName'=>$page->getShortName()]));
+        $revisions = $page->revisions()->select([
+                'id', 'page_id', 'name', 'created_at', 'created_by', 'updated_at',
+                'type', 'revision_number', 'summary',
+            ])
+            ->selectRaw("IF(markdown = '', false, true) as is_markdown")
+            ->with(['page.book', 'createdBy'])
+            ->get();
+
+        $this->setPageTitle(trans('entities.pages_revisions_named', ['pageName' => $page->getShortName()]));
 
         return view('pages.revisions', [
+            'revisions' => $revisions,
             'page'    => $page,
-            'current' => $page,
         ]);
     }
 
diff --git a/resources/views/pages/parts/revision-table-row.blade.php b/resources/views/pages/parts/revision-table-row.blade.php
new file mode 100644 (file)
index 0000000..bd891d6
--- /dev/null
@@ -0,0 +1,69 @@
+<tr>
+    <td>{{ $revision->revision_number == 0 ? '' : $revision->revision_number }}</td>
+    <td>
+        {{ $revision->name }}
+        <br>
+        <small class="text-muted">({{ $revision->is_markdown ? 'Markdown' : 'WYSIWYG' }})</small>
+    </td>
+    <td style="line-height: 0;" width="30">
+        @if($revision->createdBy)
+            <img class="avatar" src="{{ $revision->createdBy->getAvatar(30) }}" alt="{{ $revision->createdBy->name }}">
+        @endif
+    </td>
+    <td width="260">
+        @if($revision->createdBy) {{ $revision->createdBy->name }} @else {{ trans('common.deleted_user') }} @endif
+        <br>
+        <div class="text-muted">
+            <small>{{ $revision->created_at->formatLocalized('%e %B %Y %H:%M:%S') }}</small>
+            <small>({{ $revision->created_at->diffForHumans() }})</small>
+        </div>
+    </td>
+    <td>
+        {{ $revision->summary }}
+    </td>
+    <td class="actions text-small text-right">
+        <a href="{{ $revision->getUrl('changes') }}" target="_blank" rel="noopener">{{ trans('entities.pages_revisions_changes') }}</a>
+        <span class="text-muted">&nbsp;|&nbsp;</span>
+
+
+        @if ($index === 0)
+            <a target="_blank" rel="noopener" href="{{ $revision->page->getUrl() }}"><i>{{ trans('entities.pages_revisions_current') }}</i></a>
+        @else
+            <a href="{{ $revision->getUrl() }}" target="_blank" rel="noopener">{{ trans('entities.pages_revisions_preview') }}</a>
+            <span class="text-muted">&nbsp;|&nbsp;</span>
+            <div component="dropdown" class="dropdown-container">
+                <a refs="dropdown@toggle" href="#" aria-haspopup="true" aria-expanded="false">{{ trans('entities.pages_revisions_restore') }}</a>
+                <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
+                    <li class="px-m py-s"><small class="text-muted">{{trans('entities.revision_restore_confirm')}}</small></li>
+                    <li>
+                        <form action="{{ $revision->getUrl('/restore') }}" method="POST">
+                            {!! csrf_field() !!}
+                            <input type="hidden" name="_method" value="PUT">
+                            <button type="submit" class="text-primary icon-item">
+                                @icon('history')
+                                <div>{{ trans('entities.pages_revisions_restore') }}</div>
+                            </button>
+                        </form>
+                    </li>
+                </ul>
+            </div>
+            <span class="text-muted">&nbsp;|&nbsp;</span>
+            <div component="dropdown" class="dropdown-container">
+                <a refs="dropdown@toggle" href="#" aria-haspopup="true" aria-expanded="false">{{ trans('common.delete') }}</a>
+                <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
+                    <li class="px-m py-s"><small class="text-muted">{{trans('entities.revision_delete_confirm')}}</small></li>
+                    <li>
+                        <form action="{{ $revision->getUrl('/delete/') }}" method="POST">
+                            {!! csrf_field() !!}
+                            <input type="hidden" name="_method" value="DELETE">
+                            <button type="submit" class="text-neg icon-item">
+                                @icon('delete')
+                                <div>{{ trans('common.delete') }}</div>
+                            </button>
+                        </form>
+                    </li>
+                </ul>
+            </div>
+        @endif
+    </td>
+</tr>
\ No newline at end of file
index fb9e891bbcf8c34f918cc2a03cc1621fb655ce3c..3e7edad997fe1c8cdb823abd2762ca67475a7d64 100644 (file)
 
         <main class="card content-wrap">
             <h1 class="list-heading">{{ trans('entities.pages_revisions') }}</h1>
-            @if(count($page->revisions) > 0)
+            @if(count($revisions) > 0)
 
                 <table class="table">
                     <tr>
-                        <th width="40">{{ trans('entities.pages_revisions_number') }}</th>
+                        <th width="56">{{ trans('entities.pages_revisions_number') }}</th>
                         <th>
                             {{ trans('entities.pages_name') }} / {{ trans('entities.pages_revisions_editor') }}
                         </th>
                         <th>{{ trans('entities.pages_revisions_changelog') }}</th>
                         <th class="text-right">{{ trans('common.actions') }}</th>
                     </tr>
-                    @foreach($page->revisions as $index => $revision)
-                        <tr>
-                            <td>{{ $revision->revision_number == 0 ? '' : $revision->revision_number }}</td>
-                            <td>
-                                {{ $revision->name }}
-                                <br>
-                                <small class="text-muted">({{ $revision->markdown ? 'Markdown' : 'WYSIWYG' }})</small>
-                            </td>
-                            <td style="line-height: 0;" width="30">
-                                @if($revision->createdBy)
-                                    <img class="avatar" src="{{ $revision->createdBy->getAvatar(30) }}" alt="{{ $revision->createdBy->name }}">
-                                @endif
-                            </td>
-                            <td width="260">
-                                @if($revision->createdBy) {{ $revision->createdBy->name }} @else {{ trans('common.deleted_user') }} @endif
-                                    <br>
-                                <div class="text-muted">
-                                    <small>{{ $revision->created_at->formatLocalized('%e %B %Y %H:%M:%S') }}</small>
-                                    <small>({{ $revision->created_at->diffForHumans() }})</small>
-                                </div>
-                            </td>
-                            <td>
-                                {{ $revision->summary }}
-                            </td>
-                            <td class="actions text-small text-right">
-                                <a href="{{ $revision->getUrl('changes') }}" target="_blank" rel="noopener">{{ trans('entities.pages_revisions_changes') }}</a>
-                                <span class="text-muted">&nbsp;|&nbsp;</span>
-
-
-                                @if ($index === 0)
-                                    <a target="_blank" rel="noopener" href="{{ $page->getUrl() }}"><i>{{ trans('entities.pages_revisions_current') }}</i></a>
-                                @else
-                                    <a href="{{ $revision->getUrl() }}" target="_blank" rel="noopener">{{ trans('entities.pages_revisions_preview') }}</a>
-                                    <span class="text-muted">&nbsp;|&nbsp;</span>
-                                    <div component="dropdown" class="dropdown-container">
-                                        <a refs="dropdown@toggle" href="#" aria-haspopup="true" aria-expanded="false">{{ trans('entities.pages_revisions_restore') }}</a>
-                                        <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
-                                            <li class="px-m py-s"><small class="text-muted">{{trans('entities.revision_restore_confirm')}}</small></li>
-                                            <li>
-                                                <form action="{{ $revision->getUrl('/restore') }}" method="POST">
-                                                    {!! csrf_field() !!}
-                                                    <input type="hidden" name="_method" value="PUT">
-                                                    <button type="submit" class="text-primary icon-item">
-                                                        @icon('history')
-                                                        <div>{{ trans('entities.pages_revisions_restore') }}</div>
-                                                    </button>
-                                                </form>
-                                            </li>
-                                        </ul>
-                                    </div>
-                                    <span class="text-muted">&nbsp;|&nbsp;</span>
-                                    <div component="dropdown" class="dropdown-container">
-                                        <a refs="dropdown@toggle" href="#" aria-haspopup="true" aria-expanded="false">{{ trans('common.delete') }}</a>
-                                        <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
-                                            <li class="px-m py-s"><small class="text-muted">{{trans('entities.revision_delete_confirm')}}</small></li>
-                                            <li>
-                                                <form action="{{ $revision->getUrl('/delete/') }}" method="POST">
-                                                    {!! csrf_field() !!}
-                                                    <input type="hidden" name="_method" value="DELETE">
-                                                    <button type="submit" class="text-neg icon-item">
-                                                        @icon('delete')
-                                                        <div>{{ trans('common.delete') }}</div>
-                                                    </button>
-                                                </form>
-                                            </li>
-                                        </ul>
-                                    </div>
-                                @endif
-                            </td>
-                        </tr>
+                    @foreach($revisions as $index => $revision)
+                        @include('pages.parts.revision-table-row', ['revision' => $revision])
                     @endforeach
                 </table>