Eager load relations and optimize queries

This commit is contained in:
Igor Scheller 2023-12-13 14:42:25 +01:00 committed by xuwhite
parent 5fccc7e421
commit 05725cd58c
19 changed files with 71 additions and 29 deletions

View File

@ -175,7 +175,9 @@ function angeltype_controller()
$angeltype = AngelType::findOrFail(request()->input('angeltype_id'));
/** @var UserAngelType $user_angeltype */
$user_angeltype = UserAngelType::whereUserId($user->id)->where('angel_type_id', $angeltype->id)->first();
$members = $angeltype->userAngelTypes->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE);
$members = $angeltype->userAngelTypes
->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE)
->load(['state', 'personalData', 'contact']);
$days = angeltype_controller_shiftsFilterDays($angeltype);
$shiftsFilter = angeltype_controller_shiftsFilter($angeltype, $days);
if (request()->input('showFilledShifts')) {

View File

@ -7,6 +7,7 @@ use Engelsystem\Models\User\User;
use Engelsystem\ShiftCalendarRenderer;
use Engelsystem\ShiftsFilter;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Str;
/**
@ -297,13 +298,15 @@ function users_list_controller()
}
/** @var User[]|Collection $users */
$users = User::with(['contact', 'personalData', 'state'])
$users = User::with(['contact', 'personalData', 'state', 'shiftEntries' => function (HasMany $query) {
$query->where('freeloaded', true);
}])
->orderBy('name')
->get();
foreach ($users as $user) {
$user->setAttribute(
'freeloads',
$user->shiftEntries()
$user->shiftEntries
->where('freeloaded', true)
->count()
);

View File

@ -116,7 +116,7 @@ function Shifts_free($start, $end, ShiftsFilter $filter = null)
$shifts = collect($shifts);
return Shift::query()
return Shift::with(['location', 'shiftType'])
->whereIn('id', $shifts->pluck('id')->toArray())
->orderBy('shifts.start')
->get();
@ -189,12 +189,14 @@ function Shifts_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
]
);
$shifts = [];
$shifts = new Collection();
foreach ($shiftsData as $shift) {
$shifts[] = (new Shift())->forceFill($shift);
}
return collect($shifts);
$shifts->load(['location', 'shiftType']);
return $shifts;
}
/**
@ -354,7 +356,7 @@ function NeededAngeltype_by_Shift_and_Angeltype(Shift $shift, AngelType $angelty
*/
function ShiftEntries_by_ShiftsFilter(ShiftsFilter $shiftsFilter)
{
return ShiftEntry::with('user')
return ShiftEntry::with('user', 'user.state')
->join('shifts', 'shifts.id', 'shift_entries.shift_id')
->whereIn('shifts.location_id', $shiftsFilter->getLocations())
->whereBetween('start', [$shiftsFilter->getStart(), $shiftsFilter->getEnd()])
@ -638,12 +640,13 @@ function Shifts_by_user($userId, $include_freeloaded_comments = false)
]
);
$shifts = [];
$shifts = new Collection();
foreach ($shiftsData as $data) {
$shifts[] = (new Shift())->forceFill($data);
}
$shifts->load(['shiftType', 'location']);
return collect($shifts);
return $shifts;
}
/**

View File

@ -18,6 +18,7 @@ function UserWorkLogsForUser($userId, Carbon $sinceTime = null)
if ($sinceTime) {
$worklogs = $worklogs->whereDate('worked_at', '>=', $sinceTime);
}
$worklogs->with(['user', 'creator']);
return $worklogs->get();
}

View File

@ -151,7 +151,7 @@ function admin_active()
}
}
$query = User::with('personalData')
$query = User::with(['personalData', 'state'])
->selectRaw(
sprintf(
'

View File

@ -65,7 +65,7 @@ function admin_arrive()
}
/** @var User[] $users */
$users = User::with('personalData')->orderBy('name')->get();
$users = User::with(['personalData', 'state'])->orderBy('name')->get();
$arrival_count_at_day = [];
$planned_arrival_count_at_day = [];
$planned_departure_count_at_day = [];

View File

@ -40,7 +40,7 @@ function admin_free()
/** @var User[] $users */
$users = [];
if ($request->has('submit')) {
$query = User::with('personalData')
$query = User::with(['personalData', 'contact', 'state'])
->select('users.*')
->leftJoin('shift_entries', 'users.id', 'shift_entries.user_id')
->leftJoin('users_state', 'users.id', 'users_state.user_id')

View File

@ -21,13 +21,13 @@ function admin_groups()
$html = '';
$request = request();
/** @var Group[]|Collection $groups */
$groups = Group::query()->orderBy('name')->get();
$groups = Group::with('privileges')->orderBy('name')->get();
if (!$request->has('action')) {
$groups_table = [];
foreach ($groups as $group) {
/** @var Privilege[]|Collection $privileges */
$privileges = $group->privileges()->orderBy('name')->get();
$privileges = $group->privileges->sortBy('name');
$privileges_html = [];
foreach ($privileges as $privilege) {

View File

@ -326,6 +326,9 @@ function admin_shifts()
$shifts_table = [];
foreach ($shifts as $shift) {
$shiftType = $shifttypes_source->find($shift['shift_type_id']);
$location = $locations->find($shift['location_id']);
/** @var Carbon $start */
$start = $shift['start'];
/** @var Carbon $end */
@ -340,9 +343,9 @@ function admin_shifts()
. '</span>'
. ', ' . round($end->copy()->diffInMinutes($start) / 60, 2) . 'h'
. '<br>'
. location_name_render(Location::find($shift['location_id'])),
. location_name_render($location),
'title' =>
htmlspecialchars(ShiftType::find($shifttype_id)->name)
htmlspecialchars($shiftType->name)
. ($shift['title'] ? '<br />' . htmlspecialchars($shift['title']) : ''),
'needed_angels' => '',
];

View File

@ -552,8 +552,7 @@ class ImportSchedule extends BaseController
$existingShifts = $this->getScheduleShiftsByGuid($scheduleUrl, $scheduleEventsGuidList);
foreach ($existingShifts as $scheduleShift) {
$guid = $scheduleShift->guid;
/** @var Shift $shift */
$shift = Shift::with('location')->find($scheduleShift->shift_id);
$shift = $scheduleShift->shift;
$event = $scheduleEvents[$guid];
$location = $locations->where('name', $event->getRoom()->getName())->first();
@ -620,7 +619,7 @@ class ImportSchedule extends BaseController
*/
protected function getScheduleShiftsByGuid(ScheduleUrl $scheduleUrl, array $events)
{
return ScheduleShift::query()
return ScheduleShift::with('shift.location')
->whereIn('guid', $events)
->where('schedule_id', $scheduleUrl->id)
->get();
@ -633,7 +632,7 @@ class ImportSchedule extends BaseController
*/
protected function getScheduleShiftsWhereNotGuid(ScheduleUrl $scheduleUrl, array $events)
{
return ScheduleShift::query()
return ScheduleShift::with('shift.location')
->whereNotIn('guid', $events)
->where('schedule_id', $scheduleUrl->id)
->get();

View File

@ -297,7 +297,6 @@ function view_user_shifts()
return page([
div('col-md-12', [
msg(),
view(__DIR__ . '/../../resources/views/pages/user-shifts.html', [
'title' => shifts_title(),
'add_link' => auth()->can('admin_shifts') ? $link : '',

View File

@ -38,7 +38,8 @@ class QuestionsController extends BaseController
$questions = $this->question
->orderBy('answered_at')
->orderByDesc('created_at')
->get();
->get()
->load(['user.state', 'answerer.state']);
return $this->response->withView(
'pages/questions/overview.twig',

View File

@ -218,7 +218,8 @@ class MessagesController extends BaseController
$join->on('messages.id', '=', 'conversations.last_id');
})
->orderBy('created_at', 'DESC')
->get();
->get()
->load(['receiver.personalData', 'receiver.state']);
}
protected function raw(mixed $value): QueryExpression

View File

@ -53,8 +53,7 @@ class NewsController extends BaseController
$newsId = (int) $request->getAttribute('news_id');
$news = $this->news
->with('user')
->with('comments')
->with(['user', 'comments.user.state', 'comments.user.personalData'])
->findOrFail($newsId);
return $this->renderView('pages/news/news.twig', ['news' => $news]);

View File

@ -36,7 +36,8 @@ class QuestionsController extends BaseController
->whereUserId($this->auth->user()->id)
->orderByDesc('answered_at')
->orderBy('created_at')
->get();
->get()
->load(['user.state', 'answerer.state']);
return $this->response->withView(
'pages/questions/overview.twig',

View File

@ -42,7 +42,7 @@ class Authenticator
}
$this->user = $this->userFromSession();
if (!$this->user && request()->getAttribute('route-api-accessible', false)) {
if (!$this->user && $this->isApiRequest()) {
$this->user = $this->userFromApi();
}
@ -102,8 +102,10 @@ class Authenticator
if ($user) {
$this->permissions = $user->privileges->pluck('name')->toArray();
$user->last_login_at = new Carbon();
$user->save();
if ($user->last_login_at < Carbon::now()->subMinutes(5) && !$this->isApiRequest()) {
$user->last_login_at = Carbon::now();
$user->save(['touch' => false]);
}
} elseif ($this->session->get('user_id')) {
$this->session->remove('user_id');
}
@ -206,6 +208,11 @@ class Authenticator
return $this->user;
}
protected function isApiRequest(): bool
{
return (bool) request()->getAttribute('route-api-accessible', false);
}
public function setPassword(User $user, string $password): void
{
$user->password = password_hash($password, $this->passwordAlgorithm);

View File

@ -14,6 +14,8 @@ use function array_key_exists;
class Globals extends TwigExtension implements GlobalsInterface
{
protected array $globals = [];
public function __construct(protected Authenticator $auth, protected Request $request)
{
}
@ -22,6 +24,18 @@ class Globals extends TwigExtension implements GlobalsInterface
* Returns a list of global variables to add to the existing list.
*/
public function getGlobals(): array
{
if (empty($this->globals)) {
$this->globals = $this->getGlobalValues();
}
return $this->globals;
}
/**
* Generates the list of global variables
*/
protected function getGlobalValues(): array
{
$user = $this->auth->user();
$themes = config('themes');

View File

@ -207,6 +207,7 @@ class AuthenticatorTest extends ServiceProviderTest
/**
* @covers \Engelsystem\Helpers\Authenticator::can
* @covers \Engelsystem\Helpers\Authenticator::isApiRequest
*/
public function testCan(): void
{

View File

@ -31,6 +31,7 @@ class GlobalsTest extends ExtensionTest
/**
* @covers \Engelsystem\Renderer\Twig\Extensions\Globals::__construct
* @covers \Engelsystem\Renderer\Twig\Extensions\Globals::getGlobals
* @covers \Engelsystem\Renderer\Twig\Extensions\Globals::getGlobalValues
*/
public function testGetGlobals(): void
{
@ -82,6 +83,7 @@ class GlobalsTest extends ExtensionTest
$this->assertGlobalsExists('theme', $theme2, $globals);
// User
$extension = new Globals($auth, $request);
$globals = $extension->getGlobals();
$this->assertGlobalsExists('user', $user, $globals);
$this->assertGlobalsExists('user_messages', 0, $globals);
@ -89,15 +91,21 @@ class GlobalsTest extends ExtensionTest
$this->assertGlobalsExists('theme', $theme, $globals);
// User with not available theme configured
$extension = new Globals($auth, $request);
$user->settings->theme = 9999;
$globals = $extension->getGlobals();
$this->assertGlobalsExists('themeId', 42, $globals);
// Request query parameter
$extension = new Globals($auth, $request);
$request->query->set('theme', 1337);
$globals = $extension->getGlobals();
$this->assertGlobalsExists('user', [], $globals);
$this->assertGlobalsExists('themeId', 1337, $globals);
$this->assertGlobalsExists('theme', $theme3, $globals);
// Second retrieval is loaded directly
$globals = $extension->getGlobals();
$this->assertGlobalsExists('themeId', 1337, $globals);
}
}