Improved organisation of Services

This commit is contained in:
Jan Böhmer 2022-12-18 17:28:42 +01:00
parent c3308aaf24
commit a4eae19a1f
56 changed files with 100 additions and 97 deletions

View file

@ -0,0 +1,138 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Services\Formatters;
use App\Entity\Parts\MeasurementUnit;
use App\Services\Formatters\SIFormatter;
use InvalidArgumentException;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* This service formats an part amout using a Measurement Unit.
*/
class AmountFormatter
{
protected SIFormatter $siFormatter;
public function __construct(SIFormatter $siFormatter)
{
$this->siFormatter = $siFormatter;
}
/**
* Formats the given value using the measurement unit and options.
*
* @param float|string|int $value
* @param MeasurementUnit|null $unit The measurement unit, whose unit symbol should be used for formatting.
* If set to null, it is assumed that the part amount is measured in pieces.
*
* @return string The formatted string
*
* @throws InvalidArgumentException thrown if $value is not numeric
*/
public function format($value, ?MeasurementUnit $unit = null, array $options = []): string
{
if (!is_numeric($value)) {
throw new InvalidArgumentException('$value must be an numeric value!');
}
$value = (float) $value;
//Find out what options to use
$resolver = new OptionsResolver();
$resolver->setDefault('measurement_unit', $unit);
$this->configureOptions($resolver);
$options = $resolver->resolve($options);
if ($options['is_integer']) {
$value = round($value);
}
//If the measurement unit uses a SI prefix format it that way.
if ($options['show_prefix']) {
return $this->siFormatter->format($value, $options['unit'], $options['decimals']);
}
//Otherwise just output it
if (!empty($options['unit'])) {
$format_string = '%.'.$options['decimals'].'f '.$options['unit'];
} else { //Dont add space after number if no unit was specified
$format_string = '%.'.$options['decimals'].'f';
}
return sprintf($format_string, $value);
}
protected function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'show_prefix' => static function (Options $options) {
if (null !== $options['measurement_unit']) {
/** @var MeasurementUnit $unit */
$unit = $options['measurement_unit'];
return $unit->isUseSIPrefix();
}
return false;
},
'is_integer' => static function (Options $options) {
if (null !== $options['measurement_unit']) {
/** @var MeasurementUnit $unit */
$unit = $options['measurement_unit'];
return $unit->isInteger();
}
return true;
},
'unit' => static function (Options $options) {
if (null !== $options['measurement_unit']) {
/** @var MeasurementUnit $unit */
$unit = $options['measurement_unit'];
//When no unit is set, return empty string so that this can be formatted properly
return $unit->getUnit() ?? '';
}
return '';
},
'decimals' => 2,
'error_mapping' => [
'.' => 'value',
],
]);
$resolver->setAllowedTypes('decimals', 'int');
$resolver->setNormalizer('decimals', static function (Options $options, $value) {
// If the unit is integer based, then dont show any decimals
if ($options['is_integer']) {
return 0;
}
return $value;
});
}
}

View file

@ -0,0 +1,57 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Services\Formatters;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* This class allows you to convert markdown text to HTML.
*/
class MarkdownParser
{
protected TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
/**
* Mark the markdown for rendering.
* The rendering of markdown is done on client side.
*
* @param string $markdown the markdown text that should be parsed to html
* @param bool $inline_mode When true, p blocks will have no margins behind them
*
* @return string the markdown in a version that can be parsed on client side
*/
public function markForRendering(string $markdown, bool $inline_mode = false): string
{
return sprintf(
'<div class="markdown %s" data-markdown="%s" data-controller="common--markdown">%s</div>',
$inline_mode ? 'markdown-inline' : '', //Add class if inline mode is enabled, to prevent margin after p
htmlspecialchars($markdown),
$this->translator->trans('markdown.loading')
);
}
}

View file

@ -0,0 +1,64 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Services\Formatters;
use App\Entity\PriceInformations\Currency;
use Locale;
use NumberFormatter;
class MoneyFormatter
{
protected string $base_currency;
protected string $locale;
public function __construct(string $base_currency)
{
$this->base_currency = $base_currency;
$this->locale = Locale::getDefault();
}
/**
* Format the the given value in the given currency.
*
* @param string|float $value the value that should be formatted
* @param Currency|null $currency The currency that should be used for formatting. If null the global one is used
* @param int $decimals the number of decimals that should be shown
* @param bool $show_all_digits if set to true, all digits are shown, even if they are null
*/
public function format($value, ?Currency $currency = null, int $decimals = 5, bool $show_all_digits = false): string
{
$iso_code = $this->base_currency;
if (null !== $currency && !empty($currency->getIsoCode())) {
$iso_code = $currency->getIsoCode();
}
$number_formatter = new NumberFormatter($this->locale, NumberFormatter::CURRENCY);
if ($show_all_digits) {
$number_formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, $decimals);
} else {
$number_formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $decimals);
}
return $number_formatter->formatCurrency((float) $value, $iso_code);
}
}

View file

@ -0,0 +1,104 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Services\Formatters;
/**
* A service that helps you to format values using the SI prefixes.
*/
class SIFormatter
{
/**
* Returns the magnitude of a value (the count of decimal place of the highest decimal place).
* For example, for 100 (=10^2) this function returns 2. For -2500 (=-2.5*10^3) this function returns 3.
*
* @param float $value the value of which the magnitude should be determined
*
* @return int The magnitude of the value
*/
public function getMagnitude(float $value): int
{
return (int) floor(log10(abs($value)));
}
/**
* Returns the best SI prefix (and its corresponding divisor) for the given magnitude.
*
* @param int $magnitude the magnitude for which the prefix should be determined
*
* @return array A array, containing the divisor in first element, and the prefix symbol in second. For example, [1000, "k"].
*/
public function getPrefixByMagnitude(int $magnitude): array
{
$prefixes_pos = ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
$prefixes_neg = ['', 'm', 'μ', 'n', 'p', 'f', 'a', 'z', 'y'];
if ($magnitude >= 0) {
$nearest = (int) floor(abs($magnitude) / 3);
$symbol = $prefixes_pos[$nearest];
} else {
$nearest = (int) round(abs($magnitude) / 3);
$symbol = $prefixes_neg[$nearest];
}
if ($magnitude < 0) {
$nearest *= -1;
}
return [10 ** (3 * $nearest), $symbol];
}
public function convertValue(float $value): array
{
//Choose the prefix to use
$tmp = $this->getPrefixByMagnitude($this->getMagnitude($value));
return [
'value' => $value / $tmp[0],
'prefix_magnitude' => log10($tmp[0]),
'prefix' => $tmp[1],
];
}
/**
* Formats the given value to a string, using the given options.
*
* @param float $value The value that should be converted
* @param string $unit The unit that should be appended after the prefix
* @param int $decimals the number of decimals (after decimal dot) that should be outputed
*
* @return string The formatted value
*/
public function format(float $value, string $unit = '', int $decimals = 2): string
{
[$divisor, $symbol] = $this->getPrefixByMagnitude($this->getMagnitude($value));
$value /= $divisor;
//Build the format string, e.g.: %.2d km
if ('' !== $unit || '' !== $symbol) {
$format_string = '%.'.$decimals.'f '.$symbol.$unit;
} else {
$format_string = '%.'.$decimals.'f';
}
return sprintf($format_string, $value);
}
}