Optimize queries for statistics.

This commit is contained in:
James Cole
2025-09-26 06:05:37 +02:00
parent 08879d31ba
commit 4ec2fcdb8a
92 changed files with 6499 additions and 6514 deletions

View File

@@ -46,19 +46,17 @@ trait AppendsLocationData
return $return;
}
private function validLongitude(string $longitude): bool
{
$number = (float) $longitude;
return $number >= -180 && $number <= 180;
}
private function validLatitude(string $latitude): bool
{
$number = (float) $latitude;
return $number >= -90 && $number <= 90;
}
/**
* Abstract method stolen from "InteractsWithInput".
*
* @param null $key
* @param bool $default
*
* @return mixed
*
* @SuppressWarnings("PHPMD.BooleanArgumentFlag")
*/
abstract public function boolean($key = null, $default = false);
/**
* Abstract method.
@@ -69,6 +67,22 @@ trait AppendsLocationData
*/
abstract public function has($key);
/**
* Abstract method.
*
* @return string
*/
abstract public function method();
/**
* Abstract method.
*
* @param mixed ...$patterns
*
* @return mixed
*/
abstract public function routeIs(...$patterns);
/**
* Read the submitted Request data and add new or updated Location data to the array.
*/
@@ -82,12 +96,12 @@ trait AppendsLocationData
$data['latitude'] = null;
$data['zoom_level'] = null;
$longitudeKey = $this->getLocationKey($prefix, 'longitude');
$latitudeKey = $this->getLocationKey($prefix, 'latitude');
$zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level');
$isValidPOST = $this->isValidPost($prefix);
$isValidPUT = $this->isValidPUT($prefix);
$isValidEmptyPUT = $this->isValidEmptyPUT($prefix);
$longitudeKey = $this->getLocationKey($prefix, 'longitude');
$latitudeKey = $this->getLocationKey($prefix, 'latitude');
$zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level');
$isValidPOST = $this->isValidPost($prefix);
$isValidPUT = $this->isValidPUT($prefix);
$isValidEmptyPUT = $this->isValidEmptyPUT($prefix);
// for a POST (store), all fields must be present and not NULL.
if ($isValidPOST) {
@@ -132,72 +146,22 @@ trait AppendsLocationData
return sprintf('%s_%s', $prefix, $key);
}
private function isValidPost(?string $prefix): bool
private function isValidEmptyPUT(?string $prefix): bool
{
app('log')->debug('Now in isValidPost()');
$longitudeKey = $this->getLocationKey($prefix, 'longitude');
$latitudeKey = $this->getLocationKey($prefix, 'latitude');
$zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level');
$hasLocationKey = $this->getLocationKey($prefix, 'has_location');
// fields must not be null:
if (null !== $this->get($longitudeKey) && null !== $this->get($latitudeKey) && null !== $this->get($zoomLevelKey)) {
app('log')->debug('All fields present');
// if is POST and route contains API, this is enough:
if ('POST' === $this->method() && $this->routeIs('api.v1.*')) {
app('log')->debug('Is API location');
$longitudeKey = $this->getLocationKey($prefix, 'longitude');
$latitudeKey = $this->getLocationKey($prefix, 'latitude');
$zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level');
return true;
}
// if is POST and route does not contain API, must also have "has_location" = true
if ('POST' === $this->method() && $this->routeIs('*.store') && !$this->routeIs('api.v1.*') && '' !== $hasLocationKey) {
app('log')->debug('Is POST + store route.');
$hasLocation = $this->boolean($hasLocationKey);
if (true === $hasLocation) {
app('log')->debug('Has form form location');
return true;
}
app('log')->debug('Does not have form location');
return false;
}
app('log')->debug('Is not POST API or POST form');
return false;
}
app('log')->debug('Fields not present');
return false;
return (
null === $this->get($longitudeKey)
&& null === $this->get($latitudeKey)
&& null === $this->get($zoomLevelKey))
&& (
'PUT' === $this->method()
|| ('POST' === $this->method() && $this->routeIs('*.update'))
);
}
/**
* Abstract method.
*
* @return string
*/
abstract public function method();
/**
* Abstract method.
*
* @param mixed ...$patterns
*
* @return mixed
*/
abstract public function routeIs(...$patterns);
/**
* Abstract method stolen from "InteractsWithInput".
*
* @param null $key
* @param bool $default
*
* @return mixed
*
* @SuppressWarnings("PHPMD.BooleanArgumentFlag")
*/
abstract public function boolean($key = null, $default = false);
private function isValidPUT(?string $prefix): bool
{
$longitudeKey = $this->getLocationKey($prefix, 'longitude');
@@ -238,19 +202,55 @@ trait AppendsLocationData
return false;
}
private function isValidEmptyPUT(?string $prefix): bool
private function isValidPost(?string $prefix): bool
{
$longitudeKey = $this->getLocationKey($prefix, 'longitude');
$latitudeKey = $this->getLocationKey($prefix, 'latitude');
$zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level');
app('log')->debug('Now in isValidPost()');
$longitudeKey = $this->getLocationKey($prefix, 'longitude');
$latitudeKey = $this->getLocationKey($prefix, 'latitude');
$zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level');
$hasLocationKey = $this->getLocationKey($prefix, 'has_location');
// fields must not be null:
if (null !== $this->get($longitudeKey) && null !== $this->get($latitudeKey) && null !== $this->get($zoomLevelKey)) {
app('log')->debug('All fields present');
// if is POST and route contains API, this is enough:
if ('POST' === $this->method() && $this->routeIs('api.v1.*')) {
app('log')->debug('Is API location');
return (
null === $this->get($longitudeKey)
&& null === $this->get($latitudeKey)
&& null === $this->get($zoomLevelKey))
&& (
'PUT' === $this->method()
|| ('POST' === $this->method() && $this->routeIs('*.update'))
);
return true;
}
// if is POST and route does not contain API, must also have "has_location" = true
if ('POST' === $this->method() && $this->routeIs('*.store') && !$this->routeIs('api.v1.*') && '' !== $hasLocationKey) {
app('log')->debug('Is POST + store route.');
$hasLocation = $this->boolean($hasLocationKey);
if (true === $hasLocation) {
app('log')->debug('Has form form location');
return true;
}
app('log')->debug('Does not have form location');
return false;
}
app('log')->debug('Is not POST API or POST form');
return false;
}
app('log')->debug('Fields not present');
return false;
}
private function validLatitude(string $latitude): bool
{
$number = (float)$latitude;
return $number >= -90 && $number <= 90;
}
private function validLongitude(string $longitude): bool
{
$number = (float)$longitude;
return $number >= -180 && $number <= 180;
}
}

View File

@@ -40,7 +40,7 @@ trait ChecksLogin
{
app('log')->debug(sprintf('Now in %s', __METHOD__));
// Only allow logged-in users
$check = auth()->check();
$check = auth()->check();
if (!$check) {
return false;
}
@@ -79,19 +79,19 @@ trait ChecksLogin
public function getUserGroup(): ?UserGroup
{
/** @var User $user */
$user = auth()->user();
$user = auth()->user();
app('log')->debug('Now in getUserGroup()');
/** @var null|UserGroup $userGroup */
$userGroup = $this->route()?->parameter('userGroup');
if (null === $userGroup) {
app('log')->debug('Request class has no userGroup parameter, but perhaps there is a parameter.');
$userGroupId = (int) $this->get('user_group_id');
$userGroupId = (int)$this->get('user_group_id');
if (0 === $userGroupId) {
app('log')->debug(sprintf('Request class has no user_group_id parameter, grab default from user (group #%d).', $user->user_group_id));
$userGroupId = (int) $user->user_group_id;
$userGroupId = (int)$user->user_group_id;
}
$userGroup = UserGroup::find($userGroupId);
$userGroup = UserGroup::find($userGroupId);
if (null === $userGroup) {
app('log')->error(sprintf('Request class has user_group_id (#%d), but group does not exist.', $userGroupId));

View File

@@ -31,7 +31,6 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use function Safe\preg_replace;
/**
@@ -99,28 +98,6 @@ trait ConvertsDataTypes
return Steam::filterSpaces($string);
}
public function convertSortParameters(string $field, string $class): array
{
// assume this all works, because the validator would have caught any errors.
$parameter = (string)request()->query->get($field);
if ('' === $parameter) {
return [];
}
$parts = explode(',', $parameter);
$sortParameters = [];
foreach ($parts as $part) {
$part = trim($part);
$direction = 'asc';
if ('-' === $part[0]) {
$part = substr($part, 1);
$direction = 'desc';
}
$sortParameters[] = [$part, $direction];
}
return $sortParameters;
}
public function clearString(?string $string): ?string
{
$string = $this->clearStringKeepNewlines($string);
@@ -159,6 +136,36 @@ trait ConvertsDataTypes
return Steam::filterSpaces($this->convertString($field));
}
/**
* Return integer value.
*/
public function convertInteger(string $field): int
{
return (int)$this->get($field);
}
public function convertSortParameters(string $field, string $class): array
{
// assume this all works, because the validator would have caught any errors.
$parameter = (string)request()->query->get($field);
if ('' === $parameter) {
return [];
}
$parts = explode(',', $parameter);
$sortParameters = [];
foreach ($parts as $part) {
$part = trim($part);
$direction = 'asc';
if ('-' === $part[0]) {
$part = substr($part, 1);
$direction = 'desc';
}
$sortParameters[] = [$part, $direction];
}
return $sortParameters;
}
/**
* Return string value.
*/
@@ -178,14 +185,6 @@ trait ConvertsDataTypes
*/
abstract public function get(string $key, mixed $default = null): mixed;
/**
* Return integer value.
*/
public function convertInteger(string $field): int
{
return (int)$this->get($field);
}
/**
* TODO duplicate, see SelectTransactionsRequest
*
@@ -218,6 +217,16 @@ trait ConvertsDataTypes
return $collection;
}
/**
* Abstract method that always exists in the Request classes that use this
* trait, OR a stub needs to be added by any other class that uses this train.
*
* @param mixed $key
*
* @return mixed
*/
abstract public function has($key);
/**
* Return string value with newlines.
*/
@@ -386,16 +395,6 @@ trait ConvertsDataTypes
return $return;
}
/**
* Abstract method that always exists in the Request classes that use this
* trait, OR a stub needs to be added by any other class that uses this train.
*
* @param mixed $key
*
* @return mixed
*/
abstract public function has($key);
/**
* Return date or NULL.
*/
@@ -418,6 +417,21 @@ trait ConvertsDataTypes
return $result;
}
/**
* Parse to integer
*/
protected function integerFromValue(?string $string): ?int
{
if (null === $string) {
return null;
}
if ('' === $string) {
return null;
}
return (int)$string;
}
/**
* Return integer value, or NULL when it's not set.
*/
@@ -445,7 +459,7 @@ trait ConvertsDataTypes
if (!is_array($entry)) {
continue;
}
$amount = null;
$amount = null;
if (array_key_exists('current_amount', $entry)) {
$amount = $this->clearString((string)($entry['current_amount'] ?? '0'));
if (null === $entry['current_amount']) {
@@ -463,19 +477,4 @@ trait ConvertsDataTypes
return $return;
}
/**
* Parse to integer
*/
protected function integerFromValue(?string $string): ?int
{
if (null === $string) {
return null;
}
if ('' === $string) {
return null;
}
return (int)$string;
}
}

View File

@@ -38,12 +38,12 @@ trait GetRecurrenceData
foreach ($stringKeys as $key) {
if (array_key_exists($key, $transaction)) {
$return[$key] = (string) $transaction[$key];
$return[$key] = (string)$transaction[$key];
}
}
foreach ($intKeys as $key) {
if (array_key_exists($key, $transaction)) {
$return[$key] = (int) $transaction[$key];
$return[$key] = (int)$transaction[$key];
}
}
foreach ($keys as $key) {

View File

@@ -25,10 +25,10 @@ declare(strict_types=1);
namespace FireflyIII\Support\Request;
use Illuminate\Validation\Validator;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Models\Webhook;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
trait ValidatesWebhooks
{
@@ -40,9 +40,9 @@ trait ValidatesWebhooks
if (count($validator->failed()) > 0) {
return;
}
$data = $validator->getData();
$triggers = $data['triggers'] ?? [];
$responses = $data['responses'] ?? [];
$data = $validator->getData();
$triggers = $data['triggers'] ?? [];
$responses = $data['responses'] ?? [];
if (0 === count($triggers) || 0 === count($responses)) {
Log::debug('No trigger or response, return.');