Reduced the memory usage, db queries and cache hits loading revisions

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
This commit is contained in:
Dan Brown 2022-08-10 17:50:35 +01:00
parent 764489e30b
commit 031c67ba58
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
4 changed files with 94 additions and 78 deletions

View 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;
}

View 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,
]);
}

View File

@ -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>

View File

@ -17,11 +17,11 @@
<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>
@ -29,76 +29,8 @@
<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>