diff --git a/src/Services/LogSystem/LogDiffFormatter.php b/src/Services/LogSystem/LogDiffFormatter.php index 8b165d5a..1ac5a2f5 100644 --- a/src/Services/LogSystem/LogDiffFormatter.php +++ b/src/Services/LogSystem/LogDiffFormatter.php @@ -32,7 +32,7 @@ class LogDiffFormatter * @param $old_data * @param $new_data */ - public function formatDiff($old_data, $new_data): string + public function formatDiff(mixed $old_data, mixed $new_data): string { if (is_string($old_data) && is_string($new_data)) { return $this->diffString($old_data, $new_data); diff --git a/src/Twig/AttachmentExtension.php b/src/Twig/AttachmentExtension.php index 9f81abe6..3d5ec611 100644 --- a/src/Twig/AttachmentExtension.php +++ b/src/Twig/AttachmentExtension.php @@ -25,22 +25,34 @@ namespace App\Twig; use App\Entity\Attachments\Attachment; use App\Services\Attachments\AttachmentURLGenerator; use App\Services\Misc\FAIconGenerator; +use Twig\Attribute\AsTwigFunction; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; -final class AttachmentExtension extends AbstractExtension +final readonly class AttachmentExtension { - public function __construct(protected AttachmentURLGenerator $attachmentURLGenerator, protected FAIconGenerator $FAIconGenerator) + public function __construct(private AttachmentURLGenerator $attachmentURLGenerator, private FAIconGenerator $FAIconGenerator) { } - public function getFunctions(): array + /** + * Returns the URL of the thumbnail of the given attachment. Returns null if no thumbnail is available. + */ + #[AsTwigFunction("attachment_thumbnail")] + public function attachmentThumbnail(Attachment $attachment, string $filter_name = 'thumbnail_sm'): ?string { - return [ - /* Returns the URL to a thumbnail of the given attachment */ - new TwigFunction('attachment_thumbnail', fn(Attachment $attachment, string $filter_name = 'thumbnail_sm'): ?string => $this->attachmentURLGenerator->getThumbnailURL($attachment, $filter_name)), - /* Returns the font awesome icon class which is representing the given file extension (We allow null here for attachments without extension) */ - new TwigFunction('ext_to_fa_icon', fn(?string $extension): string => $this->FAIconGenerator->fileExtensionToFAType($extension ?? '')), - ]; + return $this->attachmentURLGenerator->getThumbnailURL($attachment, $filter_name); + } + + /** + * Return the font-awesome icon type for the given file extension. Returns "file" if no specific icon is available. + * Null is allowed for files withot extension + * @param string|null $extension + * @return string + */ + #[AsTwigFunction("ext_to_fa_icon")] + public function extToFAIcon(?string $extension): string + { + return $this->FAIconGenerator->fileExtensionToFAType($extension ?? ''); } } diff --git a/src/Twig/BarcodeExtension.php b/src/Twig/BarcodeExtension.php index ae1973e3..25c0d78e 100644 --- a/src/Twig/BarcodeExtension.php +++ b/src/Twig/BarcodeExtension.php @@ -23,19 +23,14 @@ declare(strict_types=1); namespace App\Twig; use Com\Tecnick\Barcode\Barcode; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; +use Twig\Attribute\AsTwigFunction; -final class BarcodeExtension extends AbstractExtension +final class BarcodeExtension { - public function getFunctions(): array - { - return [ - /* Generates a barcode with the given Type and Data and returns it as an SVG represenation */ - new TwigFunction('barcode_svg', fn(string $content, string $type = 'QRCODE'): string => $this->barcodeSVG($content, $type)), - ]; - } - + /** + * Generates a barcode in SVG format for the given content and type. + */ + #[AsTwigFunction('barcode_svg')] public function barcodeSVG(string $content, string $type = 'QRCODE'): string { $barcodeFactory = new Barcode(); diff --git a/src/Twig/EntityExtension.php b/src/Twig/EntityExtension.php index 427a39b5..bff21eb8 100644 --- a/src/Twig/EntityExtension.php +++ b/src/Twig/EntityExtension.php @@ -41,6 +41,9 @@ use App\Exceptions\EntityNotSupportedException; use App\Services\ElementTypeNameGenerator; use App\Services\EntityURLGenerator; use App\Services\Trees\TreeViewGenerator; +use Twig\Attribute\AsTwigFunction; +use Twig\Attribute\AsTwigTest; +use Twig\DeprecatedCallableInfo; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; use Twig\TwigTest; @@ -48,61 +51,27 @@ use Twig\TwigTest; /** * @see \App\Tests\Twig\EntityExtensionTest */ -final class EntityExtension extends AbstractExtension +final readonly class EntityExtension { - public function __construct(protected EntityURLGenerator $entityURLGenerator, protected TreeViewGenerator $treeBuilder, private readonly ElementTypeNameGenerator $nameGenerator) + public function __construct(private EntityURLGenerator $entityURLGenerator, private TreeViewGenerator $treeBuilder, private ElementTypeNameGenerator $nameGenerator) { } - public function getTests(): array + /** + * Checks if the given variable is an entity (instance of AbstractDBElement). + */ + #[AsTwigTest("entity")] + public function entityTest(mixed $var): bool { - return [ - /* Checks if the given variable is an entitity (instance of AbstractDBElement) */ - new TwigTest('entity', static fn($var) => $var instanceof AbstractDBElement), - ]; + return $var instanceof AbstractDBElement; } - public function getFunctions(): array - { - return [ - /* Returns a string representation of the given entity */ - new TwigFunction('entity_type', fn(object $entity): ?string => $this->getEntityType($entity)), - /* Returns the URL to the given entity */ - new TwigFunction('entity_url', fn(AbstractDBElement $entity, string $method = 'info'): string => $this->generateEntityURL($entity, $method)), - /* Returns the URL to the given entity in timetravel mode */ - new TwigFunction('timetravel_url', fn(AbstractDBElement $element, \DateTimeInterface $dateTime): ?string => $this->timeTravelURL($element, $dateTime)), - /* Generates a JSON array of the given tree */ - new TwigFunction('tree_data', fn(AbstractDBElement $element, string $type = 'newEdit'): string => $this->treeData($element, $type)), - /* Gets a human readable label for the type of the given entity */ - new TwigFunction('entity_type_label', fn(object|string $entity): string => $this->nameGenerator->getLocalizedTypeLabel($entity)), - new TwigFunction('type_label', fn(object|string $entity): string => $this->nameGenerator->typeLabel($entity)), - new TwigFunction('type_label_p', fn(object|string $entity): string => $this->nameGenerator->typeLabelPlural($entity)), - ]; - } - - public function timeTravelURL(AbstractDBElement $element, \DateTimeInterface $dateTime): ?string - { - try { - return $this->entityURLGenerator->timeTravelURL($element, $dateTime); - } catch (EntityNotSupportedException) { - return null; - } - } - - public function treeData(AbstractDBElement $element, string $type = 'newEdit'): string - { - $tree = $this->treeBuilder->getTreeView($element::class, null, $type, $element); - - return json_encode($tree, JSON_THROW_ON_ERROR); - } - - public function generateEntityURL(AbstractDBElement $entity, string $method = 'info'): string - { - return $this->entityURLGenerator->getURL($entity, $method); - } - - public function getEntityType(object $entity): ?string + /** + * Returns a string representation of the given entity + */ + #[AsTwigFunction("entity_type")] + public function entityType(object $entity): ?string { $map = [ Part::class => 'part', @@ -129,4 +98,69 @@ final class EntityExtension extends AbstractExtension return null; } + + /** + * Returns the URL for the given entity and method. E.g. for a Part and method "edit", it will return the URL to edit this part. + */ + #[AsTwigFunction("entity_url")] + public function entityURL(AbstractDBElement $entity, string $method = 'info'): string + { + return $this->entityURLGenerator->getURL($entity, $method); + } + + + /** + * Returns the URL for the given entity in timetravel mode. + */ + #[AsTwigFunction("timetravel_url")] + public function timeTravelURL(AbstractDBElement $element, \DateTimeInterface $dateTime): ?string + { + try { + return $this->entityURLGenerator->timeTravelURL($element, $dateTime); + } catch (EntityNotSupportedException) { + return null; + } + } + + /** + * Generates a tree data structure for the given element, which can be used to display a tree view of the element and its related entities. + * The type parameter can be used to specify the type of tree view (e.g. "newEdit" for the tree view in the new/edit pages). The returned data is a JSON string. + */ + #[AsTwigFunction("tree_data")] + public function treeData(AbstractDBElement $element, string $type = 'newEdit'): string + { + $tree = $this->treeBuilder->getTreeView($element::class, null, $type, $element); + + return json_encode($tree, JSON_THROW_ON_ERROR); + } + + /** + * Gets the localized type label for the given entity. E.g. for a Part, it will return "Part" in English and "Bauteil" in German. + * @deprecated Use the "type_label" function instead, which does the same but is more concise. + */ + #[AsTwigFunction("entity_type_label", deprecationInfo: new DeprecatedCallableInfo("Part-DB", "2", "Use the 'type_label' function instead."))] + public function entityTypeLabel(object|string $entity): string + { + return $this->nameGenerator->getLocalizedTypeLabel($entity); + } + + /** + * Gets the localized type label for the given entity. E.g. for a Part, it will return "Part" in English and "Bauteil" in German. + */ + #[AsTwigFunction("type_label")] + public function typeLabel(object|string $entity): string + { + return $this->nameGenerator->typeLabel($entity); + } + + /** + * Gets the localized plural type label for the given entity. E.g. for a Part, it will return "Parts" in English and "Bauteile" in German. + * @param object|string $entity + * @return string + */ + #[AsTwigFunction("type_label_p")] + public function typeLabelPlural(object|string $entity): string + { + return $this->nameGenerator->typeLabelPlural($entity); + } } diff --git a/src/Twig/FormatExtension.php b/src/Twig/FormatExtension.php index 46313aaf..b91b7e11 100644 --- a/src/Twig/FormatExtension.php +++ b/src/Twig/FormatExtension.php @@ -29,35 +29,28 @@ use App\Services\Formatters\MarkdownParser; use App\Services\Formatters\MoneyFormatter; use App\Services\Formatters\SIFormatter; use Brick\Math\BigDecimal; -use Twig\Extension\AbstractExtension; -use Twig\TwigFilter; +use Twig\Attribute\AsTwigFilter; -final class FormatExtension extends AbstractExtension +final readonly class FormatExtension { - public function __construct(protected MarkdownParser $markdownParser, protected MoneyFormatter $moneyFormatter, protected SIFormatter $siformatter, protected AmountFormatter $amountFormatter) + public function __construct(private MarkdownParser $markdownParser, private MoneyFormatter $moneyFormatter, private SIFormatter $siformatter, private AmountFormatter $amountFormatter) { } - public function getFilters(): array + /** + * Mark the given text as markdown, which will be rendered in the browser + */ + #[AsTwigFilter("format_markdown", isSafe: ['html'], preEscape: 'html')] + public function formatMarkdown(string $markdown, bool $inline_mode = false): string { - return [ - /* Mark the given text as markdown, which will be rendered in the browser */ - new TwigFilter('format_markdown', fn(string $markdown, bool $inline_mode = false): string => $this->markdownParser->markForRendering($markdown, $inline_mode), [ - 'pre_escape' => 'html', - 'is_safe' => ['html'], - ]), - /* Format the given amount as money, using a given currency */ - new TwigFilter('format_money', fn($amount, ?Currency $currency = null, int $decimals = 5): string => $this->formatCurrency($amount, $currency, $decimals)), - /* Format the given number using SI prefixes and the given unit (string) */ - new TwigFilter('format_si', fn($value, $unit, $decimals = 2, bool $show_all_digits = false): string => $this->siFormat($value, $unit, $decimals, $show_all_digits)), - /** Format the given amount using the given MeasurementUnit */ - new TwigFilter('format_amount', fn($value, ?MeasurementUnit $unit, array $options = []): string => $this->amountFormat($value, $unit, $options)), - /** Format the given number of bytes as human-readable number */ - new TwigFilter('format_bytes', fn(int $bytes, int $precision = 2): string => $this->formatBytes($bytes, $precision)), - ]; + return $this->markdownParser->markForRendering($markdown, $inline_mode); } - public function formatCurrency($amount, ?Currency $currency = null, int $decimals = 5): string + /** + * Format the given amount as money, using a given currency + */ + #[AsTwigFilter("format_money")] + public function formatMoney(BigDecimal|float|string $amount, ?Currency $currency = null, int $decimals = 5): string { if ($amount instanceof BigDecimal) { $amount = (string) $amount; @@ -66,19 +59,22 @@ final class FormatExtension extends AbstractExtension return $this->moneyFormatter->format($amount, $currency, $decimals); } - public function siFormat($value, $unit, $decimals = 2, bool $show_all_digits = false): string + /** + * Format the given number using SI prefixes and the given unit (string) + */ + #[AsTwigFilter("format_si")] + public function siFormat(float $value, string $unit, int $decimals = 2, bool $show_all_digits = false): string { return $this->siformatter->format($value, $unit, $decimals); } - public function amountFormat($value, ?MeasurementUnit $unit, array $options = []): string + #[AsTwigFilter("format_amount")] + public function amountFormat(float|int|string $value, ?MeasurementUnit $unit, array $options = []): string { return $this->amountFormatter->format($value, $unit, $options); } - /** - * @param $bytes - */ + #[AsTwigFilter("format_bytes")] public function formatBytes(int $bytes, int $precision = 2): string { $size = ['B','kB','MB','GB','TB','PB','EB','ZB','YB']; diff --git a/src/Twig/InfoProviderExtension.php b/src/Twig/InfoProviderExtension.php index dc64a80a..54dbf93a 100644 --- a/src/Twig/InfoProviderExtension.php +++ b/src/Twig/InfoProviderExtension.php @@ -29,10 +29,10 @@ use App\Services\InfoProviderSystem\Providers\InfoProviderInterface; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; -class InfoProviderExtension +final readonly class InfoProviderExtension { public function __construct( - private readonly ProviderRegistry $providerRegistry + private ProviderRegistry $providerRegistry ) {} /** @@ -64,4 +64,4 @@ class InfoProviderExtension return null; } } -} \ No newline at end of file +} diff --git a/src/Twig/LogExtension.php b/src/Twig/LogExtension.php index 34dad988..738a24c2 100644 --- a/src/Twig/LogExtension.php +++ b/src/Twig/LogExtension.php @@ -25,21 +25,26 @@ namespace App\Twig; use App\Entity\LogSystem\AbstractLogEntry; use App\Services\LogSystem\LogDataFormatter; use App\Services\LogSystem\LogDiffFormatter; +use Twig\Attribute\AsTwigFunction; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; -final class LogExtension extends AbstractExtension +final readonly class LogExtension { - public function __construct(private readonly LogDataFormatter $logDataFormatter, private readonly LogDiffFormatter $logDiffFormatter) + public function __construct(private LogDataFormatter $logDataFormatter, private LogDiffFormatter $logDiffFormatter) { } - public function getFunctions(): array + #[AsTwigFunction(name: 'format_log_data', isSafe: ['html'])] + public function formatLogData(mixed $data, AbstractLogEntry $logEntry, string $fieldName): string { - return [ - new TwigFunction('format_log_data', fn($data, AbstractLogEntry $logEntry, string $fieldName): string => $this->logDataFormatter->formatData($data, $logEntry, $fieldName), ['is_safe' => ['html']]), - new TwigFunction('format_log_diff', fn($old_data, $new_data): string => $this->logDiffFormatter->formatDiff($old_data, $new_data), ['is_safe' => ['html']]), - ]; + return $this->logDataFormatter->formatData($data, $logEntry, $fieldName); + } + + #[AsTwigFunction(name: 'format_log_diff', isSafe: ['html'])] + public function formatLogDiff(mixed $old_data, mixed $new_data): string + { + return $this->logDiffFormatter->formatDiff($old_data, $new_data); } } diff --git a/src/Twig/MiscExtension.php b/src/Twig/MiscExtension.php index 9691abc0..390ad084 100644 --- a/src/Twig/MiscExtension.php +++ b/src/Twig/MiscExtension.php @@ -32,9 +32,9 @@ use Twig\TwigFunction; use App\Services\LogSystem\EventCommentNeededHelper; use Twig\Extension\AbstractExtension; -final class MiscExtension +final readonly class MiscExtension { - public function __construct(private readonly EventCommentNeededHelper $eventCommentNeededHelper) + public function __construct(private EventCommentNeededHelper $eventCommentNeededHelper) { } diff --git a/src/Twig/TwigCoreExtension.php b/src/Twig/TwigCoreExtension.php index 58f991b4..ceb4ce82 100644 --- a/src/Twig/TwigCoreExtension.php +++ b/src/Twig/TwigCoreExtension.php @@ -23,8 +23,10 @@ declare(strict_types=1); namespace App\Twig; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Twig\Attribute\AsTwigFilter; use Twig\Attribute\AsTwigFunction; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; +use Twig\Attribute\AsTwigTest; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; use Twig\TwigFunction; @@ -34,51 +36,54 @@ use Twig\TwigTest; * The functionalities here extend the Twig with some core functions, which are independently of Part-DB. * @see \App\Tests\Twig\TwigCoreExtensionTest */ -final class TwigCoreExtension +final readonly class TwigCoreExtension { - private readonly NormalizerInterface $objectNormalizer; + private NormalizerInterface $objectNormalizer; public function __construct() { $this->objectNormalizer = new ObjectNormalizer(); } - public function getTests(): array + /** + * Checks if the given variable is an instance of the given class/interface/enum. E.g. `x is instanceof('App\Entity\Parts\Part')` + * @param mixed $var + * @param string $instance + * @return bool + */ + #[AsTwigTest("instanceof")] + public function testInstanceOf(mixed $var, string $instance): bool { - return [ - /* - * Checks if a given variable is an instance of a given class. E.g. ` x is instanceof('App\Entity\Parts\Part')` - */ - new TwigTest('instanceof', static fn($var, $instance) => $var instanceof $instance), - /* Checks if a given variable is an object. E.g. `x is object` */ - new TwigTest('object', static fn($var): bool => is_object($var)), - new TwigTest('enum', fn($var) => $var instanceof \UnitEnum), - ]; + if (!class_exists($instance) && !interface_exists($instance) && !enum_exists($instance)) { + throw new \InvalidArgumentException(sprintf('The given class/interface/enum "%s" does not exist!', $instance)); + } + + return $var instanceof $instance; } /** - * @param string $enum_class - * @phpstan-param class-string $enum_class + * Checks if the given variable is an object. This can be used to check if a variable is an object, without knowing the exact class of the object. E.g. `x is object` + * @param mixed $var + * @return bool */ - #[AsTwigFunction(name: 'enum_cases')] - public function getEnumCases(string $enum_class): array + #[AsTwigTest("object")] + public function testObject(mixed $var): bool { - if (!enum_exists($enum_class)) { - throw new \InvalidArgumentException(sprintf('The given class "%s" is not an enum!', $enum_class)); - } - - /** @noinspection PhpUndefinedMethodInspection */ - return ($enum_class)::cases(); + return is_object($var); } - public function getFilters(): array + /** + * Checks if the given variable is an enum (instance of UnitEnum). This can be used to check if a variable is an enum, without knowing the exact class of the enum. E.g. `x is enum` + * @param mixed $var + * @return bool + */ + #[AsTwigTest("enum")] + public function testEnum(mixed $var): bool { - return [ - /* Converts the given object to an array representation of the public/accessible properties */ - new TwigFilter('to_array', fn($object) => $this->toArray($object)), - ]; + return $var instanceof \UnitEnum; } + #[AsTwigFilter('to_array')] public function toArray(object|array $object): array { //If it is already an array, we can just return it diff --git a/src/Twig/UpdateExtension.php b/src/Twig/UpdateExtension.php index 552335ae..7ec7897b 100644 --- a/src/Twig/UpdateExtension.php +++ b/src/Twig/UpdateExtension.php @@ -32,10 +32,10 @@ use Twig\TwigFunction; /** * Twig extension for update-related functions. */ -final class UpdateExtension +final readonly class UpdateExtension { - public function __construct(private readonly UpdateAvailableFacade $updateAvailableManager, - private readonly Security $security) + public function __construct(private UpdateAvailableFacade $updateAvailableManager, + private Security $security) { } diff --git a/src/Twig/UserExtension.php b/src/Twig/UserExtension.php index 687f4e84..3cf76e63 100644 --- a/src/Twig/UserExtension.php +++ b/src/Twig/UserExtension.php @@ -41,6 +41,7 @@ declare(strict_types=1); namespace App\Twig; +use Twig\Attribute\AsTwigFilter; use Twig\Attribute\AsTwigFunction; use App\Entity\Base\AbstractDBElement; use App\Entity\UserSystem\User; @@ -51,39 +52,34 @@ use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; use Symfony\Component\Security\Core\Exception\AccessDeniedException; -use Twig\Extension\AbstractExtension; -use Twig\TwigFilter; -use Twig\TwigFunction; /** * @see \App\Tests\Twig\UserExtensionTest */ -final class UserExtension +final readonly class UserExtension { - private readonly LogEntryRepository $repo; + private LogEntryRepository $repo; public function __construct(EntityManagerInterface $em, - private readonly Security $security, - private readonly UrlGeneratorInterface $urlGenerator) + private Security $security, + private UrlGeneratorInterface $urlGenerator) { $this->repo = $em->getRepository(AbstractLogEntry::class); } - public function getFilters(): array + /** + * Returns the user which has edited the given entity the last time. + */ + #[AsTwigFunction('last_editing_user')] + public function lastEditingUser(AbstractDBElement $element): ?User { - return [ - new TwigFilter('remove_locale_from_path', fn(string $path): string => $this->removeLocaleFromPath($path)), - ]; + return $this->repo->getLastEditingUser($element); } - public function getFunctions(): array + #[AsTwigFunction('creating_user')] + public function creatingUser(AbstractDBElement $element): ?User { - return [ - /* Returns the user which has edited the given entity the last time. */ - new TwigFunction('last_editing_user', fn(AbstractDBElement $element): ?User => $this->repo->getLastEditingUser($element)), - /* Returns the user which has created the given entity. */ - new TwigFunction('creating_user', fn(AbstractDBElement $element): ?User => $this->repo->getCreatingUser($element)), - ]; + return $this->repo->getCreatingUser($element); } /** @@ -125,6 +121,7 @@ final class UserExtension /** * This function/filter generates a path. */ + #[AsTwigFilter(name: 'remove_locale_from_path')] public function removeLocaleFromPath(string $path): string { //Ensure the path has the correct format diff --git a/tests/Twig/EntityExtensionTest.php b/tests/Twig/EntityExtensionTest.php index f3d5bef9..d206b941 100644 --- a/tests/Twig/EntityExtensionTest.php +++ b/tests/Twig/EntityExtensionTest.php @@ -55,20 +55,20 @@ final class EntityExtensionTest extends WebTestCase public function testGetEntityType(): void { - $this->assertSame('part', $this->service->getEntityType(new Part())); - $this->assertSame('footprint', $this->service->getEntityType(new Footprint())); - $this->assertSame('storelocation', $this->service->getEntityType(new StorageLocation())); - $this->assertSame('manufacturer', $this->service->getEntityType(new Manufacturer())); - $this->assertSame('category', $this->service->getEntityType(new Category())); - $this->assertSame('device', $this->service->getEntityType(new Project())); - $this->assertSame('attachment', $this->service->getEntityType(new PartAttachment())); - $this->assertSame('supplier', $this->service->getEntityType(new Supplier())); - $this->assertSame('user', $this->service->getEntityType(new User())); - $this->assertSame('group', $this->service->getEntityType(new Group())); - $this->assertSame('currency', $this->service->getEntityType(new Currency())); - $this->assertSame('measurement_unit', $this->service->getEntityType(new MeasurementUnit())); - $this->assertSame('label_profile', $this->service->getEntityType(new LabelProfile())); - $this->assertSame('part_custom_state', $this->service->getEntityType(new PartCustomState())); + $this->assertSame('part', $this->service->entityType(new Part())); + $this->assertSame('footprint', $this->service->entityType(new Footprint())); + $this->assertSame('storelocation', $this->service->entityType(new StorageLocation())); + $this->assertSame('manufacturer', $this->service->entityType(new Manufacturer())); + $this->assertSame('category', $this->service->entityType(new Category())); + $this->assertSame('device', $this->service->entityType(new Project())); + $this->assertSame('attachment', $this->service->entityType(new PartAttachment())); + $this->assertSame('supplier', $this->service->entityType(new Supplier())); + $this->assertSame('user', $this->service->entityType(new User())); + $this->assertSame('group', $this->service->entityType(new Group())); + $this->assertSame('currency', $this->service->entityType(new Currency())); + $this->assertSame('measurement_unit', $this->service->entityType(new MeasurementUnit())); + $this->assertSame('label_profile', $this->service->entityType(new LabelProfile())); + $this->assertSame('part_custom_state', $this->service->entityType(new PartCustomState())); } }