Applied rector with PHP8.1 migration rules

This commit is contained in:
Jan Böhmer 2023-06-11 14:15:46 +02:00
parent dc6a67c2f0
commit 7ee01d9a05
303 changed files with 1228 additions and 3465 deletions

View file

@ -27,8 +27,8 @@ return static function (RectorConfig $rectorConfig): void {
// define sets of rules // define sets of rules
$rectorConfig->sets([ $rectorConfig->sets([
//PHP rules //PHP rules
//SetList::CODE_QUALITY, SetList::CODE_QUALITY,
//LevelSetList::UP_TO_PHP_81, LevelSetList::UP_TO_PHP_81,
//Symfony rules //Symfony rules
SymfonyLevelSetList::UP_TO_SYMFONY_62, SymfonyLevelSetList::UP_TO_SYMFONY_62,

View file

@ -43,16 +43,10 @@ use const DIRECTORY_SEPARATOR;
#[\Symfony\Component\Console\Attribute\AsCommand('partdb:attachments:clean-unused|app:clean-attachments', 'Lists (and deletes if wanted) attachments files that are not used anymore (abandoned files).')] #[\Symfony\Component\Console\Attribute\AsCommand('partdb:attachments:clean-unused|app:clean-attachments', 'Lists (and deletes if wanted) attachments files that are not used anymore (abandoned files).')]
class CleanAttachmentsCommand extends Command class CleanAttachmentsCommand extends Command
{ {
protected AttachmentManager $attachment_helper;
protected AttachmentReverseSearch $reverseSearch;
protected MimeTypes $mimeTypeGuesser; protected MimeTypes $mimeTypeGuesser;
protected AttachmentPathResolver $pathResolver;
public function __construct(AttachmentManager $attachmentHelper, AttachmentReverseSearch $reverseSearch, AttachmentPathResolver $pathResolver) public function __construct(protected AttachmentManager $attachment_helper, protected AttachmentReverseSearch $reverseSearch, protected AttachmentPathResolver $pathResolver)
{ {
$this->attachment_helper = $attachmentHelper;
$this->pathResolver = $pathResolver;
$this->reverseSearch = $reverseSearch;
$this->mimeTypeGuesser = new MimeTypes(); $this->mimeTypeGuesser = new MimeTypes();
parent::__construct(); parent::__construct();
} }
@ -88,7 +82,7 @@ class CleanAttachmentsCommand extends Command
foreach ($finder as $file) { foreach ($finder as $file) {
//If not attachment object uses this file, print it //If not attachment object uses this file, print it
if (0 === count($this->reverseSearch->findAttachmentsByFile($file))) { if ([] === $this->reverseSearch->findAttachmentsByFile($file)) {
$file_list[] = $file; $file_list[] = $file;
$table->addRow([ $table->addRow([
$fs->makePathRelative($file->getPathname(), $mediaPath), $fs->makePathRelative($file->getPathname(), $mediaPath),
@ -98,7 +92,7 @@ class CleanAttachmentsCommand extends Command
} }
} }
if (count($file_list) > 0) { if ($file_list !== []) {
$table->render(); $table->render();
$continue = $io->confirm(sprintf('Found %d abandoned files. Do you want to delete them? This can not be undone!', count($file_list)), false); $continue = $io->confirm(sprintf('Found %d abandoned files. Do you want to delete them? This can not be undone!', count($file_list)), false);

View file

@ -19,14 +19,8 @@ use Symfony\Component\Console\Style\SymfonyStyle;
#[\Symfony\Component\Console\Attribute\AsCommand('partdb:backup', 'Backup the files and the database of Part-DB')] #[\Symfony\Component\Console\Attribute\AsCommand('partdb:backup', 'Backup the files and the database of Part-DB')]
class BackupCommand extends Command class BackupCommand extends Command
{ {
private string $project_dir; public function __construct(private readonly string $project_dir, private readonly EntityManagerInterface $entityManager)
private EntityManagerInterface $entityManager;
public function __construct(string $project_dir, EntityManagerInterface $entityManager)
{ {
$this->project_dir = $project_dir;
$this->entityManager = $entityManager;
parent::__construct(); parent::__construct();
} }
@ -69,14 +63,11 @@ class BackupCommand extends Command
$io->info('Backup Part-DB to '.$output_filepath); $io->info('Backup Part-DB to '.$output_filepath);
//Check if the file already exists //Check if the file already exists
if (file_exists($output_filepath)) {
//Then ask the user, if he wants to overwrite the file //Then ask the user, if he wants to overwrite the file
if (!$io->confirm('The file '.realpath($output_filepath).' already exists. Do you want to overwrite it?', false)) { if (file_exists($output_filepath) && !$io->confirm('The file '.realpath($output_filepath).' already exists. Do you want to overwrite it?', false)) {
$io->error('Backup aborted!'); $io->error('Backup aborted!');
return Command::FAILURE; return Command::FAILURE;
} }
}
$io->note('Starting backup...'); $io->note('Starting backup...');
@ -113,8 +104,6 @@ class BackupCommand extends Command
/** /**
* Constructs the MySQL PDO DSN. * Constructs the MySQL PDO DSN.
* Taken from https://github.com/doctrine/dbal/blob/3.5.x/src/Driver/PDO/MySQL/Driver.php * Taken from https://github.com/doctrine/dbal/blob/3.5.x/src/Driver/PDO/MySQL/Driver.php
*
* @param array $params
*/ */
private function configureDumper(array $params, DbDumper $dumper): void private function configureDumper(array $params, DbDumper $dumper): void
{ {

View file

@ -30,11 +30,8 @@ use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
#[\Symfony\Component\Console\Attribute\AsCommand('partdb:check-requirements', 'Checks if the requirements Part-DB needs or recommends are fulfilled.')] #[\Symfony\Component\Console\Attribute\AsCommand('partdb:check-requirements', 'Checks if the requirements Part-DB needs or recommends are fulfilled.')]
class CheckRequirementsCommand extends Command class CheckRequirementsCommand extends Command
{ {
protected ContainerBagInterface $params; public function __construct(protected ContainerBagInterface $params)
public function __construct(ContainerBagInterface $params)
{ {
$this->params = $params;
parent::__construct(); parent::__construct();
} }
@ -66,40 +63,48 @@ class CheckRequirementsCommand extends Command
protected function checkPHP(SymfonyStyle $io, $only_issues = false): void protected function checkPHP(SymfonyStyle $io, $only_issues = false): void
{ {
//Check PHP versions //Check PHP versions
$io->isVerbose() && $io->comment('Checking PHP version...'); if ($io->isVerbose()) {
$io->comment('Checking PHP version...');
}
//We recommend PHP 8.2, but 8.1 is the minimum //We recommend PHP 8.2, but 8.1 is the minimum
if (PHP_VERSION_ID < 80200) { if (PHP_VERSION_ID < 80200) {
$io->warning('You are using PHP '. PHP_VERSION .'. This will work, but a newer version is recommended.'); $io->warning('You are using PHP '. PHP_VERSION .'. This will work, but a newer version is recommended.');
} else { } elseif (!$only_issues) {
!$only_issues && $io->success('PHP version is sufficient.'); $io->success('PHP version is sufficient.');
} }
//Check if opcache is enabled //Check if opcache is enabled
$io->isVerbose() && $io->comment('Checking Opcache...'); if ($io->isVerbose()) {
$io->comment('Checking Opcache...');
}
$opcache_enabled = ini_get('opcache.enable') === '1'; $opcache_enabled = ini_get('opcache.enable') === '1';
if (!$opcache_enabled) { if (!$opcache_enabled) {
$io->warning('Opcache is not enabled. This will work, but performance will be better with opcache enabled. Set opcache.enable=1 in your php.ini to enable it'); $io->warning('Opcache is not enabled. This will work, but performance will be better with opcache enabled. Set opcache.enable=1 in your php.ini to enable it');
} else { } elseif (!$only_issues) {
!$only_issues && $io->success('Opcache is enabled.'); $io->success('Opcache is enabled.');
} }
//Check if opcache is configured correctly //Check if opcache is configured correctly
$io->isVerbose() && $io->comment('Checking Opcache configuration...'); if ($io->isVerbose()) {
$io->comment('Checking Opcache configuration...');
}
if ($opcache_enabled && (ini_get('opcache.memory_consumption') < 256 || ini_get('opcache.max_accelerated_files') < 20000)) { if ($opcache_enabled && (ini_get('opcache.memory_consumption') < 256 || ini_get('opcache.max_accelerated_files') < 20000)) {
$io->warning('Opcache configuration can be improved. See https://symfony.com/doc/current/performance.html for more info.'); $io->warning('Opcache configuration can be improved. See https://symfony.com/doc/current/performance.html for more info.');
} else { } elseif (!$only_issues) {
!$only_issues && $io->success('Opcache configuration is already performance optimized.'); $io->success('Opcache configuration is already performance optimized.');
} }
} }
protected function checkPartDBConfig(SymfonyStyle $io, $only_issues = false): void protected function checkPartDBConfig(SymfonyStyle $io, $only_issues = false): void
{ {
//Check if APP_ENV is set to prod //Check if APP_ENV is set to prod
$io->isVerbose() && $io->comment('Checking debug mode...'); if ($io->isVerbose()) {
$io->comment('Checking debug mode...');
}
if ($this->params->get('kernel.debug')) { if ($this->params->get('kernel.debug')) {
$io->warning('You have activated debug mode, this is will leak informations in a production environment.'); $io->warning('You have activated debug mode, this is will leak informations in a production environment.');
} else { } elseif (!$only_issues) {
!$only_issues && $io->success('Debug mode disabled.'); $io->success('Debug mode disabled.');
} }
} }
@ -108,61 +113,71 @@ class CheckRequirementsCommand extends Command
{ {
//Get all installed PHP extensions //Get all installed PHP extensions
$extensions = get_loaded_extensions(); $extensions = get_loaded_extensions();
$io->isVerbose() && $io->comment('Your PHP installation has '. count($extensions) .' extensions installed: '. implode(', ', $extensions)); if ($io->isVerbose()) {
$io->comment('Your PHP installation has '. count($extensions) .' extensions installed: '. implode(', ', $extensions));
}
$db_drivers_count = 0; $db_drivers_count = 0;
if(!in_array('pdo_mysql', $extensions)) { if(!in_array('pdo_mysql', $extensions)) {
$io->error('pdo_mysql is not installed. You will not be able to use MySQL databases.'); $io->error('pdo_mysql is not installed. You will not be able to use MySQL databases.');
} else { } else {
!$only_issues && $io->success('PHP extension pdo_mysql is installed.'); if (!$only_issues) {
$io->success('PHP extension pdo_mysql is installed.');
}
$db_drivers_count++; $db_drivers_count++;
} }
if(!in_array('pdo_sqlite', $extensions)) { if(!in_array('pdo_sqlite', $extensions)) {
$io->error('pdo_sqlite is not installed. You will not be able to use SQLite. databases'); $io->error('pdo_sqlite is not installed. You will not be able to use SQLite. databases');
} else { } else {
!$only_issues && $io->success('PHP extension pdo_sqlite is installed.'); if (!$only_issues) {
$io->success('PHP extension pdo_sqlite is installed.');
}
$db_drivers_count++; $db_drivers_count++;
} }
$io->isVerbose() && $io->comment('You have '. $db_drivers_count .' database drivers installed.'); if ($io->isVerbose()) {
$io->comment('You have '. $db_drivers_count .' database drivers installed.');
}
if ($db_drivers_count === 0) { if ($db_drivers_count === 0) {
$io->error('You have no database drivers installed. You have to install at least one database driver!'); $io->error('You have no database drivers installed. You have to install at least one database driver!');
} }
if (!in_array('curl', $extensions)) { if (!in_array('curl', $extensions)) {
$io->warning('curl extension is not installed. Install curl extension for better performance'); $io->warning('curl extension is not installed. Install curl extension for better performance');
} else { } elseif (!$only_issues) {
!$only_issues && $io->success('PHP extension curl is installed.'); $io->success('PHP extension curl is installed.');
} }
$gd_installed = in_array('gd', $extensions); $gd_installed = in_array('gd', $extensions);
if (!$gd_installed) { if (!$gd_installed) {
$io->error('GD is not installed. GD is required for image processing.'); $io->error('GD is not installed. GD is required for image processing.');
} else { } elseif (!$only_issues) {
!$only_issues && $io->success('PHP extension GD is installed.'); $io->success('PHP extension GD is installed.');
} }
//Check if GD has jpeg support //Check if GD has jpeg support
$io->isVerbose() && $io->comment('Checking if GD has jpeg support...'); if ($io->isVerbose()) {
$io->comment('Checking if GD has jpeg support...');
}
if ($gd_installed) { if ($gd_installed) {
$gd_info = gd_info(); $gd_info = gd_info();
if ($gd_info['JPEG Support'] === false) { if ($gd_info['JPEG Support'] === false) {
$io->warning('Your GD does not have jpeg support. You will not be able to generate thumbnails of jpeg images.'); $io->warning('Your GD does not have jpeg support. You will not be able to generate thumbnails of jpeg images.');
} else { } elseif (!$only_issues) {
!$only_issues && $io->success('GD has jpeg support.'); $io->success('GD has jpeg support.');
} }
if ($gd_info['PNG Support'] === false) { if ($gd_info['PNG Support'] === false) {
$io->warning('Your GD does not have png support. You will not be able to generate thumbnails of png images.'); $io->warning('Your GD does not have png support. You will not be able to generate thumbnails of png images.');
} else { } elseif (!$only_issues) {
!$only_issues && $io->success('GD has png support.'); $io->success('GD has png support.');
} }
if ($gd_info['WebP Support'] === false) { if ($gd_info['WebP Support'] === false) {
$io->warning('Your GD does not have WebP support. You will not be able to generate thumbnails of WebP images.'); $io->warning('Your GD does not have WebP support. You will not be able to generate thumbnails of WebP images.');
} else { } elseif (!$only_issues) {
!$only_issues && $io->success('GD has WebP support.'); $io->success('GD has WebP support.');
} }
} }

View file

@ -38,18 +38,8 @@ use function strlen;
#[\Symfony\Component\Console\Attribute\AsCommand('partdb:currencies:update-exchange-rates|partdb:update-exchange-rates|app:update-exchange-rates', 'Updates the currency exchange rates.')] #[\Symfony\Component\Console\Attribute\AsCommand('partdb:currencies:update-exchange-rates|partdb:update-exchange-rates|app:update-exchange-rates', 'Updates the currency exchange rates.')]
class UpdateExchangeRatesCommand extends Command class UpdateExchangeRatesCommand extends Command
{ {
protected string $base_current; public function __construct(protected string $base_current, protected EntityManagerInterface $em, protected ExchangeRateUpdater $exchangeRateUpdater)
protected EntityManagerInterface $em;
protected ExchangeRateUpdater $exchangeRateUpdater;
public function __construct(string $base_current, EntityManagerInterface $entityManager, ExchangeRateUpdater $exchangeRateUpdater)
{ {
//$this->swap = $swap;
$this->base_current = $base_current;
$this->em = $entityManager;
$this->exchangeRateUpdater = $exchangeRateUpdater;
parent::__construct(); parent::__construct();
} }
@ -75,11 +65,7 @@ class UpdateExchangeRatesCommand extends Command
$iso_code = $input->getArgument('iso_code'); $iso_code = $input->getArgument('iso_code');
$repo = $this->em->getRepository(Currency::class); $repo = $this->em->getRepository(Currency::class);
if (!empty($iso_code)) { $candidates = empty($iso_code) ? $repo->findAll() : $repo->findBy(['iso_code' => $iso_code]);
$candidates = $repo->findBy(['iso_code' => $iso_code]);
} else {
$candidates = $repo->findAll();
}
$success_counter = 0; $success_counter = 0;

View file

@ -39,20 +39,11 @@ use Symfony\Contracts\Translation\TranslatorInterface;
#[\Symfony\Component\Console\Attribute\AsCommand('partdb:logs:show|app:show-logs', 'List the last event log entries.')] #[\Symfony\Component\Console\Attribute\AsCommand('partdb:logs:show|app:show-logs', 'List the last event log entries.')]
class ShowEventLogCommand extends Command class ShowEventLogCommand extends Command
{ {
protected EntityManagerInterface $entityManager;
protected TranslatorInterface $translator;
protected ElementTypeNameGenerator $elementTypeNameGenerator;
protected LogEntryRepository $repo; protected LogEntryRepository $repo;
protected LogEntryExtraFormatter $formatter;
public function __construct(EntityManagerInterface $entityManager, public function __construct(protected EntityManagerInterface $entityManager,
TranslatorInterface $translator, ElementTypeNameGenerator $elementTypeNameGenerator, LogEntryExtraFormatter $formatter) protected TranslatorInterface $translator, protected ElementTypeNameGenerator $elementTypeNameGenerator, protected LogEntryExtraFormatter $formatter)
{ {
$this->entityManager = $entityManager;
$this->translator = $translator;
$this->elementTypeNameGenerator = $elementTypeNameGenerator;
$this->formatter = $formatter;
$this->repo = $this->entityManager->getRepository(AbstractLogEntry::class); $this->repo = $this->entityManager->getRepository(AbstractLogEntry::class);
parent::__construct(); parent::__construct();
} }
@ -145,15 +136,13 @@ class ShowEventLogCommand extends Command
$target_class = $this->elementTypeNameGenerator->getLocalizedTypeLabel($entry->getTargetClass()); $target_class = $this->elementTypeNameGenerator->getLocalizedTypeLabel($entry->getTargetClass());
} }
if ($entry->getUser()) { if ($entry->getUser() instanceof \App\Entity\UserSystem\User) {
$user = $entry->getUser()->getFullName(true); $user = $entry->getUser()->getFullName(true);
} else { } elseif ($entry->isCLIEntry()) {
if ($entry->isCLIEntry()) {
$user = $entry->getCLIUsername() . ' [CLI]'; $user = $entry->getCLIUsername() . ' [CLI]';
} else { } else {
$user = $entry->getUsername() . ' [deleted]'; $user = $entry->getUsername() . ' [deleted]';
} }
}
$row = [ $row = [
$entry->getID(), $entry->getID(),

View file

@ -58,16 +58,10 @@ class ConvertBBCodeCommand extends Command
* @var string The regex (performed in PHP) used to check if a property really contains BBCODE * @var string The regex (performed in PHP) used to check if a property really contains BBCODE
*/ */
protected const BBCODE_REGEX = '/\\[.+\\].*\\[\\/.+\\]/'; protected const BBCODE_REGEX = '/\\[.+\\].*\\[\\/.+\\]/';
protected EntityManagerInterface $em;
protected PropertyAccessorInterface $propertyAccessor;
protected BBCodeToMarkdownConverter $converter; protected BBCodeToMarkdownConverter $converter;
public function __construct(EntityManagerInterface $entityManager, PropertyAccessorInterface $propertyAccessor) public function __construct(protected EntityManagerInterface $em, protected PropertyAccessorInterface $propertyAccessor)
{ {
$this->em = $entityManager;
$this->propertyAccessor = $propertyAccessor;
$this->converter = new BBCodeToMarkdownConverter(); $this->converter = new BBCodeToMarkdownConverter();
parent::__construct(); parent::__construct();
@ -126,25 +120,25 @@ class ConvertBBCodeCommand extends Command
//Fetch resulting classes //Fetch resulting classes
$results = $qb->getQuery()->getResult(); $results = $qb->getQuery()->getResult();
$io->note(sprintf('Found %d entities, that need to be converted!', count($results))); $io->note(sprintf('Found %d entities, that need to be converted!', is_countable($results) ? count($results) : 0));
//In verbose mode print the names of the entities //In verbose mode print the names of the entities
foreach ($results as $result) { foreach ($results as $result) {
/** @var AbstractNamedDBElement $result */ /** @var AbstractNamedDBElement $result */
$io->writeln( $io->writeln(
'Convert entity: '.$result->getName().' ('.get_class($result).': '.$result->getID().')', 'Convert entity: '.$result->getName().' ('.$result::class.': '.$result->getID().')',
OutputInterface::VERBOSITY_VERBOSE OutputInterface::VERBOSITY_VERBOSE
); );
foreach ($properties as $property) { foreach ($properties as $property) {
//Retrieve bbcode from entity //Retrieve bbcode from entity
$bbcode = $this->propertyAccessor->getValue($result, $property); $bbcode = $this->propertyAccessor->getValue($result, $property);
//Check if the current property really contains BBCode //Check if the current property really contains BBCode
if (!preg_match(static::BBCODE_REGEX, $bbcode)) { if (!preg_match(static::BBCODE_REGEX, (string) $bbcode)) {
continue; continue;
} }
$io->writeln( $io->writeln(
'BBCode (old): ' 'BBCode (old): '
.str_replace('\n', ' ', substr($bbcode, 0, 255)), .str_replace('\n', ' ', substr((string) $bbcode, 0, 255)),
OutputInterface::VERBOSITY_VERY_VERBOSE OutputInterface::VERBOSITY_VERY_VERBOSE
); );
$markdown = $this->converter->convert($bbcode); $markdown = $this->converter->convert($bbcode);

View file

@ -37,24 +37,11 @@ use Symfony\Component\Console\Style\SymfonyStyle;
class ImportPartKeeprCommand extends Command class ImportPartKeeprCommand extends Command
{ {
protected EntityManagerInterface $em; public function __construct(protected EntityManagerInterface $em, protected MySQLDumpXMLConverter $xml_converter,
protected MySQLDumpXMLConverter $xml_converter; protected PKDatastructureImporter $datastructureImporter, protected PKPartImporter $partImporter, protected PKImportHelper $importHelper,
protected PKDatastructureImporter $datastructureImporter; protected PKOptionalImporter $optionalImporter)
protected PKImportHelper $importHelper;
protected PKPartImporter $partImporter;
protected PKOptionalImporter $optionalImporter;
public function __construct(EntityManagerInterface $em, MySQLDumpXMLConverter $xml_converter,
PKDatastructureImporter $datastructureImporter, PKPartImporter $partImporter, PKImportHelper $importHelper,
PKOptionalImporter $optionalImporter)
{ {
parent::__construct(self::$defaultName); parent::__construct(self::$defaultName);
$this->em = $em;
$this->datastructureImporter = $datastructureImporter;
$this->importHelper = $importHelper;
$this->partImporter = $partImporter;
$this->xml_converter = $xml_converter;
$this->optionalImporter = $optionalImporter;
} }
protected function configure() protected function configure()

View file

@ -33,14 +33,9 @@ use Symfony\Component\Console\Style\SymfonyStyle;
#[\Symfony\Component\Console\Attribute\AsCommand('partdb:user:convert-to-saml-user|partdb:users:convert-to-saml-user', 'Converts a local user to a SAML user (and vice versa)')] #[\Symfony\Component\Console\Attribute\AsCommand('partdb:user:convert-to-saml-user|partdb:users:convert-to-saml-user', 'Converts a local user to a SAML user (and vice versa)')]
class ConvertToSAMLUserCommand extends Command class ConvertToSAMLUserCommand extends Command
{ {
protected EntityManagerInterface $entityManager; public function __construct(protected EntityManagerInterface $entityManager, protected bool $saml_enabled)
protected bool $saml_enabled;
public function __construct(EntityManagerInterface $entityManager, bool $saml_enabled)
{ {
parent::__construct(); parent::__construct();
$this->entityManager = $entityManager;
$this->saml_enabled = $saml_enabled;
} }
protected function configure(): void protected function configure(): void

View file

@ -37,16 +37,8 @@ use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
#[\Symfony\Component\Console\Attribute\AsCommand('partdb:users:set-password|app:set-password|users:set-password|partdb:user:set-password', 'Sets the password of a user')] #[\Symfony\Component\Console\Attribute\AsCommand('partdb:users:set-password|app:set-password|users:set-password|partdb:user:set-password', 'Sets the password of a user')]
class SetPasswordCommand extends Command class SetPasswordCommand extends Command
{ {
protected EntityManagerInterface $entityManager; public function __construct(protected EntityManagerInterface $entityManager, protected UserPasswordHasherInterface $encoder, protected EventDispatcherInterface $eventDispatcher)
protected UserPasswordHasherInterface $encoder;
protected EventDispatcherInterface $eventDispatcher;
public function __construct(EntityManagerInterface $entityManager, UserPasswordHasherInterface $passwordEncoder, EventDispatcherInterface $eventDispatcher)
{ {
$this->entityManager = $entityManager;
$this->encoder = $passwordEncoder;
$this->eventDispatcher = $eventDispatcher;
parent::__construct(); parent::__construct();
} }
@ -64,7 +56,7 @@ class SetPasswordCommand extends Command
$user = $this->entityManager->getRepository(User::class)->findByEmailOrName($user_name); $user = $this->entityManager->getRepository(User::class)->findByEmailOrName($user_name);
if (!$user) { if (!$user instanceof \App\Entity\UserSystem\User) {
$io->error(sprintf('No user with the given username %s found in the database!', $user_name)); $io->error(sprintf('No user with the given username %s found in the database!', $user_name));
return \Symfony\Component\Console\Command\Command::FAILURE; return \Symfony\Component\Console\Command\Command::FAILURE;

View file

@ -34,17 +34,9 @@ use Symfony\Component\Console\Style\SymfonyStyle;
#[\Symfony\Component\Console\Attribute\AsCommand('partdb:users:upgrade-permissions-schema', '(Manually) upgrades the permissions schema of all users to the latest version.')] #[\Symfony\Component\Console\Attribute\AsCommand('partdb:users:upgrade-permissions-schema', '(Manually) upgrades the permissions schema of all users to the latest version.')]
final class UpgradePermissionsSchemaCommand extends Command final class UpgradePermissionsSchemaCommand extends Command
{ {
private PermissionSchemaUpdater $permissionSchemaUpdater; public function __construct(private readonly PermissionSchemaUpdater $permissionSchemaUpdater, private readonly EntityManagerInterface $em, private readonly EventCommentHelper $eventCommentHelper)
private EntityManagerInterface $em;
private EventCommentHelper $eventCommentHelper;
public function __construct(PermissionSchemaUpdater $permissionSchemaUpdater, EntityManagerInterface $entityManager, EventCommentHelper $eventCommentHelper)
{ {
parent::__construct(self::$defaultName); parent::__construct(self::$defaultName);
$this->permissionSchemaUpdater = $permissionSchemaUpdater;
$this->eventCommentHelper = $eventCommentHelper;
$this->em = $entityManager;
} }
protected function configure(): void protected function configure(): void
@ -79,7 +71,7 @@ final class UpgradePermissionsSchemaCommand extends Command
} }
$io->info('Found '. count($groups_to_upgrade) .' groups and '. count($users_to_upgrade) .' users that need an update.'); $io->info('Found '. count($groups_to_upgrade) .' groups and '. count($users_to_upgrade) .' users that need an update.');
if (empty($groups_to_upgrade) && empty($users_to_upgrade)) { if ($groups_to_upgrade === [] && $users_to_upgrade === []) {
$io->success('All users and group permissions schemas are up-to-date. No update needed.'); $io->success('All users and group permissions schemas are up-to-date. No update needed.');
return \Symfony\Component\Console\Command\Command::SUCCESS; return \Symfony\Component\Console\Command\Command::SUCCESS;
@ -87,14 +79,10 @@ final class UpgradePermissionsSchemaCommand extends Command
//List all users and groups that need an update //List all users and groups that need an update
$io->section('Groups that need an update:'); $io->section('Groups that need an update:');
$io->listing(array_map(static function (Group $group) { $io->listing(array_map(static fn(Group $group): string => $group->getName() . ' (ID: '. $group->getID() .', Current version: ' . $group->getPermissions()->getSchemaVersion() . ')', $groups_to_upgrade));
return $group->getName() . ' (ID: '. $group->getID() .', Current version: ' . $group->getPermissions()->getSchemaVersion() . ')';
}, $groups_to_upgrade));
$io->section('Users that need an update:'); $io->section('Users that need an update:');
$io->listing(array_map(static function (User $user) { $io->listing(array_map(static fn(User $user): string => $user->getUsername() . ' (ID: '. $user->getID() .', Current version: ' . $user->getPermissions()->getSchemaVersion() . ')', $users_to_upgrade));
return $user->getUsername() . ' (ID: '. $user->getID() .', Current version: ' . $user->getPermissions()->getSchemaVersion() . ')';
}, $users_to_upgrade));
if(!$io->confirm('Continue with the update?', false)) { if(!$io->confirm('Continue with the update?', false)) {
$io->warning('Update aborted.'); $io->warning('Update aborted.');

View file

@ -32,12 +32,8 @@ use Symfony\Component\Console\Style\SymfonyStyle;
#[\Symfony\Component\Console\Attribute\AsCommand('partdb:users:enable|partdb:user:enable', 'Enables/Disable the login of one or more users')] #[\Symfony\Component\Console\Attribute\AsCommand('partdb:users:enable|partdb:user:enable', 'Enables/Disable the login of one or more users')]
class UserEnableCommand extends Command class UserEnableCommand extends Command
{ {
protected EntityManagerInterface $entityManager; public function __construct(protected EntityManagerInterface $entityManager, string $name = null)
public function __construct(EntityManagerInterface $entityManager, string $name = null)
{ {
$this->entityManager = $entityManager;
parent::__construct($name); parent::__construct($name);
} }
@ -70,7 +66,7 @@ class UserEnableCommand extends Command
} else { //Otherwise, fetch the users from DB } else { //Otherwise, fetch the users from DB
foreach ($usernames as $username) { foreach ($usernames as $username) {
$user = $repo->findByEmailOrName($username); $user = $repo->findByEmailOrName($username);
if ($user === null) { if (!$user instanceof \App\Entity\UserSystem\User) {
$io->error('No user found with username: '.$username); $io->error('No user found with username: '.$username);
return self::FAILURE; return self::FAILURE;
} }
@ -84,9 +80,7 @@ class UserEnableCommand extends Command
$io->note('The following users will be enabled:'); $io->note('The following users will be enabled:');
} }
$io->table(['Username', 'Enabled/Disabled'], $io->table(['Username', 'Enabled/Disabled'],
array_map(static function(User $user) { array_map(static fn(User $user) => [$user->getFullName(true), $user->isDisabled() ? 'Disabled' : 'Enabled'], $users));
return [$user->getFullName(true), $user->isDisabled() ? 'Disabled' : 'Enabled'];
}, $users));
if(!$io->confirm('Do you want to continue?')) { if(!$io->confirm('Do you want to continue?')) {
$io->warning('Aborting!'); $io->warning('Aborting!');

View file

@ -31,12 +31,8 @@ use Symfony\Component\Console\Style\SymfonyStyle;
#[\Symfony\Component\Console\Attribute\AsCommand('partdb:users:list|users:list', 'Lists all users')] #[\Symfony\Component\Console\Attribute\AsCommand('partdb:users:list|users:list', 'Lists all users')]
class UserListCommand extends Command class UserListCommand extends Command
{ {
protected EntityManagerInterface $entityManager; public function __construct(protected EntityManagerInterface $entityManager)
public function __construct(EntityManagerInterface $entityManager)
{ {
$this->entityManager = $entityManager;
parent::__construct(); parent::__construct();
} }
@ -83,7 +79,7 @@ class UserListCommand extends Command
$user->getUsername(), $user->getUsername(),
$user->getFullName(), $user->getFullName(),
$user->getEmail(), $user->getEmail(),
$user->getGroup() !== null ? $user->getGroup()->getName() . ' (ID: ' . $user->getGroup()->getID() . ')' : 'No group', $user->getGroup() instanceof \App\Entity\UserSystem\Group ? $user->getGroup()->getName() . ' (ID: ' . $user->getGroup()->getID() . ')' : 'No group',
$user->isDisabled() ? 'Yes' : 'No', $user->isDisabled() ? 'Yes' : 'No',
$user->isSAMLUser() ? 'SAML' : 'Local', $user->isSAMLUser() ? 'SAML' : 'Local',
]); ]);

View file

@ -37,17 +37,11 @@ use Symfony\Contracts\Translation\TranslatorInterface;
#[\Symfony\Component\Console\Attribute\AsCommand('partdb:users:permissions|partdb:user:permissions', 'View and edit the permissions of a given user')] #[\Symfony\Component\Console\Attribute\AsCommand('partdb:users:permissions|partdb:user:permissions', 'View and edit the permissions of a given user')]
class UsersPermissionsCommand extends Command class UsersPermissionsCommand extends Command
{ {
protected EntityManagerInterface $entityManager;
protected UserRepository $userRepository; protected UserRepository $userRepository;
protected PermissionManager $permissionResolver;
protected TranslatorInterface $translator;
public function __construct(EntityManagerInterface $entityManager, PermissionManager $permissionResolver, TranslatorInterface $translator) public function __construct(protected EntityManagerInterface $entityManager, protected PermissionManager $permissionResolver, protected TranslatorInterface $translator)
{ {
$this->entityManager = $entityManager;
$this->userRepository = $entityManager->getRepository(User::class); $this->userRepository = $entityManager->getRepository(User::class);
$this->permissionResolver = $permissionResolver;
$this->translator = $translator;
parent::__construct(self::$defaultName); parent::__construct(self::$defaultName);
} }
@ -71,7 +65,7 @@ class UsersPermissionsCommand extends Command
//Find user //Find user
$io->note('Finding user with username: ' . $username); $io->note('Finding user with username: ' . $username);
$user = $this->userRepository->findByEmailOrName($username); $user = $this->userRepository->findByEmailOrName($username);
if ($user === null) { if (!$user instanceof \App\Entity\UserSystem\User) {
$io->error('No user found with username: ' . $username); $io->error('No user found with username: ' . $username);
return Command::FAILURE; return Command::FAILURE;
} }
@ -100,7 +94,7 @@ class UsersPermissionsCommand extends Command
$new_value_str = $io->ask('Enter the new value for the permission (A = allow, D = disallow, I = inherit)'); $new_value_str = $io->ask('Enter the new value for the permission (A = allow, D = disallow, I = inherit)');
switch (strtolower($new_value_str)) { switch (strtolower((string) $new_value_str)) {
case 'a': case 'a':
case 'allow': case 'allow':
$new_value = true; $new_value = true;

View file

@ -30,13 +30,8 @@ use Symfony\Component\Console\Style\SymfonyStyle;
#[\Symfony\Component\Console\Attribute\AsCommand('partdb:version|app:version', 'Shows the currently installed version of Part-DB.')] #[\Symfony\Component\Console\Attribute\AsCommand('partdb:version|app:version', 'Shows the currently installed version of Part-DB.')]
class VersionCommand extends Command class VersionCommand extends Command
{ {
protected VersionManagerInterface $versionManager; public function __construct(protected VersionManagerInterface $versionManager, protected GitVersionInfo $gitVersionInfo)
protected GitVersionInfo $gitVersionInfo;
public function __construct(VersionManagerInterface $versionManager, GitVersionInfo $gitVersionInfo)
{ {
$this->versionManager = $versionManager;
$this->gitVersionInfo = $gitVersionInfo;
parent::__construct(); parent::__construct();
} }

View file

@ -72,29 +72,16 @@ abstract class BaseAdminController extends AbstractController
protected string $route_base = ''; protected string $route_base = '';
protected string $attachment_class = ''; protected string $attachment_class = '';
protected ?string $parameter_class = ''; protected ?string $parameter_class = '';
protected UserPasswordHasherInterface $passwordEncoder;
protected TranslatorInterface $translator;
protected AttachmentSubmitHandler $attachmentSubmitHandler;
protected EventCommentHelper $commentHelper;
protected HistoryHelper $historyHelper;
protected TimeTravel $timeTravel;
protected DataTableFactory $dataTableFactory;
/** /**
* @var EventDispatcher|EventDispatcherInterface * @var EventDispatcher|EventDispatcherInterface
*/ */
protected $eventDispatcher; protected $eventDispatcher;
protected LabelGenerator $labelGenerator;
protected LabelExampleElementsGenerator $barcodeExampleGenerator;
protected EntityManagerInterface $entityManager; public function __construct(protected TranslatorInterface $translator, protected UserPasswordHasherInterface $passwordEncoder,
protected AttachmentSubmitHandler $attachmentSubmitHandler,
public function __construct(TranslatorInterface $translator, UserPasswordHasherInterface $passwordEncoder, protected EventCommentHelper $commentHelper, protected HistoryHelper $historyHelper, protected TimeTravel $timeTravel,
AttachmentSubmitHandler $attachmentSubmitHandler, protected DataTableFactory $dataTableFactory, EventDispatcherInterface $eventDispatcher, protected LabelExampleElementsGenerator $barcodeExampleGenerator,
EventCommentHelper $commentHelper, HistoryHelper $historyHelper, TimeTravel $timeTravel, protected LabelGenerator $labelGenerator, protected EntityManagerInterface $entityManager)
DataTableFactory $dataTableFactory, EventDispatcherInterface $eventDispatcher, LabelExampleElementsGenerator $barcodeExampleGenerator,
LabelGenerator $labelGenerator, EntityManagerInterface $entityManager)
{ {
if ('' === $this->entity_class || '' === $this->form_class || '' === $this->twig_template || '' === $this->route_base) { if ('' === $this->entity_class || '' === $this->form_class || '' === $this->twig_template || '' === $this->route_base) {
throw new InvalidArgumentException('You have to override the $entity_class, $form_class, $route_base and $twig_template value in your subclasss!'); throw new InvalidArgumentException('You have to override the $entity_class, $form_class, $route_base and $twig_template value in your subclasss!');
@ -107,18 +94,7 @@ abstract class BaseAdminController extends AbstractController
if ('' === $this->parameter_class || ($this->parameter_class && !is_a($this->parameter_class, AbstractParameter::class, true))) { if ('' === $this->parameter_class || ($this->parameter_class && !is_a($this->parameter_class, AbstractParameter::class, true))) {
throw new InvalidArgumentException('You have to override the $parameter_class value with a valid Parameter class in your subclass!'); throw new InvalidArgumentException('You have to override the $parameter_class value with a valid Parameter class in your subclass!');
} }
$this->translator = $translator;
$this->passwordEncoder = $passwordEncoder;
$this->attachmentSubmitHandler = $attachmentSubmitHandler;
$this->commentHelper = $commentHelper;
$this->historyHelper = $historyHelper;
$this->timeTravel = $timeTravel;
$this->dataTableFactory = $dataTableFactory;
$this->eventDispatcher = $eventDispatcher; $this->eventDispatcher = $eventDispatcher;
$this->barcodeExampleGenerator = $barcodeExampleGenerator;
$this->labelGenerator = $labelGenerator;
$this->entityManager = $entityManager;
} }
protected function revertElementIfNeeded(AbstractDBElement $entity, ?string $timestamp): ?DateTime protected function revertElementIfNeeded(AbstractDBElement $entity, ?string $timestamp): ?DateTime
@ -177,7 +153,7 @@ abstract class BaseAdminController extends AbstractController
$form_options = [ $form_options = [
'attachment_class' => $this->attachment_class, 'attachment_class' => $this->attachment_class,
'parameter_class' => $this->parameter_class, 'parameter_class' => $this->parameter_class,
'disabled' => null !== $timeTravel_timestamp, 'disabled' => $timeTravel_timestamp instanceof \DateTime,
]; ];
//Disable editing of options, if user is not allowed to use twig... //Disable editing of options, if user is not allowed to use twig...
@ -269,13 +245,7 @@ abstract class BaseAdminController extends AbstractController
protected function _new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?AbstractNamedDBElement $entity = null) protected function _new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?AbstractNamedDBElement $entity = null)
{ {
if (null === $entity) { $new_entity = $entity instanceof \App\Entity\Base\AbstractNamedDBElement ? clone $entity : new $this->entity_class();
/** @var AbstractStructuralDBElement|User $new_entity */
$new_entity = new $this->entity_class();
} else {
/** @var AbstractStructuralDBElement|User $new_entity */
$new_entity = clone $entity;
}
$this->denyAccessUnlessGranted('read', $new_entity); $this->denyAccessUnlessGranted('read', $new_entity);
@ -287,9 +257,8 @@ abstract class BaseAdminController extends AbstractController
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//Perform additional actions //Perform additional actions
if ($this->additionalActionNew($form, $new_entity)) { if ($form->isSubmitted() && $form->isValid() && $this->additionalActionNew($form, $new_entity)) {
//Upload passed files //Upload passed files
$attachments = $form['attachments']; $attachments = $form['attachments'];
foreach ($attachments as $attachment) { foreach ($attachments as $attachment) {
@ -314,16 +283,12 @@ abstract class BaseAdminController extends AbstractController
); );
} }
} }
$this->commentHelper->setMessage($form['log_comment']->getData()); $this->commentHelper->setMessage($form['log_comment']->getData());
$em->persist($new_entity); $em->persist($new_entity);
$em->flush(); $em->flush();
$this->addFlash('success', 'entity.created_flash'); $this->addFlash('success', 'entity.created_flash');
return $this->redirectToRoute($this->route_base.'_edit', ['id' => $new_entity->getID()]); return $this->redirectToRoute($this->route_base.'_edit', ['id' => $new_entity->getID()]);
} }
}
if ($form->isSubmitted() && !$form->isValid()) { if ($form->isSubmitted() && !$form->isValid()) {
$this->addFlash('error', 'entity.created_flash.invalid'); $this->addFlash('error', 'entity.created_flash.invalid');
@ -369,7 +334,7 @@ abstract class BaseAdminController extends AbstractController
} }
} }
} }
catch (UnexpectedValueException $e) { catch (UnexpectedValueException) {
$this->addFlash('error', 'parts.import.flash.error.invalid_file'); $this->addFlash('error', 'parts.import.flash.error.invalid_file');
} }
} }

View file

@ -64,8 +64,6 @@ class CurrencyController extends BaseAdminController
protected string $attachment_class = CurrencyAttachment::class; protected string $attachment_class = CurrencyAttachment::class;
protected ?string $parameter_class = CurrencyParameter::class; protected ?string $parameter_class = CurrencyParameter::class;
protected ExchangeRateUpdater $exchangeRateUpdater;
public function __construct( public function __construct(
TranslatorInterface $translator, TranslatorInterface $translator,
UserPasswordHasherInterface $passwordEncoder, UserPasswordHasherInterface $passwordEncoder,
@ -78,10 +76,8 @@ class CurrencyController extends BaseAdminController
LabelExampleElementsGenerator $barcodeExampleGenerator, LabelExampleElementsGenerator $barcodeExampleGenerator,
LabelGenerator $labelGenerator, LabelGenerator $labelGenerator,
EntityManagerInterface $entityManager, EntityManagerInterface $entityManager,
ExchangeRateUpdater $exchangeRateUpdater protected ExchangeRateUpdater $exchangeRateUpdater
) { ) {
$this->exchangeRateUpdater = $exchangeRateUpdater;
parent::__construct( parent::__construct(
$translator, $translator,
$passwordEncoder, $passwordEncoder,

View file

@ -45,9 +45,6 @@ class ManufacturerController extends BaseAdminController
protected string $attachment_class = ManufacturerAttachment::class; protected string $attachment_class = ManufacturerAttachment::class;
protected ?string $parameter_class = ManufacturerParameter::class; protected ?string $parameter_class = ManufacturerParameter::class;
/**
* @return RedirectResponse
*/
#[Route(path: '/{id}', name: 'manufacturer_delete', methods: ['DELETE'])] #[Route(path: '/{id}', name: 'manufacturer_delete', methods: ['DELETE'])]
public function delete(Request $request, Manufacturer $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse public function delete(Request $request, Manufacturer $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse
{ {

View file

@ -37,15 +37,8 @@ use Symfony\Contracts\Cache\CacheInterface;
class HomepageController extends AbstractController class HomepageController extends AbstractController
{ {
protected CacheInterface $cache; public function __construct(protected CacheInterface $cache, protected KernelInterface $kernel, protected DataTableFactory $dataTable)
protected KernelInterface $kernel;
protected DataTableFactory $dataTable;
public function __construct(CacheInterface $cache, KernelInterface $kernel, DataTableFactory $dataTable)
{ {
$this->cache = $cache;
$this->kernel = $kernel;
$this->dataTable = $dataTable;
} }
public function getBanner(): string public function getBanner(): string

View file

@ -62,20 +62,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
#[Route(path: '/label')] #[Route(path: '/label')]
class LabelController extends AbstractController class LabelController extends AbstractController
{ {
protected LabelGenerator $labelGenerator; public function __construct(protected LabelGenerator $labelGenerator, protected EntityManagerInterface $em, protected ElementTypeNameGenerator $elementTypeNameGenerator, protected RangeParser $rangeParser, protected TranslatorInterface $translator)
protected EntityManagerInterface $em;
protected ElementTypeNameGenerator $elementTypeNameGenerator;
protected RangeParser $rangeParser;
protected TranslatorInterface $translator;
public function __construct(LabelGenerator $labelGenerator, EntityManagerInterface $em, ElementTypeNameGenerator $elementTypeNameGenerator,
RangeParser $rangeParser, TranslatorInterface $translator)
{ {
$this->labelGenerator = $labelGenerator;
$this->em = $em;
$this->elementTypeNameGenerator = $elementTypeNameGenerator;
$this->rangeParser = $rangeParser;
$this->translator = $translator;
} }
#[Route(path: '/dialog', name: 'label_dialog')] #[Route(path: '/dialog', name: 'label_dialog')]
@ -85,15 +73,11 @@ class LabelController extends AbstractController
$this->denyAccessUnlessGranted('@labels.create_labels'); $this->denyAccessUnlessGranted('@labels.create_labels');
//If we inherit a LabelProfile, the user need to have access to it... //If we inherit a LabelProfile, the user need to have access to it...
if (null !== $profile) { if ($profile instanceof \App\Entity\LabelSystem\LabelProfile) {
$this->denyAccessUnlessGranted('read', $profile); $this->denyAccessUnlessGranted('read', $profile);
} }
if ($profile) { $label_options = $profile instanceof \App\Entity\LabelSystem\LabelProfile ? $profile->getOptions() : new LabelOptions();
$label_options = $profile->getOptions();
} else {
$label_options = new LabelOptions();
}
//We have to disable the options, if twig mode is selected and user is not allowed to use it. //We have to disable the options, if twig mode is selected and user is not allowed to use it.
$disable_options = 'twig' === $label_options->getLinesMode() && !$this->isGranted('@labels.use_twig'); $disable_options = 'twig' === $label_options->getLinesMode() && !$this->isGranted('@labels.use_twig');
@ -107,7 +91,7 @@ class LabelController extends AbstractController
$target_id = $request->query->get('target_id', null); $target_id = $request->query->get('target_id', null);
$generate = $request->query->getBoolean('generate', false); $generate = $request->query->getBoolean('generate', false);
if (null === $profile && is_string($target_type)) { if (!$profile instanceof \App\Entity\LabelSystem\LabelProfile && is_string($target_type)) {
$label_options->setSupportedElement($target_type); $label_options->setSupportedElement($target_type);
} }
if (is_string($target_id)) { if (is_string($target_id)) {
@ -124,10 +108,10 @@ class LabelController extends AbstractController
$filename = 'invalid.pdf'; $filename = 'invalid.pdf';
//Generate PDF either when the form is submitted and valid, or the form was not submit yet, and generate is set //Generate PDF either when the form is submitted and valid, or the form was not submit yet, and generate is set
if (($form->isSubmitted() && $form->isValid()) || ($generate && !$form->isSubmitted() && null !== $profile)) { if (($form->isSubmitted() && $form->isValid()) || ($generate && !$form->isSubmitted() && $profile instanceof \App\Entity\LabelSystem\LabelProfile)) {
$target_id = (string) $form->get('target_id')->getData(); $target_id = (string) $form->get('target_id')->getData();
$targets = $this->findObjects($form_options->getSupportedElement(), $target_id); $targets = $this->findObjects($form_options->getSupportedElement(), $target_id);
if (!empty($targets)) { if ($targets !== []) {
try { try {
$pdf_data = $this->labelGenerator->generateLabel($form_options, $targets); $pdf_data = $this->labelGenerator->generateLabel($form_options, $targets);
$filename = $this->getLabelName($targets[0], $profile); $filename = $this->getLabelName($targets[0], $profile);

View file

@ -52,20 +52,13 @@ use Symfony\Component\Routing\Annotation\Route;
#[Route(path: '/log')] #[Route(path: '/log')]
class LogController extends AbstractController class LogController extends AbstractController
{ {
protected EntityManagerInterface $entityManager;
protected TimeTravel $timeTravel;
protected DBElementRepository $dbRepository; protected DBElementRepository $dbRepository;
public function __construct(EntityManagerInterface $entityManager, TimeTravel $timeTravel) public function __construct(protected EntityManagerInterface $entityManager, protected TimeTravel $timeTravel)
{ {
$this->entityManager = $entityManager;
$this->timeTravel = $timeTravel;
$this->dbRepository = $entityManager->getRepository(AbstractDBElement::class); $this->dbRepository = $entityManager->getRepository(AbstractDBElement::class);
} }
/**
* @return Response
*/
#[Route(path: '/', name: 'log_view')] #[Route(path: '/', name: 'log_view')]
public function showLogs(Request $request, DataTableFactory $dataTable): Response public function showLogs(Request $request, DataTableFactory $dataTable): Response
{ {
@ -96,8 +89,6 @@ class LogController extends AbstractController
/** /**
* @param Request $request * @param Request $request
* @param AbstractLogEntry $logEntry
* @return Response
*/ */
#[Route(path: '/{id}/details', name: 'log_details')] #[Route(path: '/{id}/details', name: 'log_details')]
public function logDetails(AbstractLogEntry $logEntry, LogEntryExtraFormatter $logEntryExtraFormatter, public function logDetails(AbstractLogEntry $logEntry, LogEntryExtraFormatter $logEntryExtraFormatter,
@ -150,7 +141,7 @@ class LogController extends AbstractController
} }
$log_element = $this->entityManager->find(AbstractLogEntry::class, $id); $log_element = $this->entityManager->find(AbstractLogEntry::class, $id);
if (null === $log_element) { if (!$log_element instanceof \App\Entity\LogSystem\AbstractLogEntry) {
throw new InvalidArgumentException('No log entry with the given ID is existing!'); throw new InvalidArgumentException('No log entry with the given ID is existing!');
} }

View file

@ -62,16 +62,8 @@ use function Symfony\Component\Translation\t;
#[Route(path: '/part')] #[Route(path: '/part')]
class PartController extends AbstractController class PartController extends AbstractController
{ {
protected PricedetailHelper $pricedetailHelper; public function __construct(protected PricedetailHelper $pricedetailHelper, protected PartPreviewGenerator $partPreviewGenerator, protected EventCommentHelper $commentHelper)
protected PartPreviewGenerator $partPreviewGenerator;
protected EventCommentHelper $commentHelper;
public function __construct(PricedetailHelper $pricedetailHelper,
PartPreviewGenerator $partPreviewGenerator, EventCommentHelper $commentHelper)
{ {
$this->pricedetailHelper = $pricedetailHelper;
$this->partPreviewGenerator = $partPreviewGenerator;
$this->commentHelper = $commentHelper;
} }
/** /**
@ -218,11 +210,13 @@ class PartController extends AbstractController
?Part $part = null, ?Project $project = null): Response ?Part $part = null, ?Project $project = null): Response
{ {
if ($part) { //Clone part if ($part instanceof \App\Entity\Parts\Part) {
//Clone part
$new_part = clone $part; $new_part = clone $part;
} else if ($project) { //Initialize a new part for a build part from the given project } elseif ($project instanceof \App\Entity\ProjectSystem\Project) {
//Initialize a new part for a build part from the given project
//Ensure that the project has not already a build part //Ensure that the project has not already a build part
if ($project->getBuildPart() !== null) { if ($project->getBuildPart() instanceof \App\Entity\Parts\Part) {
$this->addFlash('error', 'part.new_build_part.error.build_part_already_exists'); $this->addFlash('error', 'part.new_build_part.error.build_part_already_exists');
return $this->redirectToRoute('part_edit', ['id' => $project->getBuildPart()->getID()]); return $this->redirectToRoute('part_edit', ['id' => $project->getBuildPart()->getID()]);
} }
@ -235,7 +229,7 @@ class PartController extends AbstractController
$cid = $request->get('category', null); $cid = $request->get('category', null);
$category = $cid ? $em->find(Category::class, $cid) : null; $category = $cid ? $em->find(Category::class, $cid) : null;
if (null !== $category && null === $new_part->getCategory()) { if ($category instanceof \App\Entity\Parts\Category && !$new_part->getCategory() instanceof \App\Entity\Parts\Category) {
$new_part->setCategory($category); $new_part->setCategory($category);
$new_part->setDescription($category->getDefaultDescription()); $new_part->setDescription($category->getDefaultDescription());
$new_part->setComment($category->getDefaultComment()); $new_part->setComment($category->getDefaultComment());
@ -243,19 +237,19 @@ class PartController extends AbstractController
$fid = $request->get('footprint', null); $fid = $request->get('footprint', null);
$footprint = $fid ? $em->find(Footprint::class, $fid) : null; $footprint = $fid ? $em->find(Footprint::class, $fid) : null;
if (null !== $footprint && null === $new_part->getFootprint()) { if ($footprint instanceof \App\Entity\Parts\Footprint && !$new_part->getFootprint() instanceof \App\Entity\Parts\Footprint) {
$new_part->setFootprint($footprint); $new_part->setFootprint($footprint);
} }
$mid = $request->get('manufacturer', null); $mid = $request->get('manufacturer', null);
$manufacturer = $mid ? $em->find(Manufacturer::class, $mid) : null; $manufacturer = $mid ? $em->find(Manufacturer::class, $mid) : null;
if (null !== $manufacturer && null === $new_part->getManufacturer()) { if ($manufacturer instanceof \App\Entity\Parts\Manufacturer && !$new_part->getManufacturer() instanceof \App\Entity\Parts\Manufacturer) {
$new_part->setManufacturer($manufacturer); $new_part->setManufacturer($manufacturer);
} }
$store_id = $request->get('storelocation', null); $store_id = $request->get('storelocation', null);
$storelocation = $store_id ? $em->find(Storelocation::class, $store_id) : null; $storelocation = $store_id ? $em->find(Storelocation::class, $store_id) : null;
if (null !== $storelocation && $new_part->getPartLots()->isEmpty()) { if ($storelocation instanceof \App\Entity\Parts\Storelocation && $new_part->getPartLots()->isEmpty()) {
$partLot = new PartLot(); $partLot = new PartLot();
$partLot->setStorageLocation($storelocation); $partLot->setStorageLocation($storelocation);
$partLot->setInstockUnknown(true); $partLot->setInstockUnknown(true);
@ -264,7 +258,7 @@ class PartController extends AbstractController
$supplier_id = $request->get('supplier', null); $supplier_id = $request->get('supplier', null);
$supplier = $supplier_id ? $em->find(Supplier::class, $supplier_id) : null; $supplier = $supplier_id ? $em->find(Supplier::class, $supplier_id) : null;
if (null !== $supplier && $new_part->getOrderdetails()->isEmpty()) { if ($supplier instanceof \App\Entity\Parts\Supplier && $new_part->getOrderdetails()->isEmpty()) {
$orderdetail = new Orderdetail(); $orderdetail = new Orderdetail();
$orderdetail->setSupplier($supplier); $orderdetail->setSupplier($supplier);
$new_part->addOrderdetail($orderdetail); $new_part->addOrderdetail($orderdetail);
@ -335,7 +329,7 @@ class PartController extends AbstractController
if ($this->isCsrfTokenValid('part_withraw' . $part->getID(), $request->request->get('_csfr'))) { if ($this->isCsrfTokenValid('part_withraw' . $part->getID(), $request->request->get('_csfr'))) {
//Retrieve partlot from the request //Retrieve partlot from the request
$partLot = $em->find(PartLot::class, $request->request->get('lot_id')); $partLot = $em->find(PartLot::class, $request->request->get('lot_id'));
if($partLot === null) { if(!$partLot instanceof \App\Entity\Parts\PartLot) {
throw new \RuntimeException('Part lot not found!'); throw new \RuntimeException('Part lot not found!');
} }
//Ensure that the partlot belongs to the part //Ensure that the partlot belongs to the part
@ -375,7 +369,7 @@ class PartController extends AbstractController
default: default:
throw new \RuntimeException("Unknown action!"); throw new \RuntimeException("Unknown action!");
} }
} catch (AccessDeniedException $exception) { } catch (AccessDeniedException) {
$this->addFlash('error', t('part.withdraw.access_denied')); $this->addFlash('error', t('part.withdraw.access_denied'));
goto err; goto err;
} }

View file

@ -36,22 +36,10 @@ use UnexpectedValueException;
class PartImportExportController extends AbstractController class PartImportExportController extends AbstractController
{ {
private PartsTableActionHandler $partsTableActionHandler; public function __construct(private readonly PartsTableActionHandler $partsTableActionHandler, private readonly EntityImporter $entityImporter, private readonly EventCommentHelper $commentHelper)
private EntityImporter $entityImporter;
private EventCommentHelper $commentHelper;
public function __construct(PartsTableActionHandler $partsTableActionHandler,
EntityImporter $entityImporter, EventCommentHelper $commentHelper)
{ {
$this->partsTableActionHandler = $partsTableActionHandler;
$this->entityImporter = $entityImporter;
$this->commentHelper = $commentHelper;
} }
/**
* @param Request $request
* @return Response
*/
#[Route(path: '/parts/import', name: 'parts_import')] #[Route(path: '/parts/import', name: 'parts_import')]
public function importParts(Request $request): Response public function importParts(Request $request): Response
{ {
@ -116,16 +104,13 @@ class PartImportExportController extends AbstractController
]); ]);
} }
/**
* @return Response
*/
#[Route(path: '/parts/export', name: 'parts_export', methods: ['GET'])] #[Route(path: '/parts/export', name: 'parts_export', methods: ['GET'])]
public function exportParts(Request $request, EntityExporter $entityExporter): Response public function exportParts(Request $request, EntityExporter $entityExporter): Response
{ {
$ids = $request->query->get('ids', ''); $ids = $request->query->get('ids', '');
$parts = $this->partsTableActionHandler->idStringToArray($ids); $parts = $this->partsTableActionHandler->idStringToArray($ids);
if (empty($parts)) { if ($parts === []) {
throw new \RuntimeException('No parts found!'); throw new \RuntimeException('No parts found!');
} }

View file

@ -48,18 +48,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class PartListsController extends AbstractController class PartListsController extends AbstractController
{ {
private EntityManagerInterface $entityManager; public function __construct(private readonly EntityManagerInterface $entityManager, private readonly NodesListBuilder $nodesListBuilder, private readonly DataTableFactory $dataTableFactory, private readonly TranslatorInterface $translator)
private NodesListBuilder $nodesListBuilder;
private DataTableFactory $dataTableFactory;
private TranslatorInterface $translator;
public function __construct(EntityManagerInterface $entityManager, NodesListBuilder $nodesListBuilder, DataTableFactory $dataTableFactory, TranslatorInterface $translator)
{ {
$this->entityManager = $entityManager;
$this->nodesListBuilder = $nodesListBuilder;
$this->dataTableFactory = $dataTableFactory;
$this->translator = $translator;
} }
#[Route(path: '/table/action', name: 'table_action', methods: ['POST'])] #[Route(path: '/table/action', name: 'table_action', methods: ['POST'])]
@ -100,8 +90,6 @@ class PartListsController extends AbstractController
/** /**
* Disable the given form interface after creation of the form by removing and reattaching the form. * Disable the given form interface after creation of the form by removing and reattaching the form.
* @param FormInterface $form
* @return void
*/ */
private function disableFormFieldAfterCreation(FormInterface $form, bool $disabled = true): void private function disableFormFieldAfterCreation(FormInterface $form, bool $disabled = true): void
{ {
@ -109,12 +97,12 @@ class PartListsController extends AbstractController
$attrs['disabled'] = $disabled; $attrs['disabled'] = $disabled;
$parent = $form->getParent(); $parent = $form->getParent();
if ($parent === null) { if (!$parent instanceof \Symfony\Component\Form\FormInterface) {
throw new \RuntimeException('This function can only be used on form fields that are children of another form!'); throw new \RuntimeException('This function can only be used on form fields that are children of another form!');
} }
$parent->remove($form->getName()); $parent->remove($form->getName());
$parent->add($form->getName(), get_class($form->getConfig()->getType()->getInnerType()), $attrs); $parent->add($form->getName(), $form->getConfig()->getType()->getInnerType()::class, $attrs);
} }
/** /**
@ -125,7 +113,6 @@ class PartListsController extends AbstractController
* @param callable|null $form_changer A function that is called with the form object as parameter. This function can be used to customize the form * @param callable|null $form_changer A function that is called with the form object as parameter. This function can be used to customize the form
* @param array $additonal_template_vars Any additional template variables that should be passed to the template * @param array $additonal_template_vars Any additional template variables that should be passed to the template
* @param array $additional_table_vars Any additional variables that should be passed to the table creation * @param array $additional_table_vars Any additional variables that should be passed to the table creation
* @return Response
*/ */
protected function showListWithFilter(Request $request, string $template, ?callable $filter_changer = null, ?callable $form_changer = null, array $additonal_template_vars = [], array $additional_table_vars = []): Response protected function showListWithFilter(Request $request, string $template, ?callable $filter_changer = null, ?callable $form_changer = null, array $additonal_template_vars = [], array $additional_table_vars = []): Response
{ {
@ -172,9 +159,6 @@ class PartListsController extends AbstractController
], $additonal_template_vars)); ], $additonal_template_vars));
} }
/**
* @return JsonResponse|Response
*/
#[Route(path: '/category/{id}/parts', name: 'part_list_category')] #[Route(path: '/category/{id}/parts', name: 'part_list_category')]
public function showCategory(Category $category, Request $request): Response public function showCategory(Category $category, Request $request): Response
{ {
@ -193,9 +177,6 @@ class PartListsController extends AbstractController
); );
} }
/**
* @return JsonResponse|Response
*/
#[Route(path: '/footprint/{id}/parts', name: 'part_list_footprint')] #[Route(path: '/footprint/{id}/parts', name: 'part_list_footprint')]
public function showFootprint(Footprint $footprint, Request $request): Response public function showFootprint(Footprint $footprint, Request $request): Response
{ {
@ -214,9 +195,6 @@ class PartListsController extends AbstractController
); );
} }
/**
* @return JsonResponse|Response
*/
#[Route(path: '/manufacturer/{id}/parts', name: 'part_list_manufacturer')] #[Route(path: '/manufacturer/{id}/parts', name: 'part_list_manufacturer')]
public function showManufacturer(Manufacturer $manufacturer, Request $request): Response public function showManufacturer(Manufacturer $manufacturer, Request $request): Response
{ {
@ -235,9 +213,6 @@ class PartListsController extends AbstractController
); );
} }
/**
* @return JsonResponse|Response
*/
#[Route(path: '/store_location/{id}/parts', name: 'part_list_store_location')] #[Route(path: '/store_location/{id}/parts', name: 'part_list_store_location')]
public function showStorelocation(Storelocation $storelocation, Request $request): Response public function showStorelocation(Storelocation $storelocation, Request $request): Response
{ {
@ -256,9 +231,6 @@ class PartListsController extends AbstractController
); );
} }
/**
* @return JsonResponse|Response
*/
#[Route(path: '/supplier/{id}/parts', name: 'part_list_supplier')] #[Route(path: '/supplier/{id}/parts', name: 'part_list_supplier')]
public function showSupplier(Supplier $supplier, Request $request): Response public function showSupplier(Supplier $supplier, Request $request): Response
{ {
@ -277,9 +249,6 @@ class PartListsController extends AbstractController
); );
} }
/**
* @return JsonResponse|Response
*/
#[Route(path: '/parts/by_tag/{tag}', name: 'part_list_tags', requirements: ['tag' => '.*'])] #[Route(path: '/parts/by_tag/{tag}', name: 'part_list_tags', requirements: ['tag' => '.*'])]
public function showTag(string $tag, Request $request): Response public function showTag(string $tag, Request $request): Response
{ {
@ -321,9 +290,6 @@ class PartListsController extends AbstractController
return $filter; return $filter;
} }
/**
* @return JsonResponse|Response
*/
#[Route(path: '/parts/search', name: 'parts_search')] #[Route(path: '/parts/search', name: 'parts_search')]
public function showSearch(Request $request, DataTableFactory $dataTable): Response public function showSearch(Request $request, DataTableFactory $dataTable): Response
{ {
@ -343,9 +309,6 @@ class PartListsController extends AbstractController
); );
} }
/**
* @return Response
*/
#[Route(path: '/parts', name: 'parts_show_all')] #[Route(path: '/parts', name: 'parts_show_all')]
public function showAll(Request $request): Response public function showAll(Request $request): Response
{ {

View file

@ -50,11 +50,8 @@ use function Symfony\Component\Translation\t;
#[Route(path: '/project')] #[Route(path: '/project')]
class ProjectController extends AbstractController class ProjectController extends AbstractController
{ {
private DataTableFactory $dataTableFactory; public function __construct(private readonly DataTableFactory $dataTableFactory)
public function __construct(DataTableFactory $dataTableFactory)
{ {
$this->dataTableFactory = $dataTableFactory;
} }
#[Route(path: '/{id}/info', name: 'project_info', requirements: ['id' => '\d+'])] #[Route(path: '/{id}/info', name: 'project_info', requirements: ['id' => '\d+'])]
@ -180,9 +177,7 @@ class ProjectController extends AbstractController
if (count ($errors) > 0) { if (count ($errors) > 0) {
$this->addFlash('error', t('project.bom_import.flash.invalid_entries')); $this->addFlash('error', t('project.bom_import.flash.invalid_entries'));
} }
} catch (\UnexpectedValueException $e) { } catch (\UnexpectedValueException|SyntaxError $e) {
$this->addFlash('error', t('project.bom_import.flash.invalid_file', ['%message%' => $e->getMessage()]));
} catch (SyntaxError $e) {
$this->addFlash('error', t('project.bom_import.flash.invalid_file', ['%message%' => $e->getMessage()])); $this->addFlash('error', t('project.bom_import.flash.invalid_file', ['%message%' => $e->getMessage()]));
} }
} }
@ -194,15 +189,11 @@ class ProjectController extends AbstractController
]); ]);
} }
/**
* @param Request $request
* @param Project|null $project
*/
#[Route(path: '/add_parts', name: 'project_add_parts_no_id')] #[Route(path: '/add_parts', name: 'project_add_parts_no_id')]
#[Route(path: '/{id}/add_parts', name: 'project_add_parts', requirements: ['id' => '\d+'])] #[Route(path: '/{id}/add_parts', name: 'project_add_parts', requirements: ['id' => '\d+'])]
public function addPart(Request $request, EntityManagerInterface $entityManager, ?Project $project): Response public function addPart(Request $request, EntityManagerInterface $entityManager, ?Project $project): Response
{ {
if($project) { if($project instanceof \App\Entity\ProjectSystem\Project) {
$this->denyAccessUnlessGranted('edit', $project); $this->denyAccessUnlessGranted('edit', $project);
} else { } else {
$this->denyAccessUnlessGranted('@projects.edit'); $this->denyAccessUnlessGranted('@projects.edit');
@ -212,7 +203,7 @@ class ProjectController extends AbstractController
$builder->add('project', StructuralEntityType::class, [ $builder->add('project', StructuralEntityType::class, [
'class' => Project::class, 'class' => Project::class,
'required' => true, 'required' => true,
'disabled' => $project !== null, //If a project is given, disable the field 'disabled' => $project instanceof \App\Entity\ProjectSystem\Project, //If a project is given, disable the field
'data' => $project, 'data' => $project,
'constraints' => [ 'constraints' => [
new NotNull() new NotNull()
@ -224,7 +215,7 @@ class ProjectController extends AbstractController
//Preset the BOM entries with the selected parts, when the form was not submitted yet //Preset the BOM entries with the selected parts, when the form was not submitted yet
$preset_data = new ArrayCollection(); $preset_data = new ArrayCollection();
foreach (explode(',', $request->get('parts', '')) as $part_id) { foreach (explode(',', (string) $request->get('parts', '')) as $part_id) {
$part = $entityManager->getRepository(Part::class)->find($part_id); $part = $entityManager->getRepository(Part::class)->find($part_id);
if (null !== $part) { if (null !== $part) {
//If there is already a BOM entry for this part, we use this one (we edit it then) //If there is already a BOM entry for this part, we use this one (we edit it then)

View file

@ -32,15 +32,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class RedirectController extends AbstractController class RedirectController extends AbstractController
{ {
protected string $default_locale; public function __construct(protected string $default_locale, protected TranslatorInterface $translator, protected bool $enforce_index_php)
protected TranslatorInterface $translator;
protected bool $enforce_index_php;
public function __construct(string $default_locale, TranslatorInterface $translator, bool $enforce_index_php)
{ {
$this->default_locale = $default_locale;
$this->translator = $translator;
$this->enforce_index_php = $enforce_index_php;
} }
/** /**

View file

@ -54,13 +54,8 @@ use Symfony\Component\Routing\Annotation\Route;
#[Route(path: '/scan')] #[Route(path: '/scan')]
class ScanController extends AbstractController class ScanController extends AbstractController
{ {
protected BarcodeRedirector $barcodeParser; public function __construct(protected BarcodeRedirector $barcodeParser, protected BarcodeNormalizer $barcodeNormalizer)
protected BarcodeNormalizer $barcodeNormalizer;
public function __construct(BarcodeRedirector $barcodeParser, BarcodeNormalizer $barcodeNormalizer)
{ {
$this->barcodeParser = $barcodeParser;
$this->barcodeNormalizer = $barcodeNormalizer;
} }
#[Route(path: '', name: 'scan_dialog')] #[Route(path: '', name: 'scan_dialog')]
@ -79,10 +74,10 @@ class ScanController extends AbstractController
try { try {
return $this->redirect($this->barcodeParser->getRedirectURL($type, $id)); return $this->redirect($this->barcodeParser->getRedirectURL($type, $id));
} catch (EntityNotFoundException $exception) { } catch (EntityNotFoundException) {
$this->addFlash('success', 'scan.qr_not_found'); $this->addFlash('success', 'scan.qr_not_found');
} }
} catch (InvalidArgumentException $exception) { } catch (InvalidArgumentException) {
$this->addFlash('error', 'scan.format_unknown'); $this->addFlash('error', 'scan.format_unknown');
} }
} }
@ -101,7 +96,7 @@ class ScanController extends AbstractController
$this->addFlash('success', 'scan.qr_success'); $this->addFlash('success', 'scan.qr_success');
return $this->redirect($this->barcodeParser->getRedirectURL($type, $id)); return $this->redirect($this->barcodeParser->getRedirectURL($type, $id));
} catch (EntityNotFoundException $exception) { } catch (EntityNotFoundException) {
$this->addFlash('success', 'scan.qr_not_found'); $this->addFlash('success', 'scan.qr_not_found');
return $this->redirectToRoute('homepage'); return $this->redirectToRoute('homepage');

View file

@ -48,13 +48,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class SecurityController extends AbstractController class SecurityController extends AbstractController
{ {
protected TranslatorInterface $translator; public function __construct(protected TranslatorInterface $translator, protected bool $allow_email_pw_reset)
protected bool $allow_email_pw_reset;
public function __construct(TranslatorInterface $translator, bool $allow_email_pw_reset)
{ {
$this->translator = $translator;
$this->allow_email_pw_reset = $allow_email_pw_reset;
} }
#[Route(path: '/login', name: 'login', methods: ['GET', 'POST'])] #[Route(path: '/login', name: 'login', methods: ['GET', 'POST'])]
@ -76,7 +71,7 @@ class SecurityController extends AbstractController
* @return RedirectResponse|Response * @return RedirectResponse|Response
*/ */
#[Route(path: '/pw_reset/request', name: 'pw_reset_request')] #[Route(path: '/pw_reset/request', name: 'pw_reset_request')]
public function requestPwReset(PasswordResetManager $passwordReset, Request $request) public function requestPwReset(PasswordResetManager $passwordReset, Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
{ {
if (!$this->allow_email_pw_reset) { if (!$this->allow_email_pw_reset) {
throw new AccessDeniedHttpException('The password reset via email is disabled!'); throw new AccessDeniedHttpException('The password reset via email is disabled!');
@ -119,7 +114,7 @@ class SecurityController extends AbstractController
* @return RedirectResponse|Response * @return RedirectResponse|Response
*/ */
#[Route(path: '/pw_reset/new_pw/{user}/{token}', name: 'pw_reset_new_pw')] #[Route(path: '/pw_reset/new_pw/{user}/{token}', name: 'pw_reset_new_pw')]
public function pwResetNewPw(PasswordResetManager $passwordReset, Request $request, EntityManagerInterface $em, EventDispatcherInterface $eventDispatcher, ?string $user = null, ?string $token = null) public function pwResetNewPw(PasswordResetManager $passwordReset, Request $request, EntityManagerInterface $em, EventDispatcherInterface $eventDispatcher, ?string $user = null, ?string $token = null): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
{ {
if (!$this->allow_email_pw_reset) { if (!$this->allow_email_pw_reset) {
throw new AccessDeniedHttpException('The password reset via email is disabled!'); throw new AccessDeniedHttpException('The password reset via email is disabled!');
@ -189,7 +184,7 @@ class SecurityController extends AbstractController
} }
#[Route(path: '/logout', name: 'logout')] #[Route(path: '/logout', name: 'logout')]
public function logout(): void public function logout(): never
{ {
throw new RuntimeException('Will be intercepted before getting here'); throw new RuntimeException('Will be intercepted before getting here');
} }

View file

@ -42,15 +42,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
#[Route(path: '/select_api')] #[Route(path: '/select_api')]
class SelectAPIController extends AbstractController class SelectAPIController extends AbstractController
{ {
private NodesListBuilder $nodesListBuilder; public function __construct(private readonly NodesListBuilder $nodesListBuilder, private readonly TranslatorInterface $translator, private readonly StructuralEntityChoiceHelper $choiceHelper)
private TranslatorInterface $translator;
private StructuralEntityChoiceHelper $choiceHelper;
public function __construct(NodesListBuilder $nodesListBuilder, TranslatorInterface $translator, StructuralEntityChoiceHelper $choiceHelper)
{ {
$this->nodesListBuilder = $nodesListBuilder;
$this->translator = $translator;
$this->choiceHelper = $choiceHelper;
} }
#[Route(path: '/category', name: 'select_category')] #[Route(path: '/category', name: 'select_category')]
@ -92,17 +85,12 @@ class SelectAPIController extends AbstractController
3 => $this->translator->trans('export.level.full'), 3 => $this->translator->trans('export.level.full'),
]; ];
return $this->json(array_map(static function ($key, $value) { return $this->json(array_map(static fn($key, $value) => [
return [
'text' => $value, 'text' => $value,
'value' => $key, 'value' => $key,
]; ], array_keys($entries), $entries));
}, array_keys($entries), $entries));
} }
/**
* @return Response
*/
#[Route(path: '/label_profiles', name: 'select_label_profiles')] #[Route(path: '/label_profiles', name: 'select_label_profiles')]
public function labelProfiles(EntityManagerInterface $entityManager): Response public function labelProfiles(EntityManagerInterface $entityManager): Response
{ {
@ -121,9 +109,6 @@ class SelectAPIController extends AbstractController
return $this->json($nodes); return $this->json($nodes);
} }
/**
* @return Response
*/
#[Route(path: '/label_profiles_lot', name: 'select_label_profiles_lot')] #[Route(path: '/label_profiles_lot', name: 'select_label_profiles_lot')]
public function labelProfilesLot(EntityManagerInterface $entityManager): Response public function labelProfilesLot(EntityManagerInterface $entityManager): Response
{ {

View file

@ -77,7 +77,7 @@ class ToolsController extends AbstractController
'php_version' => PHP_VERSION, 'php_version' => PHP_VERSION,
'php_uname' => php_uname('a'), 'php_uname' => php_uname('a'),
'php_sapi' => PHP_SAPI, 'php_sapi' => PHP_SAPI,
'php_extensions' => array_merge(get_loaded_extensions()), 'php_extensions' => [...get_loaded_extensions()],
'php_opcache_enabled' => ini_get('opcache.enable'), 'php_opcache_enabled' => ini_get('opcache.enable'),
'php_upload_max_filesize' => ini_get('upload_max_filesize'), 'php_upload_max_filesize' => ini_get('upload_max_filesize'),
'php_post_max_size' => ini_get('post_max_size'), 'php_post_max_size' => ini_get('post_max_size'),
@ -91,32 +91,22 @@ class ToolsController extends AbstractController
]); ]);
} }
/**
* @return Response
*/
#[Route(path: '/builtin_footprints', name: 'tools_builtin_footprints_viewer')] #[Route(path: '/builtin_footprints', name: 'tools_builtin_footprints_viewer')]
public function builtInFootprintsViewer(BuiltinAttachmentsFinder $builtinAttachmentsFinder, AttachmentURLGenerator $urlGenerator): Response public function builtInFootprintsViewer(BuiltinAttachmentsFinder $builtinAttachmentsFinder, AttachmentURLGenerator $urlGenerator): Response
{ {
$this->denyAccessUnlessGranted('@tools.builtin_footprints_viewer'); $this->denyAccessUnlessGranted('@tools.builtin_footprints_viewer');
$grouped_footprints = $builtinAttachmentsFinder->getListOfFootprintsGroupedByFolder(); $grouped_footprints = $builtinAttachmentsFinder->getListOfFootprintsGroupedByFolder();
$grouped_footprints = array_map(function($group) use ($urlGenerator) { $grouped_footprints = array_map(fn($group) => array_map(fn($placeholder_filepath) => [
return array_map(function($placeholder_filepath) use ($urlGenerator) { 'filename' => basename((string) $placeholder_filepath),
return [
'filename' => basename($placeholder_filepath),
'assets_path' => $urlGenerator->placeholderPathToAssetPath($placeholder_filepath), 'assets_path' => $urlGenerator->placeholderPathToAssetPath($placeholder_filepath),
]; ], $group), $grouped_footprints);
}, $group);
}, $grouped_footprints);
return $this->render('tools/builtin_footprints_viewer/builtin_footprints_viewer.html.twig', [ return $this->render('tools/builtin_footprints_viewer/builtin_footprints_viewer.html.twig', [
'grouped_footprints' => $grouped_footprints, 'grouped_footprints' => $grouped_footprints,
]); ]);
} }
/**
* @return Response
*/
#[Route(path: '/ic_logos', name: 'tools_ic_logos')] #[Route(path: '/ic_logos', name: 'tools_ic_logos')]
public function icLogos(): Response public function icLogos(): Response
{ {

View file

@ -40,11 +40,8 @@ use Symfony\Component\Routing\Annotation\Route;
#[Route(path: '/tree')] #[Route(path: '/tree')]
class TreeController extends AbstractController class TreeController extends AbstractController
{ {
protected TreeViewGenerator $treeGenerator; public function __construct(protected TreeViewGenerator $treeGenerator)
public function __construct(TreeViewGenerator $treeGenerator)
{ {
$this->treeGenerator = $treeGenerator;
} }
#[Route(path: '/tools', name: 'tree_tools')] #[Route(path: '/tools', name: 'tree_tools')]

View file

@ -55,13 +55,8 @@ use Symfony\Component\Serializer\Serializer;
#[Route(path: '/typeahead')] #[Route(path: '/typeahead')]
class TypeaheadController extends AbstractController class TypeaheadController extends AbstractController
{ {
protected AttachmentURLGenerator $urlGenerator; public function __construct(protected AttachmentURLGenerator $urlGenerator, protected Packages $assets)
protected Packages $assets;
public function __construct(AttachmentURLGenerator $URLGenerator, Packages $assets)
{ {
$this->urlGenerator = $URLGenerator;
$this->assets = $assets;
} }
#[Route(path: '/builtInResources/search', name: 'typeahead_builtInRessources')] #[Route(path: '/builtInResources/search', name: 'typeahead_builtInRessources')]
@ -93,45 +88,26 @@ class TypeaheadController extends AbstractController
/** /**
* This function map the parameter type to the class, so we can access its repository * This function map the parameter type to the class, so we can access its repository
* @param string $type
* @return class-string * @return class-string
*/ */
private function typeToParameterClass(string $type): string private function typeToParameterClass(string $type): string
{ {
switch ($type) { return match ($type) {
case 'category': 'category' => CategoryParameter::class,
return CategoryParameter::class; 'part' => PartParameter::class,
case 'part': 'device' => ProjectParameter::class,
return PartParameter::class; 'footprint' => FootprintParameter::class,
case 'device': 'manufacturer' => ManufacturerParameter::class,
return ProjectParameter::class; 'storelocation' => StorelocationParameter::class,
case 'footprint': 'supplier' => SupplierParameter::class,
return FootprintParameter::class; 'attachment_type' => AttachmentTypeParameter::class,
case 'manufacturer': 'group' => GroupParameter::class,
return ManufacturerParameter::class; 'measurement_unit' => MeasurementUnitParameter::class,
case 'storelocation': 'currency' => Currency::class,
return StorelocationParameter::class; default => throw new \InvalidArgumentException('Invalid parameter type: '.$type),
case 'supplier': };
return SupplierParameter::class;
case 'attachment_type':
return AttachmentTypeParameter::class;
case 'group':
return GroupParameter::class;
case 'measurement_unit':
return MeasurementUnitParameter::class;
case 'currency':
return Currency::class;
default:
throw new \InvalidArgumentException('Invalid parameter type: '.$type);
}
} }
/**
* @param string $query
* @param EntityManagerInterface $entityManager
* @return JsonResponse
*/
#[Route(path: '/parts/search/{query}', name: 'typeahead_parts')] #[Route(path: '/parts/search/{query}', name: 'typeahead_parts')]
public function parts(EntityManagerInterface $entityManager, PartPreviewGenerator $previewGenerator, public function parts(EntityManagerInterface $entityManager, PartPreviewGenerator $previewGenerator,
AttachmentURLGenerator $attachmentURLGenerator, string $query = ""): JsonResponse AttachmentURLGenerator $attachmentURLGenerator, string $query = ""): JsonResponse
@ -146,7 +122,7 @@ class TypeaheadController extends AbstractController
foreach ($parts as $part) { foreach ($parts as $part) {
//Determine the picture to show: //Determine the picture to show:
$preview_attachment = $previewGenerator->getTablePreviewAttachment($part); $preview_attachment = $previewGenerator->getTablePreviewAttachment($part);
if($preview_attachment !== null) { if($preview_attachment instanceof \App\Entity\Attachments\Attachment) {
$preview_url = $attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_sm'); $preview_url = $attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_sm');
} else { } else {
$preview_url = ''; $preview_url = '';
@ -156,8 +132,8 @@ class TypeaheadController extends AbstractController
$data[] = [ $data[] = [
'id' => $part->getID(), 'id' => $part->getID(),
'name' => $part->getName(), 'name' => $part->getName(),
'category' => $part->getCategory() ? $part->getCategory()->getName() : 'Unknown', 'category' => $part->getCategory() instanceof \App\Entity\Parts\Category ? $part->getCategory()->getName() : 'Unknown',
'footprint' => $part->getFootprint() ? $part->getFootprint()->getName() : '', 'footprint' => $part->getFootprint() instanceof \App\Entity\Parts\Footprint ? $part->getFootprint()->getName() : '',
'description' => mb_strimwidth($part->getDescription(), 0, 127, '...'), 'description' => mb_strimwidth($part->getDescription(), 0, 127, '...'),
'image' => $preview_url, 'image' => $preview_url,
]; ];
@ -166,10 +142,6 @@ class TypeaheadController extends AbstractController
return new JsonResponse($data); return new JsonResponse($data);
} }
/**
* @param string $query
* @return JsonResponse
*/
#[Route(path: '/parameters/{type}/search/{query}', name: 'typeahead_parameters', requirements: ['type' => '.+'])] #[Route(path: '/parameters/{type}/search/{query}', name: 'typeahead_parameters', requirements: ['type' => '.+'])]
public function parameters(string $type, EntityManagerInterface $entityManager, string $query = ""): JsonResponse public function parameters(string $type, EntityManagerInterface $entityManager, string $query = ""): JsonResponse
{ {

View file

@ -180,7 +180,7 @@ class UserController extends AdminPages\BaseAdminController
public function userInfo(?User $user, Packages $packages, Request $request, DataTableFactory $dataTableFactory): Response public function userInfo(?User $user, Packages $packages, Request $request, DataTableFactory $dataTableFactory): Response
{ {
//If no user id was passed, then we show info about the current user //If no user id was passed, then we show info about the current user
if (null === $user) { if (!$user instanceof \App\Entity\UserSystem\User) {
$tmp = $this->getUser(); $tmp = $this->getUser();
if (!$tmp instanceof User) { if (!$tmp instanceof User) {
throw new InvalidArgumentException('Userinfo only works for database users!'); throw new InvalidArgumentException('Userinfo only works for database users!');

View file

@ -54,16 +54,13 @@ use Symfony\Component\Validator\Constraints\Length;
#[Route(path: '/user')] #[Route(path: '/user')]
class UserSettingsController extends AbstractController class UserSettingsController extends AbstractController
{ {
protected bool $demo_mode;
/** /**
* @var EventDispatcher|EventDispatcherInterface * @var EventDispatcher|EventDispatcherInterface
*/ */
protected $eventDispatcher; protected $eventDispatcher;
public function __construct(bool $demo_mode, EventDispatcherInterface $eventDispatcher) public function __construct(protected bool $demo_mode, EventDispatcherInterface $eventDispatcher)
{ {
$this->demo_mode = $demo_mode;
$this->eventDispatcher = $eventDispatcher; $this->eventDispatcher = $eventDispatcher;
} }
@ -121,24 +118,21 @@ class UserSettingsController extends AbstractController
$key_repo = $entityManager->getRepository(U2FKey::class); $key_repo = $entityManager->getRepository(U2FKey::class);
/** @var U2FKey|null $u2f */ /** @var U2FKey|null $u2f */
$u2f = $key_repo->find($key_id); $u2f = $key_repo->find($key_id);
if (null === $u2f) { if (!$u2f instanceof \App\Entity\UserSystem\U2FKey) {
$this->addFlash('danger', 'tfa_u2f.u2f_delete.not_existing'); $this->addFlash('danger', 'tfa_u2f.u2f_delete.not_existing');
return $this->redirectToRoute('user_settings'); return $this->redirectToRoute('user_settings');
} }
//User can only delete its own U2F keys //User can only delete its own U2F keys
if ($u2f->getUser() !== $user) { if ($u2f->getUser() !== $user) {
$this->addFlash('danger', 'tfa_u2f.u2f_delete.access_denied'); $this->addFlash('danger', 'tfa_u2f.u2f_delete.access_denied');
return $this->redirectToRoute('user_settings'); return $this->redirectToRoute('user_settings');
} }
$backupCodeManager->disableBackupCodesIfUnused($user); $backupCodeManager->disableBackupCodesIfUnused($user);
$entityManager->remove($u2f); $entityManager->remove($u2f);
$entityManager->flush(); $entityManager->flush();
$this->addFlash('success', 'tfa.u2f.u2f_delete.success'); $this->addFlash('success', 'tfa.u2f.u2f_delete.success');
$security_event = new SecurityEvent($user); $security_event = new SecurityEvent($user);
$this->eventDispatcher->dispatch($security_event, SecurityEvents::U2F_REMOVED); $this->eventDispatcher->dispatch($security_event, SecurityEvents::U2F_REMOVED);
} elseif ($request->request->has('webauthn_key_id')) { } elseif ($request->request->has('webauthn_key_id')) {
@ -146,24 +140,21 @@ class UserSettingsController extends AbstractController
$key_repo = $entityManager->getRepository(WebauthnKey::class); $key_repo = $entityManager->getRepository(WebauthnKey::class);
/** @var WebauthnKey|null $key */ /** @var WebauthnKey|null $key */
$key = $key_repo->find($key_id); $key = $key_repo->find($key_id);
if (null === $key) { if (!$key instanceof \App\Entity\UserSystem\WebauthnKey) {
$this->addFlash('error', 'tfa_u2f.u2f_delete.not_existing'); $this->addFlash('error', 'tfa_u2f.u2f_delete.not_existing');
return $this->redirectToRoute('user_settings'); return $this->redirectToRoute('user_settings');
} }
//User can only delete its own U2F keys //User can only delete its own U2F keys
if ($key->getUser() !== $user) { if ($key->getUser() !== $user) {
$this->addFlash('error', 'tfa_u2f.u2f_delete.access_denied'); $this->addFlash('error', 'tfa_u2f.u2f_delete.access_denied');
return $this->redirectToRoute('user_settings'); return $this->redirectToRoute('user_settings');
} }
$backupCodeManager->disableBackupCodesIfUnused($user); $backupCodeManager->disableBackupCodesIfUnused($user);
$entityManager->remove($key); $entityManager->remove($key);
$entityManager->flush(); $entityManager->flush();
$this->addFlash('success', 'tfa.u2f.u2f_delete.success'); $this->addFlash('success', 'tfa.u2f.u2f_delete.success');
$security_event = new SecurityEvent($user); $security_event = new SecurityEvent($user);
$this->eventDispatcher->dispatch($security_event, SecurityEvents::U2F_REMOVED); $this->eventDispatcher->dispatch($security_event, SecurityEvents::U2F_REMOVED);
} }
@ -174,11 +165,8 @@ class UserSettingsController extends AbstractController
return $this->redirectToRoute('user_settings'); return $this->redirectToRoute('user_settings');
} }
/**
* @return RuntimeException|RedirectResponse
*/
#[Route(path: '/invalidate_trustedDevices', name: 'tfa_trustedDevices_invalidate', methods: ['DELETE'])] #[Route(path: '/invalidate_trustedDevices', name: 'tfa_trustedDevices_invalidate', methods: ['DELETE'])]
public function resetTrustedDevices(Request $request, EntityManagerInterface $entityManager) public function resetTrustedDevices(Request $request, EntityManagerInterface $entityManager): \RuntimeException|\Symfony\Component\HttpFoundation\RedirectResponse
{ {
if ($this->demo_mode) { if ($this->demo_mode) {
throw new RuntimeException('You can not do 2FA things in demo mode'); throw new RuntimeException('You can not do 2FA things in demo mode');
@ -215,7 +203,7 @@ class UserSettingsController extends AbstractController
* @return RedirectResponse|Response * @return RedirectResponse|Response
*/ */
#[Route(path: '/settings', name: 'user_settings')] #[Route(path: '/settings', name: 'user_settings')]
public function userSettings(Request $request, EntityManagerInterface $em, UserPasswordHasherInterface $passwordEncoder, GoogleAuthenticator $googleAuthenticator, BackupCodeManager $backupCodeManager, FormFactoryInterface $formFactory, UserAvatarHelper $avatarHelper) public function userSettings(Request $request, EntityManagerInterface $em, UserPasswordHasherInterface $passwordEncoder, GoogleAuthenticator $googleAuthenticator, BackupCodeManager $backupCodeManager, FormFactoryInterface $formFactory, UserAvatarHelper $avatarHelper): \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
{ {
/** @var User $user */ /** @var User $user */
$user = $this->getUser(); $user = $this->getUser();
@ -254,14 +242,12 @@ class UserSettingsController extends AbstractController
} }
/** @var Form $form We need a form implementation for the next calls */ /** @var Form $form We need a form implementation for the next calls */
if ($form->getClickedButton() && 'remove_avatar' === $form->getClickedButton()->getName()) {
//Remove the avatar attachment from the user if requested //Remove the avatar attachment from the user if requested
if ($user->getMasterPictureAttachment() !== null) { if ($form->getClickedButton() && 'remove_avatar' === $form->getClickedButton()->getName() && $user->getMasterPictureAttachment() instanceof \App\Entity\Attachments\Attachment) {
$em->remove($user->getMasterPictureAttachment()); $em->remove($user->getMasterPictureAttachment());
$user->setMasterPictureAttachment(null); $user->setMasterPictureAttachment(null);
$page_need_reload = true; $page_need_reload = true;
} }
}
$em->flush(); $em->flush();
$this->addFlash('success', 'user.settings.saved_flash'); $this->addFlash('success', 'user.settings.saved_flash');

View file

@ -33,11 +33,8 @@ use function Symfony\Component\Translation\t;
class WebauthnKeyRegistrationController extends AbstractController class WebauthnKeyRegistrationController extends AbstractController
{ {
private bool $demo_mode; public function __construct(private readonly bool $demo_mode)
public function __construct(bool $demo_mode)
{ {
$this->demo_mode = $demo_mode;
} }
#[Route(path: '/webauthn/register', name: 'webauthn_register')] #[Route(path: '/webauthn/register', name: 'webauthn_register')]
@ -73,7 +70,7 @@ class WebauthnKeyRegistrationController extends AbstractController
//Check the response //Check the response
try { try {
$new_key = $registrationHelper->checkRegistrationResponse($webauthnResponse); $new_key = $registrationHelper->checkRegistrationResponse($webauthnResponse);
} catch (\Exception $exception) { } catch (\Exception) {
$this->addFlash('error', t('tfa_u2f.add_key.registration_error')); $this->addFlash('error', t('tfa_u2f.add_key.registration_error'));
return $this->redirectToRoute('webauthn_register'); return $this->redirectToRoute('webauthn_register');
} }

View file

@ -38,11 +38,8 @@ use InvalidArgumentException;
class DataStructureFixtures extends Fixture class DataStructureFixtures extends Fixture
{ {
protected EntityManagerInterface $em; public function __construct(protected EntityManagerInterface $em)
public function __construct(EntityManagerInterface $entityManager)
{ {
$this->em = $entityManager;
} }
/** /**

View file

@ -30,18 +30,12 @@ use Doctrine\Persistence\ObjectManager;
class GroupFixtures extends Fixture class GroupFixtures extends Fixture
{ {
public const ADMINS = 'group-admin'; final public const ADMINS = 'group-admin';
public const USERS = 'group-users'; final public const USERS = 'group-users';
public const READONLY = 'group-readonly'; final public const READONLY = 'group-readonly';
public function __construct(private readonly PermissionPresetsHelper $permission_presets, private readonly PermissionManager $permissionManager)
private PermissionPresetsHelper $permission_presets;
private PermissionManager $permissionManager;
public function __construct(PermissionPresetsHelper $permissionPresetsHelper, PermissionManager $permissionManager)
{ {
$this->permission_presets = $permissionPresetsHelper;
$this->permissionManager = $permissionManager;
} }
public function load(ObjectManager $manager): void public function load(ObjectManager $manager): void

View file

@ -49,11 +49,8 @@ use Doctrine\Persistence\ObjectManager;
class LabelProfileFixtures extends Fixture class LabelProfileFixtures extends Fixture
{ {
protected EntityManagerInterface $em; public function __construct(protected EntityManagerInterface $em)
public function __construct(EntityManagerInterface $entityManager)
{ {
$this->em = $entityManager;
} }
public function load(ObjectManager $manager): void public function load(ObjectManager $manager): void

View file

@ -60,11 +60,8 @@ use Doctrine\Persistence\ObjectManager;
class PartFixtures extends Fixture class PartFixtures extends Fixture
{ {
protected EntityManagerInterface $em; public function __construct(protected EntityManagerInterface $em)
public function __construct(EntityManagerInterface $entityManager)
{ {
$this->em = $entityManager;
} }
public function load(ObjectManager $manager): void public function load(ObjectManager $manager): void

View file

@ -30,13 +30,8 @@ use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
class UserFixtures extends Fixture class UserFixtures extends Fixture
{ {
protected UserPasswordHasherInterface $encoder; public function __construct(protected UserPasswordHasherInterface $encoder, protected EntityManagerInterface $em)
protected EntityManagerInterface $em;
public function __construct(UserPasswordHasherInterface $encoder, EntityManagerInterface $entityManager)
{ {
$this->em = $entityManager;
$this->encoder = $encoder;
} }
public function load(ObjectManager $manager): void public function load(ObjectManager $manager): void

View file

@ -42,27 +42,14 @@ use Symfony\Contracts\Translation\TranslatorInterface;
final class AttachmentDataTable implements DataTableTypeInterface final class AttachmentDataTable implements DataTableTypeInterface
{ {
private TranslatorInterface $translator; public function __construct(private readonly TranslatorInterface $translator, private readonly EntityURLGenerator $entityURLGenerator, private readonly AttachmentManager $attachmentHelper, private readonly AttachmentURLGenerator $attachmentURLGenerator, private readonly ElementTypeNameGenerator $elementTypeNameGenerator)
private EntityURLGenerator $entityURLGenerator;
private AttachmentManager $attachmentHelper;
private ElementTypeNameGenerator $elementTypeNameGenerator;
private AttachmentURLGenerator $attachmentURLGenerator;
public function __construct(TranslatorInterface $translator, EntityURLGenerator $entityURLGenerator,
AttachmentManager $attachmentHelper, AttachmentURLGenerator $attachmentURLGenerator,
ElementTypeNameGenerator $elementTypeNameGenerator)
{ {
$this->translator = $translator;
$this->entityURLGenerator = $entityURLGenerator;
$this->attachmentHelper = $attachmentHelper;
$this->elementTypeNameGenerator = $elementTypeNameGenerator;
$this->attachmentURLGenerator = $attachmentURLGenerator;
} }
public function configure(DataTable $dataTable, array $options): void public function configure(DataTable $dataTable, array $options): void
{ {
$dataTable->add('dont_matter', RowClassColumn::class, [ $dataTable->add('dont_matter', RowClassColumn::class, [
'render' => function ($value, Attachment $context) { 'render' => function ($value, Attachment $context): string {
//Mark attachments with missing files yellow //Mark attachments with missing files yellow
if(!$this->attachmentHelper->isFileExisting($context)){ if(!$this->attachmentHelper->isFileExisting($context)){
return 'table-warning'; return 'table-warning';
@ -75,7 +62,7 @@ final class AttachmentDataTable implements DataTableTypeInterface
$dataTable->add('picture', TextColumn::class, [ $dataTable->add('picture', TextColumn::class, [
'label' => '', 'label' => '',
'className' => 'no-colvis', 'className' => 'no-colvis',
'render' => function ($value, Attachment $context) { 'render' => function ($value, Attachment $context): string {
if ($context->isPicture() if ($context->isPicture()
&& !$context->isExternal() && !$context->isExternal()
&& $this->attachmentHelper->isFileExisting($context)) { && $this->attachmentHelper->isFileExisting($context)) {
@ -125,25 +112,21 @@ final class AttachmentDataTable implements DataTableTypeInterface
$dataTable->add('attachment_type', TextColumn::class, [ $dataTable->add('attachment_type', TextColumn::class, [
'label' => 'attachment.table.type', 'label' => 'attachment.table.type',
'field' => 'attachment_type.name', 'field' => 'attachment_type.name',
'render' => function ($value, Attachment $context) { 'render' => fn($value, Attachment $context): string => sprintf(
return sprintf(
'<a href="%s">%s</a>', '<a href="%s">%s</a>',
$this->entityURLGenerator->editURL($context->getAttachmentType()), $this->entityURLGenerator->editURL($context->getAttachmentType()),
htmlspecialchars($value) htmlspecialchars((string) $value)
); ),
},
]); ]);
$dataTable->add('element', TextColumn::class, [ $dataTable->add('element', TextColumn::class, [
'label' => 'attachment.table.element', 'label' => 'attachment.table.element',
//'propertyPath' => 'element.name', //'propertyPath' => 'element.name',
'render' => function ($value, Attachment $context) { 'render' => fn($value, Attachment $context): string => sprintf(
return sprintf(
'<a href="%s">%s</a>', '<a href="%s">%s</a>',
$this->entityURLGenerator->infoURL($context->getElement()), $this->entityURLGenerator->infoURL($context->getElement()),
$this->elementTypeNameGenerator->getTypeNameCombination($context->getElement(), true) $this->elementTypeNameGenerator->getTypeNameCombination($context->getElement(), true)
); ),
},
]); ]);
$dataTable->add('filename', TextColumn::class, [ $dataTable->add('filename', TextColumn::class, [

View file

@ -31,13 +31,8 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
class EntityColumn extends AbstractColumn class EntityColumn extends AbstractColumn
{ {
protected EntityURLGenerator $urlGenerator; public function __construct(protected EntityURLGenerator $urlGenerator, protected PropertyAccessorInterface $accessor)
protected PropertyAccessorInterface $accessor;
public function __construct(EntityURLGenerator $URLGenerator, PropertyAccessorInterface $accessor)
{ {
$this->urlGenerator = $URLGenerator;
$this->accessor = $accessor;
} }
/** /**
@ -58,12 +53,9 @@ class EntityColumn extends AbstractColumn
$resolver->setRequired('property'); $resolver->setRequired('property');
$resolver->setDefault('field', static function (Options $option) { $resolver->setDefault('field', static fn(Options $option): string => $option['property'].'.name');
return $option['property'].'.name';
});
$resolver->setDefault('render', function (Options $options) { $resolver->setDefault('render', fn(Options $options) => function ($value, $context) use ($options): string {
return function ($value, $context) use ($options) {
if ($this->accessor->isReadable($context, $options['property'])) { if ($this->accessor->isReadable($context, $options['property'])) {
$entity = $this->accessor->getValue($context, $options['property']); $entity = $this->accessor->getValue($context, $options['property']);
} else { } else {
@ -72,7 +64,7 @@ class EntityColumn extends AbstractColumn
/** @var AbstractNamedDBElement|null $entity */ /** @var AbstractNamedDBElement|null $entity */
if (null !== $entity) { if ($entity instanceof \App\Entity\Base\AbstractNamedDBElement) {
if (null !== $entity->getID()) { if (null !== $entity->getID()) {
return sprintf( return sprintf(
'<a href="%s">%s</a>', '<a href="%s">%s</a>',
@ -85,7 +77,6 @@ class EntityColumn extends AbstractColumn
} }
return ''; return '';
};
}); });
return $this; return $this;

View file

@ -38,7 +38,6 @@ class LocaleDateTimeColumn extends AbstractColumn
{ {
/** /**
* @param $value * @param $value
* @return string
* @throws Exception * @throws Exception
*/ */
public function normalize($value): string public function normalize($value): string

View file

@ -28,13 +28,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class LogEntryExtraColumn extends AbstractColumn class LogEntryExtraColumn extends AbstractColumn
{ {
protected TranslatorInterface $translator; public function __construct(protected TranslatorInterface $translator, protected LogEntryExtraFormatter $formatter)
protected LogEntryExtraFormatter $formatter;
public function __construct(TranslatorInterface $translator, LogEntryExtraFormatter $formatter)
{ {
$this->translator = $translator;
$this->formatter = $formatter;
} }
/** /**

View file

@ -44,11 +44,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class LogEntryTargetColumn extends AbstractColumn class LogEntryTargetColumn extends AbstractColumn
{ {
private LogTargetHelper $logTargetHelper; public function __construct(private readonly LogTargetHelper $logTargetHelper)
public function __construct(LogTargetHelper $logTargetHelper)
{ {
$this->logTargetHelper = $logTargetHelper;
} }
/** /**

View file

@ -27,11 +27,8 @@ use Omines\DataTablesBundle\Column\AbstractColumn;
class MarkdownColumn extends AbstractColumn class MarkdownColumn extends AbstractColumn
{ {
protected MarkdownParser $markdown; public function __construct(protected MarkdownParser $markdown)
public function __construct(MarkdownParser $markdown)
{ {
$this->markdown = $markdown;
} }
/** /**

View file

@ -33,15 +33,8 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class PartAttachmentsColumn extends AbstractColumn class PartAttachmentsColumn extends AbstractColumn
{ {
protected FAIconGenerator $FAIconGenerator; public function __construct(protected FAIconGenerator $FAIconGenerator, protected EntityURLGenerator $urlGenerator, protected AttachmentManager $attachmentManager)
protected EntityURLGenerator $urlGenerator;
protected AttachmentManager $attachmentManager;
public function __construct(FAIconGenerator $FAIconGenerator, EntityURLGenerator $urlGenerator, AttachmentManager $attachmentManager)
{ {
$this->FAIconGenerator = $FAIconGenerator;
$this->urlGenerator = $urlGenerator;
$this->attachmentManager = $attachmentManager;
} }
/** /**
@ -61,9 +54,7 @@ class PartAttachmentsColumn extends AbstractColumn
throw new RuntimeException('$context must be a Part object!'); throw new RuntimeException('$context must be a Part object!');
} }
$tmp = ''; $tmp = '';
$attachments = $context->getAttachments()->filter(function (Attachment $attachment) { $attachments = $context->getAttachments()->filter(fn(Attachment $attachment) => $attachment->getShowInTable() && $this->attachmentManager->isFileExisting($attachment));
return $attachment->getShowInTable() && $this->attachmentManager->isFileExisting($attachment);
});
$count = 5; $count = 5;
foreach ($attachments as $attachment) { foreach ($attachments as $attachment) {

View file

@ -25,11 +25,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class PrettyBoolColumn extends AbstractColumn class PrettyBoolColumn extends AbstractColumn
{ {
protected TranslatorInterface $translator; public function __construct(protected TranslatorInterface $translator)
public function __construct(TranslatorInterface $translator)
{ {
$this->translator = $translator;
} }
public function normalize($value): ?bool public function normalize($value): ?bool

View file

@ -51,13 +51,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class RevertLogColumn extends AbstractColumn class RevertLogColumn extends AbstractColumn
{ {
protected TranslatorInterface $translator; public function __construct(protected TranslatorInterface $translator, protected \Symfony\Bundle\SecurityBundle\Security $security)
protected \Symfony\Bundle\SecurityBundle\Security $security;
public function __construct(TranslatorInterface $translator, \Symfony\Bundle\SecurityBundle\Security $security)
{ {
$this->translator = $translator;
$this->security = $security;
} }
/** /**
@ -105,8 +100,6 @@ class RevertLogColumn extends AbstractColumn
$this->translator->trans('log.undo.revert') $this->translator->trans('log.undo.revert')
); );
$tmp .= '</div>'; return $tmp . '</div>';
return $tmp;
} }
} }

View file

@ -26,11 +26,8 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class SIUnitNumberColumn extends AbstractColumn class SIUnitNumberColumn extends AbstractColumn
{ {
protected SIFormatter $formatter; public function __construct(protected SIFormatter $formatter)
public function __construct(SIFormatter $formatter)
{ {
$this->formatter = $formatter;
} }
public function configureOptions(OptionsResolver $resolver): self public function configureOptions(OptionsResolver $resolver): self

View file

@ -27,11 +27,8 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class TagsColumn extends AbstractColumn class TagsColumn extends AbstractColumn
{ {
protected UrlGeneratorInterface $urlGenerator; public function __construct(protected UrlGeneratorInterface $urlGenerator)
public function __construct(UrlGeneratorInterface $urlGenerator)
{ {
$this->urlGenerator = $urlGenerator;
} }
/** /**
@ -46,7 +43,7 @@ class TagsColumn extends AbstractColumn
return []; return [];
} }
return explode(',', $value); return explode(',', (string) $value);
} }
public function render($tags, $context): string public function render($tags, $context): string
@ -61,7 +58,7 @@ class TagsColumn extends AbstractColumn
$html .= sprintf( $html .= sprintf(
'<a href="%s" class="badge bg-primary badge-table">%s</a>', '<a href="%s" class="badge bg-primary badge-table">%s</a>',
$this->urlGenerator->generate('part_list_tags', ['tag' => $tag]), $this->urlGenerator->generate('part_list_tags', ['tag' => $tag]),
htmlspecialchars($tag) htmlspecialchars((string) $tag)
); );
} }

View file

@ -54,16 +54,12 @@ class ErrorDataTable implements DataTableTypeInterface
$dataTable $dataTable
->add('dont_matter_we_only_set_color', RowClassColumn::class, [ ->add('dont_matter_we_only_set_color', RowClassColumn::class, [
'render' => function ($value, $context) { 'render' => fn($value, $context): string => 'table-warning',
return 'table-warning';
},
]) ])
->add('error', TextColumn::class, [ ->add('error', TextColumn::class, [
'label' => 'error_table.error', 'label' => 'error_table.error',
'render' => function ($value, $context) { 'render' => fn($value, $context): string => '<i class="fa-solid fa-triangle-exclamation fa-fw"></i> ' . $value,
return '<i class="fa-solid fa-triangle-exclamation fa-fw"></i> ' . $value;
},
]) ])
; ;

View file

@ -60,59 +60,38 @@ class AttachmentFilter implements FilterInterface
$this->applyAllChildFilters($queryBuilder); $this->applyAllChildFilters($queryBuilder);
} }
/**
* @return NumberConstraint
*/
public function getDbId(): NumberConstraint public function getDbId(): NumberConstraint
{ {
return $this->dbId; return $this->dbId;
} }
/**
* @return TextConstraint
*/
public function getName(): TextConstraint public function getName(): TextConstraint
{ {
return $this->name; return $this->name;
} }
/**
* @return DateTimeConstraint
*/
public function getLastModified(): DateTimeConstraint public function getLastModified(): DateTimeConstraint
{ {
return $this->lastModified; return $this->lastModified;
} }
/**
* @return DateTimeConstraint
*/
public function getAddedDate(): DateTimeConstraint public function getAddedDate(): DateTimeConstraint
{ {
return $this->addedDate; return $this->addedDate;
} }
/**
* @return BooleanConstraint
*/
public function getShowInTable(): BooleanConstraint public function getShowInTable(): BooleanConstraint
{ {
return $this->showInTable; return $this->showInTable;
} }
/**
* @return EntityConstraint
*/
public function getAttachmentType(): EntityConstraint public function getAttachmentType(): EntityConstraint
{ {
return $this->attachmentType; return $this->attachmentType;
} }
/**
* @return InstanceOfConstraint
*/
public function getTargetType(): InstanceOfConstraint public function getTargetType(): InstanceOfConstraint
{ {
return $this->targetType; return $this->targetType;

View file

@ -60,8 +60,6 @@ trait CompoundFilterTrait
/** /**
* Applies all children filters that are declared as property of this filter using reflection. * Applies all children filters that are declared as property of this filter using reflection.
* @param QueryBuilder $queryBuilder
* @return void
*/ */
protected function applyAllChildFilters(QueryBuilder $queryBuilder): void protected function applyAllChildFilters(QueryBuilder $queryBuilder): void
{ {

View file

@ -26,11 +26,6 @@ abstract class AbstractConstraint implements FilterInterface
{ {
use FilterTrait; use FilterTrait;
/**
* @var string The property where this BooleanConstraint should apply to
*/
protected string $property;
/** /**
* @var string * @var string
*/ */
@ -43,9 +38,11 @@ abstract class AbstractConstraint implements FilterInterface
*/ */
abstract public function isEnabled(): bool; abstract public function isEnabled(): bool;
public function __construct(string $property, string $identifier = null) public function __construct(/**
* @var string The property where this BooleanConstraint should apply to
*/
protected string $property, string $identifier = null)
{ {
$this->property = $property;
$this->identifier = $identifier ?? $this->generateParameterIdentifier($property); $this->identifier = $identifier ?? $this->generateParameterIdentifier($property);
} }
} }

View file

@ -24,19 +24,14 @@ use Doctrine\ORM\QueryBuilder;
class BooleanConstraint extends AbstractConstraint class BooleanConstraint extends AbstractConstraint
{ {
/** @var bool|null The value of our constraint */ public function __construct(string $property, string $identifier = null, /** @var bool|null The value of our constraint */
protected ?bool $value; protected ?bool $value = null)
public function __construct(string $property, string $identifier = null, ?bool $default_value = null)
{ {
parent::__construct($property, $identifier); parent::__construct($property, $identifier);
$this->value = $default_value;
} }
/** /**
* Gets the value of this constraint. Null means "don't filter", true means "filter for true", false means "filter for false". * Gets the value of this constraint. Null means "don't filter", true means "filter for true", false means "filter for false".
* @return bool|null
*/ */
public function getValue(): ?bool public function getValue(): ?bool
{ {
@ -45,7 +40,6 @@ class BooleanConstraint extends AbstractConstraint
/** /**
* Sets the value of this constraint. Null means "don't filter", true means "filter for true", false means "filter for false". * Sets the value of this constraint. Null means "don't filter", true means "filter for true", false means "filter for false".
* @param bool|null $value
*/ */
public function setValue(?bool $value): void public function setValue(?bool $value): void
{ {

View file

@ -24,7 +24,7 @@ use Doctrine\ORM\QueryBuilder;
class ChoiceConstraint extends AbstractConstraint class ChoiceConstraint extends AbstractConstraint
{ {
public const ALLOWED_OPERATOR_VALUES = ['ANY', 'NONE']; final public const ALLOWED_OPERATOR_VALUES = ['ANY', 'NONE'];
/** /**
* @var string[]|int[] The values to compare to * @var string[]|int[] The values to compare to
@ -46,7 +46,6 @@ class ChoiceConstraint extends AbstractConstraint
/** /**
* @param string[]|int[] $value * @param string[]|int[] $value
* @return ChoiceConstraint
*/ */
public function setValue(array $value): ChoiceConstraint public function setValue(array $value): ChoiceConstraint
{ {
@ -54,18 +53,11 @@ class ChoiceConstraint extends AbstractConstraint
return $this; return $this;
} }
/**
* @return string
*/
public function getOperator(): string public function getOperator(): string
{ {
return $this->operator; return $this->operator;
} }
/**
* @param string $operator
* @return ChoiceConstraint
*/
public function setOperator(string $operator): ChoiceConstraint public function setOperator(string $operator): ChoiceConstraint
{ {
$this->operator = $operator; $this->operator = $operator;

View file

@ -33,26 +33,6 @@ class EntityConstraint extends AbstractConstraint
private const ALLOWED_OPERATOR_VALUES_BASE = ['=', '!=']; private const ALLOWED_OPERATOR_VALUES_BASE = ['=', '!='];
private const ALLOWED_OPERATOR_VALUES_STRUCTURAL = ['INCLUDING_CHILDREN', 'EXCLUDING_CHILDREN']; private const ALLOWED_OPERATOR_VALUES_STRUCTURAL = ['INCLUDING_CHILDREN', 'EXCLUDING_CHILDREN'];
/**
* @var NodesListBuilder
*/
protected ?NodesListBuilder $nodesListBuilder;
/**
* @var class-string<T> The class to use for the comparison
*/
protected string $class;
/**
* @var string|null The operator to use
*/
protected ?string $operator;
/**
* @var T The value to compare to
*/
protected $value;
/** /**
* @param NodesListBuilder|null $nodesListBuilder * @param NodesListBuilder|null $nodesListBuilder
* @param class-string<T> $class * @param class-string<T> $class
@ -61,18 +41,13 @@ class EntityConstraint extends AbstractConstraint
* @param null $value * @param null $value
* @param string $operator * @param string $operator
*/ */
public function __construct(?NodesListBuilder $nodesListBuilder, string $class, string $property, string $identifier = null, $value = null, string $operator = '') public function __construct(protected ?\App\Services\Trees\NodesListBuilder $nodesListBuilder, protected string $class, string $property, string $identifier = null, protected $value = null, protected ?string $operator = '')
{ {
$this->nodesListBuilder = $nodesListBuilder; if (!$nodesListBuilder instanceof \App\Services\Trees\NodesListBuilder && $this->isStructural()) {
$this->class = $class;
if ($nodesListBuilder === null && $this->isStructural()) {
throw new \InvalidArgumentException('NodesListBuilder must be provided for structural entities'); throw new \InvalidArgumentException('NodesListBuilder must be provided for structural entities');
} }
parent::__construct($property, $identifier); parent::__construct($property, $identifier);
$this->value = $value;
$this->operator = $operator;
} }
public function getClass(): string public function getClass(): string
@ -80,17 +55,11 @@ class EntityConstraint extends AbstractConstraint
return $this->class; return $this->class;
} }
/**
* @return string|null
*/
public function getOperator(): ?string public function getOperator(): ?string
{ {
return $this->operator; return $this->operator;
} }
/**
* @param string|null $operator
*/
public function setOperator(?string $operator): self public function setOperator(?string $operator): self
{ {
$this->operator = $operator; $this->operator = $operator;
@ -119,7 +88,6 @@ class EntityConstraint extends AbstractConstraint
/** /**
* Checks whether the constraints apply to a structural type or not * Checks whether the constraints apply to a structural type or not
* @return bool
*/ */
public function isStructural(): bool public function isStructural(): bool
{ {
@ -136,7 +104,7 @@ class EntityConstraint extends AbstractConstraint
$tmp = self::ALLOWED_OPERATOR_VALUES_BASE; $tmp = self::ALLOWED_OPERATOR_VALUES_BASE;
if ($this->isStructural()) { if ($this->isStructural()) {
$tmp = array_merge($tmp, self::ALLOWED_OPERATOR_VALUES_STRUCTURAL); $tmp = [...$tmp, ...self::ALLOWED_OPERATOR_VALUES_STRUCTURAL];
} }
return $tmp; return $tmp;
@ -160,7 +128,7 @@ class EntityConstraint extends AbstractConstraint
} }
//We need to handle null values differently, as they can not be compared with == or != //We need to handle null values differently, as they can not be compared with == or !=
if ($this->value === null) { if (!$this->value instanceof \App\Entity\Base\AbstractDBElement) {
if($this->operator === '=' || $this->operator === 'INCLUDING_CHILDREN') { if($this->operator === '=' || $this->operator === 'INCLUDING_CHILDREN') {
$queryBuilder->andWhere(sprintf("%s IS NULL", $this->property)); $queryBuilder->andWhere(sprintf("%s IS NULL", $this->property));
return; return;

View file

@ -35,7 +35,6 @@ trait FilterTrait
/** /**
* Checks if the given input is an aggregateFunction like COUNT(part.partsLot) or so * Checks if the given input is an aggregateFunction like COUNT(part.partsLot) or so
* @return bool
*/ */
protected function isAggregateFunctionString(string $input): bool protected function isAggregateFunctionString(string $input): bool
{ {
@ -44,8 +43,6 @@ trait FilterTrait
/** /**
* Generates a parameter identifier that can be used for the given property. It gives random results, to be unique, so you have to cache it. * Generates a parameter identifier that can be used for the given property. It gives random results, to be unique, so you have to cache it.
* @param string $property
* @return string
*/ */
protected function generateParameterIdentifier(string $property): string protected function generateParameterIdentifier(string $property): string
{ {
@ -57,13 +54,8 @@ trait FilterTrait
/** /**
* Adds a simple constraint in the form of (property OPERATOR value) (e.g. "part.name = :name") to the given query builder. * Adds a simple constraint in the form of (property OPERATOR value) (e.g. "part.name = :name") to the given query builder.
* @param QueryBuilder $queryBuilder
* @param string $property
* @param string $comparison_operator
* @param mixed $value
* @return void
*/ */
protected function addSimpleAndConstraint(QueryBuilder $queryBuilder, string $property, string $parameterIdentifier, string $comparison_operator, $value): void protected function addSimpleAndConstraint(QueryBuilder $queryBuilder, string $property, string $parameterIdentifier, string $comparison_operator, mixed $value): void
{ {
if ($comparison_operator === 'IN' || $comparison_operator === 'NOT IN') { if ($comparison_operator === 'IN' || $comparison_operator === 'NOT IN') {
$expression = sprintf("%s %s (:%s)", $property, $comparison_operator, $parameterIdentifier); $expression = sprintf("%s %s (:%s)", $property, $comparison_operator, $parameterIdentifier);

View file

@ -27,7 +27,7 @@ use Doctrine\ORM\QueryBuilder;
*/ */
class InstanceOfConstraint extends AbstractConstraint class InstanceOfConstraint extends AbstractConstraint
{ {
public const ALLOWED_OPERATOR_VALUES = ['ANY', 'NONE']; final public const ALLOWED_OPERATOR_VALUES = ['ANY', 'NONE'];
/** /**
* @var string[] The values to compare to (fully qualified class names) * @var string[] The values to compare to (fully qualified class names)
@ -57,16 +57,12 @@ class InstanceOfConstraint extends AbstractConstraint
return $this; return $this;
} }
/**
* @return string
*/
public function getOperator(): string public function getOperator(): string
{ {
return $this->operator; return $this->operator;
} }
/** /**
* @param string $operator
* @return $this * @return $this
*/ */
public function setOperator(string $operator): self public function setOperator(string $operator): self

View file

@ -25,61 +25,28 @@ use RuntimeException;
class NumberConstraint extends AbstractConstraint class NumberConstraint extends AbstractConstraint
{ {
public const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN']; final public const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN'];
public function getValue1(): float|int|null|\DateTimeInterface
/**
* The value1 used for comparison (this is the main one used for all mono-value comparisons)
* @var float|null|int|\DateTimeInterface
*/
protected $value1;
/**
* The second value used when operator is RANGE; this is the upper bound of the range
* @var float|null|int|\DateTimeInterface
*/
protected $value2;
/**
* @var string|null The operator to use
*/
protected ?string $operator;
/**
* @return float|int|null|\DateTimeInterface
*/
public function getValue1()
{ {
return $this->value1; return $this->value1;
} }
/** public function setValue1(float|int|\DateTimeInterface|null $value1): void
* @param float|int|\DateTimeInterface|null $value1
*/
public function setValue1($value1): void
{ {
$this->value1 = $value1; $this->value1 = $value1;
} }
/** public function getValue2(): float|int|null
* @return float|int|null
*/
public function getValue2()
{ {
return $this->value2; return $this->value2;
} }
/** public function setValue2(float|int|null $value2): void
* @param float|int|null $value2
*/
public function setValue2($value2): void
{ {
$this->value2 = $value2; $this->value2 = $value2;
} }
/**
* @return string
*/
public function getOperator(): string public function getOperator(): string
{ {
return $this->operator; return $this->operator;
@ -94,12 +61,22 @@ class NumberConstraint extends AbstractConstraint
} }
public function __construct(string $property, string $identifier = null, $value1 = null, string $operator = null, $value2 = null) /**
* @param float|null|int|\DateTimeInterface $value1
* @param float|null|int|\DateTimeInterface $value2
*/
public function __construct(string $property, string $identifier = null, /**
* The value1 used for comparison (this is the main one used for all mono-value comparisons)
*/
protected float|int|\DateTimeInterface|null $value1 = null, /**
* @var string|null The operator to use
*/
protected ?string $operator = null, /**
* The second value used when operator is RANGE; this is the upper bound of the range
*/
protected float|int|\DateTimeInterface|null $value2 = null)
{ {
parent::__construct($property, $identifier); parent::__construct($property, $identifier);
$this->value1 = $value1;
$this->value2 = $value2;
$this->operator = $operator;
} }
public function isEnabled(): bool public function isEnabled(): bool

View file

@ -103,71 +103,44 @@ class ParameterConstraint extends AbstractConstraint
$queryBuilder->andWhere('(' . $subqb->getDQL() . ') > 0'); $queryBuilder->andWhere('(' . $subqb->getDQL() . ') > 0');
} }
/**
* @return string
*/
public function getName(): string public function getName(): string
{ {
return $this->name; return $this->name;
} }
/**
* @param string $name
* @return ParameterConstraint
*/
public function setName(string $name): ParameterConstraint public function setName(string $name): ParameterConstraint
{ {
$this->name = $name; $this->name = $name;
return $this; return $this;
} }
/**
* @return string
*/
public function getSymbol(): string public function getSymbol(): string
{ {
return $this->symbol; return $this->symbol;
} }
/**
* @param string $symbol
* @return ParameterConstraint
*/
public function setSymbol(string $symbol): ParameterConstraint public function setSymbol(string $symbol): ParameterConstraint
{ {
$this->symbol = $symbol; $this->symbol = $symbol;
return $this; return $this;
} }
/**
* @return string
*/
public function getUnit(): string public function getUnit(): string
{ {
return $this->unit; return $this->unit;
} }
/**
* @param string $unit
* @return ParameterConstraint
*/
public function setUnit(string $unit): ParameterConstraint public function setUnit(string $unit): ParameterConstraint
{ {
$this->unit = $unit; $this->unit = $unit;
return $this; return $this;
} }
/**
* @return TextConstraint
*/
public function getValueText(): TextConstraint public function getValueText(): TextConstraint
{ {
return $this->value_text; return $this->value_text;
} }
/**
* @return ParameterValueConstraint
*/
public function getValue(): ParameterValueConstraint public function getValue(): ParameterValueConstraint
{ {
return $this->value; return $this->value;

View file

@ -25,18 +25,14 @@ use Doctrine\ORM\QueryBuilder;
class ParameterValueConstraint extends NumberConstraint class ParameterValueConstraint extends NumberConstraint
{ {
protected string $alias; final public const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN',
public const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN',
//Additional operators //Additional operators
'IN_RANGE', 'NOT_IN_RANGE', 'GREATER_THAN_RANGE', 'GREATER_EQUAL_RANGE', 'LESS_THAN_RANGE', 'LESS_EQUAL_RANGE', 'RANGE_IN_RANGE', 'RANGE_INTERSECT_RANGE']; 'IN_RANGE', 'NOT_IN_RANGE', 'GREATER_THAN_RANGE', 'GREATER_EQUAL_RANGE', 'LESS_THAN_RANGE', 'LESS_EQUAL_RANGE', 'RANGE_IN_RANGE', 'RANGE_INTERSECT_RANGE'];
/** /**
* @param string $alias The alias which is used in the sub query of ParameterConstraint * @param string $alias The alias which is used in the sub query of ParameterConstraint
*/ */
public function __construct(string $alias) { public function __construct(protected string $alias) {
$this->alias = $alias;
parent::__construct($alias . '.value_typical'); parent::__construct($alias . '.value_typical');
} }

View file

@ -26,23 +26,20 @@ use Doctrine\ORM\QueryBuilder;
class TagsConstraint extends AbstractConstraint class TagsConstraint extends AbstractConstraint
{ {
public const ALLOWED_OPERATOR_VALUES = ['ANY', 'ALL', 'NONE']; final public const ALLOWED_OPERATOR_VALUES = ['ANY', 'ALL', 'NONE'];
/** /**
* @var string|null The operator to use * @param string $value
*/ */
protected ?string $operator; public function __construct(string $property, string $identifier = null, /**
/**
* @var string The value to compare to * @var string The value to compare to
*/ */
protected $value; protected $value = null, /**
* @var string|null The operator to use
public function __construct(string $property, string $identifier = null, $value = null, string $operator = '') */
protected ?string $operator = '')
{ {
parent::__construct($property, $identifier); parent::__construct($property, $identifier);
$this->value = $value;
$this->operator = $operator;
} }
/** /**
@ -62,17 +59,11 @@ class TagsConstraint extends AbstractConstraint
return $this; return $this;
} }
/**
* @return string
*/
public function getValue(): string public function getValue(): string
{ {
return $this->value; return $this->value;
} }
/**
* @param string $value
*/
public function setValue(string $value): self public function setValue(string $value): self
{ {
$this->value = $value; $this->value = $value;
@ -96,9 +87,6 @@ class TagsConstraint extends AbstractConstraint
/** /**
* Builds an expression to query for a single tag * Builds an expression to query for a single tag
* @param QueryBuilder $queryBuilder
* @param string $tag
* @return Expr\Orx
*/ */
protected function getExpressionForTag(QueryBuilder $queryBuilder, string $tag): Expr\Orx protected function getExpressionForTag(QueryBuilder $queryBuilder, string $tag): Expr\Orx
{ {

View file

@ -25,23 +25,20 @@ use Doctrine\ORM\QueryBuilder;
class TextConstraint extends AbstractConstraint class TextConstraint extends AbstractConstraint
{ {
public const ALLOWED_OPERATOR_VALUES = ['=', '!=', 'STARTS', 'ENDS', 'CONTAINS', 'LIKE', 'REGEX']; final public const ALLOWED_OPERATOR_VALUES = ['=', '!=', 'STARTS', 'ENDS', 'CONTAINS', 'LIKE', 'REGEX'];
/** /**
* @var string|null The operator to use * @param string $value
*/ */
protected ?string $operator; public function __construct(string $property, string $identifier = null, /**
/**
* @var string The value to compare to * @var string The value to compare to
*/ */
protected $value; protected $value = null, /**
* @var string|null The operator to use
public function __construct(string $property, string $identifier = null, $value = null, string $operator = '') */
protected ?string $operator = '')
{ {
parent::__construct($property, $identifier); parent::__construct($property, $identifier);
$this->value = $value;
$this->operator = $operator;
} }
/** /**
@ -61,17 +58,11 @@ class TextConstraint extends AbstractConstraint
return $this; return $this;
} }
/**
* @return string
*/
public function getValue(): string public function getValue(): string
{ {
return $this->value; return $this->value;
} }
/**
* @param string $value
*/
public function setValue(string $value): self public function setValue(string $value): self
{ {
$this->value = $value; $this->value = $value;

View file

@ -58,9 +58,6 @@ class LogFilter implements FilterInterface
$this->applyAllChildFilters($queryBuilder); $this->applyAllChildFilters($queryBuilder);
} }
/**
* @return DateTimeConstraint
*/
public function getTimestamp(): DateTimeConstraint public function getTimestamp(): DateTimeConstraint
{ {
return $this->timestamp; return $this->timestamp;
@ -69,38 +66,26 @@ class LogFilter implements FilterInterface
/** /**
* @return IntConstraint|NumberConstraint * @return IntConstraint|NumberConstraint
*/ */
public function getDbId() public function getDbId(): \App\DataTables\Filters\Constraints\IntConstraint|\App\DataTables\Filters\Constraints\NumberConstraint
{ {
return $this->dbId; return $this->dbId;
} }
/**
* @return ChoiceConstraint
*/
public function getLevel(): ChoiceConstraint public function getLevel(): ChoiceConstraint
{ {
return $this->level; return $this->level;
} }
/**
* @return InstanceOfConstraint
*/
public function getEventType(): InstanceOfConstraint public function getEventType(): InstanceOfConstraint
{ {
return $this->eventType; return $this->eventType;
} }
/**
* @return ChoiceConstraint
*/
public function getTargetType(): ChoiceConstraint public function getTargetType(): ChoiceConstraint
{ {
return $this->targetType; return $this->targetType;
} }
/**
* @return IntConstraint
*/
public function getTargetId(): IntConstraint public function getTargetId(): IntConstraint
{ {
return $this->targetId; return $this->targetId;

View file

@ -145,17 +145,11 @@ class PartFilter implements FilterInterface
} }
/**
* @return BooleanConstraint
*/
public function getFavorite(): BooleanConstraint public function getFavorite(): BooleanConstraint
{ {
return $this->favorite; return $this->favorite;
} }
/**
* @return BooleanConstraint
*/
public function getNeedsReview(): BooleanConstraint public function getNeedsReview(): BooleanConstraint
{ {
return $this->needsReview; return $this->needsReview;
@ -176,17 +170,11 @@ class PartFilter implements FilterInterface
return $this->description; return $this->description;
} }
/**
* @return DateTimeConstraint
*/
public function getLastModified(): DateTimeConstraint public function getLastModified(): DateTimeConstraint
{ {
return $this->lastModified; return $this->lastModified;
} }
/**
* @return DateTimeConstraint
*/
public function getAddedDate(): DateTimeConstraint public function getAddedDate(): DateTimeConstraint
{ {
return $this->addedDate; return $this->addedDate;
@ -197,49 +185,31 @@ class PartFilter implements FilterInterface
return $this->category; return $this->category;
} }
/**
* @return EntityConstraint
*/
public function getFootprint(): EntityConstraint public function getFootprint(): EntityConstraint
{ {
return $this->footprint; return $this->footprint;
} }
/**
* @return EntityConstraint
*/
public function getManufacturer(): EntityConstraint public function getManufacturer(): EntityConstraint
{ {
return $this->manufacturer; return $this->manufacturer;
} }
/**
* @return EntityConstraint
*/
public function getSupplier(): EntityConstraint public function getSupplier(): EntityConstraint
{ {
return $this->supplier; return $this->supplier;
} }
/**
* @return EntityConstraint
*/
public function getStorelocation(): EntityConstraint public function getStorelocation(): EntityConstraint
{ {
return $this->storelocation; return $this->storelocation;
} }
/**
* @return EntityConstraint
*/
public function getMeasurementUnit(): EntityConstraint public function getMeasurementUnit(): EntityConstraint
{ {
return $this->measurementUnit; return $this->measurementUnit;
} }
/**
* @return NumberConstraint
*/
public function getDbId(): NumberConstraint public function getDbId(): NumberConstraint
{ {
return $this->dbId; return $this->dbId;
@ -250,33 +220,21 @@ class PartFilter implements FilterInterface
return $this->ipn; return $this->ipn;
} }
/**
* @return TextConstraint
*/
public function getComment(): TextConstraint public function getComment(): TextConstraint
{ {
return $this->comment; return $this->comment;
} }
/**
* @return NumberConstraint
*/
public function getMinAmount(): NumberConstraint public function getMinAmount(): NumberConstraint
{ {
return $this->minAmount; return $this->minAmount;
} }
/**
* @return TextConstraint
*/
public function getManufacturerProductUrl(): TextConstraint public function getManufacturerProductUrl(): TextConstraint
{ {
return $this->manufacturer_product_url; return $this->manufacturer_product_url;
} }
/**
* @return TextConstraint
*/
public function getManufacturerProductNumber(): TextConstraint public function getManufacturerProductNumber(): TextConstraint
{ {
return $this->manufacturer_product_number; return $this->manufacturer_product_number;
@ -287,73 +245,46 @@ class PartFilter implements FilterInterface
return $this->lotCount; return $this->lotCount;
} }
/**
* @return EntityConstraint
*/
public function getLotOwner(): EntityConstraint public function getLotOwner(): EntityConstraint
{ {
return $this->lotOwner; return $this->lotOwner;
} }
/**
* @return TagsConstraint
*/
public function getTags(): TagsConstraint public function getTags(): TagsConstraint
{ {
return $this->tags; return $this->tags;
} }
/**
* @return IntConstraint
*/
public function getOrderdetailsCount(): IntConstraint public function getOrderdetailsCount(): IntConstraint
{ {
return $this->orderdetailsCount; return $this->orderdetailsCount;
} }
/**
* @return IntConstraint
*/
public function getAttachmentsCount(): IntConstraint public function getAttachmentsCount(): IntConstraint
{ {
return $this->attachmentsCount; return $this->attachmentsCount;
} }
/**
* @return BooleanConstraint
*/
public function getLotNeedsRefill(): BooleanConstraint public function getLotNeedsRefill(): BooleanConstraint
{ {
return $this->lotNeedsRefill; return $this->lotNeedsRefill;
} }
/**
* @return BooleanConstraint
*/
public function getLotUnknownAmount(): BooleanConstraint public function getLotUnknownAmount(): BooleanConstraint
{ {
return $this->lotUnknownAmount; return $this->lotUnknownAmount;
} }
/**
* @return DateTimeConstraint
*/
public function getLotExpirationDate(): DateTimeConstraint public function getLotExpirationDate(): DateTimeConstraint
{ {
return $this->lotExpirationDate; return $this->lotExpirationDate;
} }
/**
* @return EntityConstraint
*/
public function getAttachmentType(): EntityConstraint public function getAttachmentType(): EntityConstraint
{ {
return $this->attachmentType; return $this->attachmentType;
} }
/**
* @return TextConstraint
*/
public function getAttachmentName(): TextConstraint public function getAttachmentName(): TextConstraint
{ {
return $this->attachmentName; return $this->attachmentName;
@ -369,9 +300,6 @@ class PartFilter implements FilterInterface
return $this->amountSum; return $this->amountSum;
} }
/**
* @return ArrayCollection
*/
public function getParameters(): ArrayCollection public function getParameters(): ArrayCollection
{ {
return $this->parameters; return $this->parameters;
@ -382,25 +310,16 @@ class PartFilter implements FilterInterface
return $this->parametersCount; return $this->parametersCount;
} }
/**
* @return TextConstraint
*/
public function getLotDescription(): TextConstraint public function getLotDescription(): TextConstraint
{ {
return $this->lotDescription; return $this->lotDescription;
} }
/**
* @return BooleanConstraint
*/
public function getObsolete(): BooleanConstraint public function getObsolete(): BooleanConstraint
{ {
return $this->obsolete; return $this->obsolete;
} }
/**
* @return LessThanDesiredConstraint
*/
public function getLessThanDesired(): LessThanDesiredConstraint public function getLessThanDesired(): LessThanDesiredConstraint
{ {
return $this->lessThanDesired; return $this->lessThanDesired;

View file

@ -26,9 +26,6 @@ use Doctrine\ORM\QueryBuilder;
class PartSearchFilter implements FilterInterface class PartSearchFilter implements FilterInterface
{ {
/** @var string The string to query for */
protected string $keyword;
/** @var boolean Whether to use regex for searching */ /** @var boolean Whether to use regex for searching */
protected bool $regex = false; protected bool $regex = false;
@ -68,9 +65,11 @@ class PartSearchFilter implements FilterInterface
/** @var bool Use Internal Part number for searching */ /** @var bool Use Internal Part number for searching */
protected bool $ipn = true; protected bool $ipn = true;
public function __construct(string $query) public function __construct(
/** @var string The string to query for */
protected string $keyword
)
{ {
$this->keyword = $query;
} }
protected function getFieldsToSearch(): array protected function getFieldsToSearch(): array
@ -122,12 +121,12 @@ class PartSearchFilter implements FilterInterface
$fields_to_search = $this->getFieldsToSearch(); $fields_to_search = $this->getFieldsToSearch();
//If we have nothing to search for, do nothing //If we have nothing to search for, do nothing
if (empty($fields_to_search) || empty($this->keyword)) { if ($fields_to_search === [] || empty($this->keyword)) {
return; return;
} }
//Convert the fields to search to a list of expressions //Convert the fields to search to a list of expressions
$expressions = array_map(function (string $field) { $expressions = array_map(function (string $field): string {
if ($this->regex) { if ($this->regex) {
return sprintf("REGEXP(%s, :search_query) = 1", $field); return sprintf("REGEXP(%s, :search_query) = 1", $field);
} }
@ -148,162 +147,99 @@ class PartSearchFilter implements FilterInterface
} }
} }
/**
* @return string
*/
public function getKeyword(): string public function getKeyword(): string
{ {
return $this->keyword; return $this->keyword;
} }
/**
* @param string $keyword
* @return PartSearchFilter
*/
public function setKeyword(string $keyword): PartSearchFilter public function setKeyword(string $keyword): PartSearchFilter
{ {
$this->keyword = $keyword; $this->keyword = $keyword;
return $this; return $this;
} }
/**
* @return bool
*/
public function isRegex(): bool public function isRegex(): bool
{ {
return $this->regex; return $this->regex;
} }
/**
* @param bool $regex
* @return PartSearchFilter
*/
public function setRegex(bool $regex): PartSearchFilter public function setRegex(bool $regex): PartSearchFilter
{ {
$this->regex = $regex; $this->regex = $regex;
return $this; return $this;
} }
/**
* @return bool
*/
public function isName(): bool public function isName(): bool
{ {
return $this->name; return $this->name;
} }
/**
* @param bool $name
* @return PartSearchFilter
*/
public function setName(bool $name): PartSearchFilter public function setName(bool $name): PartSearchFilter
{ {
$this->name = $name; $this->name = $name;
return $this; return $this;
} }
/**
* @return bool
*/
public function isCategory(): bool public function isCategory(): bool
{ {
return $this->category; return $this->category;
} }
/**
* @param bool $category
* @return PartSearchFilter
*/
public function setCategory(bool $category): PartSearchFilter public function setCategory(bool $category): PartSearchFilter
{ {
$this->category = $category; $this->category = $category;
return $this; return $this;
} }
/**
* @return bool
*/
public function isDescription(): bool public function isDescription(): bool
{ {
return $this->description; return $this->description;
} }
/**
* @param bool $description
* @return PartSearchFilter
*/
public function setDescription(bool $description): PartSearchFilter public function setDescription(bool $description): PartSearchFilter
{ {
$this->description = $description; $this->description = $description;
return $this; return $this;
} }
/**
* @return bool
*/
public function isTags(): bool public function isTags(): bool
{ {
return $this->tags; return $this->tags;
} }
/**
* @param bool $tags
* @return PartSearchFilter
*/
public function setTags(bool $tags): PartSearchFilter public function setTags(bool $tags): PartSearchFilter
{ {
$this->tags = $tags; $this->tags = $tags;
return $this; return $this;
} }
/**
* @return bool
*/
public function isStorelocation(): bool public function isStorelocation(): bool
{ {
return $this->storelocation; return $this->storelocation;
} }
/**
* @param bool $storelocation
* @return PartSearchFilter
*/
public function setStorelocation(bool $storelocation): PartSearchFilter public function setStorelocation(bool $storelocation): PartSearchFilter
{ {
$this->storelocation = $storelocation; $this->storelocation = $storelocation;
return $this; return $this;
} }
/**
* @return bool
*/
public function isOrdernr(): bool public function isOrdernr(): bool
{ {
return $this->ordernr; return $this->ordernr;
} }
/**
* @param bool $ordernr
* @return PartSearchFilter
*/
public function setOrdernr(bool $ordernr): PartSearchFilter public function setOrdernr(bool $ordernr): PartSearchFilter
{ {
$this->ordernr = $ordernr; $this->ordernr = $ordernr;
return $this; return $this;
} }
/**
* @return bool
*/
public function isMpn(): bool public function isMpn(): bool
{ {
return $this->mpn; return $this->mpn;
} }
/**
* @param bool $mpn
* @return PartSearchFilter
*/
public function setMpn(bool $mpn): PartSearchFilter public function setMpn(bool $mpn): PartSearchFilter
{ {
$this->mpn = $mpn; $this->mpn = $mpn;
@ -321,72 +257,44 @@ class PartSearchFilter implements FilterInterface
return $this; return $this;
} }
/**
* @return bool
*/
public function isSupplier(): bool public function isSupplier(): bool
{ {
return $this->supplier; return $this->supplier;
} }
/**
* @param bool $supplier
* @return PartSearchFilter
*/
public function setSupplier(bool $supplier): PartSearchFilter public function setSupplier(bool $supplier): PartSearchFilter
{ {
$this->supplier = $supplier; $this->supplier = $supplier;
return $this; return $this;
} }
/**
* @return bool
*/
public function isManufacturer(): bool public function isManufacturer(): bool
{ {
return $this->manufacturer; return $this->manufacturer;
} }
/**
* @param bool $manufacturer
* @return PartSearchFilter
*/
public function setManufacturer(bool $manufacturer): PartSearchFilter public function setManufacturer(bool $manufacturer): PartSearchFilter
{ {
$this->manufacturer = $manufacturer; $this->manufacturer = $manufacturer;
return $this; return $this;
} }
/**
* @return bool
*/
public function isFootprint(): bool public function isFootprint(): bool
{ {
return $this->footprint; return $this->footprint;
} }
/**
* @param bool $footprint
* @return PartSearchFilter
*/
public function setFootprint(bool $footprint): PartSearchFilter public function setFootprint(bool $footprint): PartSearchFilter
{ {
$this->footprint = $footprint; $this->footprint = $footprint;
return $this; return $this;
} }
/**
* @return bool
*/
public function isComment(): bool public function isComment(): bool
{ {
return $this->comment; return $this->comment;
} }
/**
* @param bool $comment
* @return PartSearchFilter
*/
public function setComment(bool $comment): PartSearchFilter public function setComment(bool $comment): PartSearchFilter
{ {
$this->comment = $comment; $this->comment = $comment;

View file

@ -31,19 +31,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/ */
class PartDataTableHelper class PartDataTableHelper
{ {
private PartPreviewGenerator $previewGenerator; public function __construct(private readonly PartPreviewGenerator $previewGenerator, private readonly AttachmentURLGenerator $attachmentURLGenerator, private readonly EntityURLGenerator $entityURLGenerator, private readonly TranslatorInterface $translator)
private AttachmentURLGenerator $attachmentURLGenerator;
private TranslatorInterface $translator;
private EntityURLGenerator $entityURLGenerator;
public function __construct(PartPreviewGenerator $previewGenerator, AttachmentURLGenerator $attachmentURLGenerator,
EntityURLGenerator $entityURLGenerator, TranslatorInterface $translator)
{ {
$this->previewGenerator = $previewGenerator;
$this->attachmentURLGenerator = $attachmentURLGenerator;
$this->translator = $translator;
$this->entityURLGenerator = $entityURLGenerator;
} }
public function renderName(Part $context): string public function renderName(Part $context): string
@ -57,7 +46,7 @@ class PartDataTableHelper
if ($context->isNeedsReview()) { if ($context->isNeedsReview()) {
$icon = sprintf('<i class="fa-solid fa-ambulance fa-fw me-1" title="%s"></i>', $this->translator->trans('part.needs_review.badge')); $icon = sprintf('<i class="fa-solid fa-ambulance fa-fw me-1" title="%s"></i>', $this->translator->trans('part.needs_review.badge'));
} }
if ($context->getBuiltProject() !== null) { if ($context->getBuiltProject() instanceof \App\Entity\ProjectSystem\Project) {
$icon = sprintf('<i class="fa-solid fa-box-archive fa-fw me-1" title="%s"></i>', $icon = sprintf('<i class="fa-solid fa-box-archive fa-fw me-1" title="%s"></i>',
$this->translator->trans('part.info.projectBuildPart.hint') . ': ' . $context->getBuiltProject()->getName()); $this->translator->trans('part.info.projectBuildPart.hint') . ': ' . $context->getBuiltProject()->getName());
} }
@ -74,7 +63,7 @@ class PartDataTableHelper
public function renderPicture(Part $context): string public function renderPicture(Part $context): string
{ {
$preview_attachment = $this->previewGenerator->getTablePreviewAttachment($context); $preview_attachment = $this->previewGenerator->getTablePreviewAttachment($context);
if (null === $preview_attachment) { if (!$preview_attachment instanceof \App\Entity\Attachments\Attachment) {
return ''; return '';
} }

View file

@ -61,27 +61,13 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class LogDataTable implements DataTableTypeInterface class LogDataTable implements DataTableTypeInterface
{ {
protected ElementTypeNameGenerator $elementTypeNameGenerator;
protected TranslatorInterface $translator;
protected UrlGeneratorInterface $urlGenerator;
protected EntityURLGenerator $entityURLGenerator;
protected LogEntryRepository $logRepo; protected LogEntryRepository $logRepo;
protected \Symfony\Bundle\SecurityBundle\Security $security;
protected UserAvatarHelper $userAvatarHelper;
protected LogLevelHelper $logLevelHelper;
public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator, TranslatorInterface $translator, public function __construct(protected ElementTypeNameGenerator $elementTypeNameGenerator, protected TranslatorInterface $translator,
UrlGeneratorInterface $urlGenerator, EntityURLGenerator $entityURLGenerator, EntityManagerInterface $entityManager, protected UrlGeneratorInterface $urlGenerator, protected EntityURLGenerator $entityURLGenerator, EntityManagerInterface $entityManager,
\Symfony\Bundle\SecurityBundle\Security $security, UserAvatarHelper $userAvatarHelper, LogLevelHelper $logLevelHelper) protected \Symfony\Bundle\SecurityBundle\Security $security, protected UserAvatarHelper $userAvatarHelper, protected LogLevelHelper $logLevelHelper)
{ {
$this->elementTypeNameGenerator = $elementTypeNameGenerator;
$this->translator = $translator;
$this->urlGenerator = $urlGenerator;
$this->entityURLGenerator = $entityURLGenerator;
$this->logRepo = $entityManager->getRepository(AbstractLogEntry::class); $this->logRepo = $entityManager->getRepository(AbstractLogEntry::class);
$this->security = $security;
$this->userAvatarHelper = $userAvatarHelper;
$this->logLevelHelper = $logLevelHelper;
} }
public function configureOptions(OptionsResolver $optionsResolver): void public function configureOptions(OptionsResolver $optionsResolver): void
@ -115,21 +101,17 @@ class LogDataTable implements DataTableTypeInterface
//This special $$rowClass column is used to set the row class depending on the log level. The class gets set by the frontend controller //This special $$rowClass column is used to set the row class depending on the log level. The class gets set by the frontend controller
$dataTable->add('dont_matter', RowClassColumn::class, [ $dataTable->add('dont_matter', RowClassColumn::class, [
'render' => function ($value, AbstractLogEntry $context) { 'render' => fn($value, AbstractLogEntry $context) => $this->logLevelHelper->logLevelToTableColorClass($context->getLevelString()),
return $this->logLevelHelper->logLevelToTableColorClass($context->getLevelString());
},
]); ]);
$dataTable->add('symbol', TextColumn::class, [ $dataTable->add('symbol', TextColumn::class, [
'label' => '', 'label' => '',
'className' => 'no-colvis', 'className' => 'no-colvis',
'render' => function ($value, AbstractLogEntry $context) { 'render' => fn($value, AbstractLogEntry $context): string => sprintf(
return sprintf(
'<i class="fas fa-fw %s" title="%s"></i>', '<i class="fas fa-fw %s" title="%s"></i>',
$this->logLevelHelper->logLevelToIconClass($context->getLevelString()), $this->logLevelHelper->logLevelToIconClass($context->getLevelString()),
$context->getLevelString() $context->getLevelString()
); ),
},
]); ]);
$dataTable->add('id', TextColumn::class, [ $dataTable->add('id', TextColumn::class, [
@ -140,12 +122,10 @@ class LogDataTable implements DataTableTypeInterface
$dataTable->add('timestamp', LocaleDateTimeColumn::class, [ $dataTable->add('timestamp', LocaleDateTimeColumn::class, [
'label' => 'log.timestamp', 'label' => 'log.timestamp',
'timeFormat' => 'medium', 'timeFormat' => 'medium',
'render' => function (string $value, AbstractLogEntry $context) { 'render' => fn(string $value, AbstractLogEntry $context): string => sprintf('<a href="%s">%s</a>',
return sprintf('<a href="%s">%s</a>',
$this->urlGenerator->generate('log_details', ['id' => $context->getId()]), $this->urlGenerator->generate('log_details', ['id' => $context->getId()]),
$value $value
); )
}
]); ]);
$dataTable->add('type', TextColumn::class, [ $dataTable->add('type', TextColumn::class, [
@ -169,18 +149,16 @@ class LogDataTable implements DataTableTypeInterface
'label' => 'log.level', 'label' => 'log.level',
'visible' => 'system_log' === $options['mode'], 'visible' => 'system_log' === $options['mode'],
'propertyPath' => 'levelString', 'propertyPath' => 'levelString',
'render' => function (string $value, AbstractLogEntry $context) { 'render' => fn(string $value, AbstractLogEntry $context) => $this->translator->trans('log.level.'.$value),
return $this->translator->trans('log.level.'.$value);
},
]); ]);
$dataTable->add('user', TextColumn::class, [ $dataTable->add('user', TextColumn::class, [
'label' => 'log.user', 'label' => 'log.user',
'render' => function ($value, AbstractLogEntry $context) { 'render' => function ($value, AbstractLogEntry $context): string {
$user = $context->getUser(); $user = $context->getUser();
//If user was deleted, show the info from the username field //If user was deleted, show the info from the username field
if ($user === null) { if (!$user instanceof \App\Entity\UserSystem\User) {
if ($context->isCLIEntry()) { if ($context->isCLIEntry()) {
return sprintf('%s [%s]', return sprintf('%s [%s]',
htmlentities($context->getCLIUsername()), htmlentities($context->getCLIUsername()),
@ -241,19 +219,17 @@ class LogDataTable implements DataTableTypeInterface
) { ) {
try { try {
$target = $this->logRepo->getTargetElement($context); $target = $this->logRepo->getTargetElement($context);
if (null !== $target) { if ($target instanceof \App\Entity\Base\AbstractDBElement) {
return $this->entityURLGenerator->timeTravelURL($target, $context->getTimestamp()); return $this->entityURLGenerator->timeTravelURL($target, $context->getTimestamp());
} }
} catch (EntityNotSupportedException $exception) { } catch (EntityNotSupportedException) {
return null; return null;
} }
} }
return null; return null;
}, },
'disabled' => function ($value, AbstractLogEntry $context) { 'disabled' => fn($value, AbstractLogEntry $context) => !$this->security->isGranted('show_history', $context->getTargetClass()),
return !$this->security->isGranted('show_history', $context->getTargetClass());
},
]); ]);
$dataTable->add('actionRevert', RevertLogColumn::class, [ $dataTable->add('actionRevert', RevertLogColumn::class, [
@ -310,7 +286,7 @@ class LogDataTable implements DataTableTypeInterface
foreach ($options['filter_elements'] as $element) { foreach ($options['filter_elements'] as $element) {
/** @var AbstractDBElement $element */ /** @var AbstractDBElement $element */
$target_type = AbstractLogEntry::targetTypeClassToID(get_class($element)); $target_type = AbstractLogEntry::targetTypeClassToID($element::class);
$target_id = $element->getID(); $target_id = $element->getID();
$builder->orWhere("log.target_type = ${target_type} AND log.target_id = ${target_id}"); $builder->orWhere("log.target_type = ${target_type} AND log.target_id = ${target_id}");
} }

View file

@ -54,22 +54,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
final class PartsDataTable implements DataTableTypeInterface final class PartsDataTable implements DataTableTypeInterface
{ {
private TranslatorInterface $translator; public function __construct(private readonly EntityURLGenerator $urlGenerator, private readonly TranslatorInterface $translator, private readonly AmountFormatter $amountFormatter, private readonly PartDataTableHelper $partDataTableHelper, private readonly \Symfony\Bundle\SecurityBundle\Security $security)
private AmountFormatter $amountFormatter;
private \Symfony\Bundle\SecurityBundle\Security $security;
private PartDataTableHelper $partDataTableHelper;
private EntityURLGenerator $urlGenerator;
public function __construct(EntityURLGenerator $urlGenerator, TranslatorInterface $translator,
AmountFormatter $amountFormatter,PartDataTableHelper $partDataTableHelper, \Symfony\Bundle\SecurityBundle\Security $security)
{ {
$this->urlGenerator = $urlGenerator;
$this->translator = $translator;
$this->amountFormatter = $amountFormatter;
$this->security = $security;
$this->partDataTableHelper = $partDataTableHelper;
} }
public function configureOptions(OptionsResolver $optionsResolver): void public function configureOptions(OptionsResolver $optionsResolver): void
@ -92,7 +78,7 @@ final class PartsDataTable implements DataTableTypeInterface
$dataTable $dataTable
//Color the table rows depending on the review and favorite status //Color the table rows depending on the review and favorite status
->add('dont_matter', RowClassColumn::class, [ ->add('dont_matter', RowClassColumn::class, [
'render' => function ($value, Part $context) { 'render' => function ($value, Part $context): string {
if ($context->isNeedsReview()) { if ($context->isNeedsReview()) {
return 'table-secondary'; return 'table-secondary';
} }
@ -108,15 +94,11 @@ final class PartsDataTable implements DataTableTypeInterface
->add('picture', TextColumn::class, [ ->add('picture', TextColumn::class, [
'label' => '', 'label' => '',
'className' => 'no-colvis', 'className' => 'no-colvis',
'render' => function ($value, Part $context) { 'render' => fn($value, Part $context) => $this->partDataTableHelper->renderPicture($context),
return $this->partDataTableHelper->renderPicture($context);
},
]) ])
->add('name', TextColumn::class, [ ->add('name', TextColumn::class, [
'label' => $this->translator->trans('part.table.name'), 'label' => $this->translator->trans('part.table.name'),
'render' => function ($value, Part $context) { 'render' => fn($value, Part $context) => $this->partDataTableHelper->renderName($context),
return $this->partDataTableHelper->renderName($context);
},
]) ])
->add('id', TextColumn::class, [ ->add('id', TextColumn::class, [
'label' => $this->translator->trans('part.table.id'), 'label' => $this->translator->trans('part.table.id'),
@ -153,11 +135,11 @@ final class PartsDataTable implements DataTableTypeInterface
$dataTable->add('storelocation', TextColumn::class, [ $dataTable->add('storelocation', TextColumn::class, [
'label' => $this->translator->trans('part.table.storeLocations'), 'label' => $this->translator->trans('part.table.storeLocations'),
'orderField' => 'storelocations.name', 'orderField' => 'storelocations.name',
'render' => function ($value, Part $context) { 'render' => function ($value, Part $context): string {
$tmp = []; $tmp = [];
foreach ($context->getPartLots() as $lot) { foreach ($context->getPartLots() as $lot) {
//Ignore lots without storelocation //Ignore lots without storelocation
if (null === $lot->getStorageLocation()) { if (!$lot->getStorageLocation() instanceof \App\Entity\Parts\Storelocation) {
continue; continue;
} }
$tmp[] = sprintf( $tmp[] = sprintf(
@ -216,9 +198,7 @@ final class PartsDataTable implements DataTableTypeInterface
->add('minamount', TextColumn::class, [ ->add('minamount', TextColumn::class, [
'label' => $this->translator->trans('part.table.minamount'), 'label' => $this->translator->trans('part.table.minamount'),
'visible' => false, 'visible' => false,
'render' => function ($value, Part $context) { 'render' => fn($value, Part $context): string => htmlspecialchars($this->amountFormatter->format($value, $context->getPartUnit())),
return htmlspecialchars($this->amountFormatter->format($value, $context->getPartUnit()));
},
]); ]);
if ($this->security->isGranted('@footprints.read')) { if ($this->security->isGranted('@footprints.read')) {
@ -278,12 +258,8 @@ final class PartsDataTable implements DataTableTypeInterface
->add('edit', IconLinkColumn::class, [ ->add('edit', IconLinkColumn::class, [
'label' => $this->translator->trans('part.table.edit'), 'label' => $this->translator->trans('part.table.edit'),
'visible' => false, 'visible' => false,
'href' => function ($value, Part $context) { 'href' => fn($value, Part $context) => $this->urlGenerator->editURL($context),
return $this->urlGenerator->editURL($context); 'disabled' => fn($value, Part $context) => !$this->security->isGranted('edit', $context),
},
'disabled' => function ($value, Part $context) {
return !$this->security->isGranted('edit', $context);
},
'title' => $this->translator->trans('part.table.edit.title'), 'title' => $this->translator->trans('part.table.edit.title'),
]) ])

View file

@ -40,18 +40,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class ProjectBomEntriesDataTable implements DataTableTypeInterface class ProjectBomEntriesDataTable implements DataTableTypeInterface
{ {
protected TranslatorInterface $translator; public function __construct(protected TranslatorInterface $translator, protected PartDataTableHelper $partDataTableHelper, protected EntityURLGenerator $entityURLGenerator, protected AmountFormatter $amountFormatter)
protected PartDataTableHelper $partDataTableHelper;
protected EntityURLGenerator $entityURLGenerator;
protected AmountFormatter $amountFormatter;
public function __construct(TranslatorInterface $translator, PartDataTableHelper $partDataTableHelper,
EntityURLGenerator $entityURLGenerator, AmountFormatter $amountFormatter)
{ {
$this->translator = $translator;
$this->partDataTableHelper = $partDataTableHelper;
$this->entityURLGenerator = $entityURLGenerator;
$this->amountFormatter = $amountFormatter;
} }
@ -63,7 +53,7 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
'label' => '', 'label' => '',
'className' => 'no-colvis', 'className' => 'no-colvis',
'render' => function ($value, ProjectBOMEntry $context) { 'render' => function ($value, ProjectBOMEntry $context) {
if($context->getPart() === null) { if(!$context->getPart() instanceof \App\Entity\Parts\Part) {
return ''; return '';
} }
return $this->partDataTableHelper->renderPicture($context->getPart()); return $this->partDataTableHelper->renderPicture($context->getPart());
@ -79,9 +69,9 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
'label' => $this->translator->trans('project.bom.quantity'), 'label' => $this->translator->trans('project.bom.quantity'),
'className' => 'text-center', 'className' => 'text-center',
'orderField' => 'bom_entry.quantity', 'orderField' => 'bom_entry.quantity',
'render' => function ($value, ProjectBOMEntry $context) { 'render' => function ($value, ProjectBOMEntry $context): float|string {
//If we have a non-part entry, only show the rounded quantity //If we have a non-part entry, only show the rounded quantity
if ($context->getPart() === null) { if (!$context->getPart() instanceof \App\Entity\Parts\Part) {
return round($context->getQuantity()); return round($context->getQuantity());
} }
//Otherwise use the unit of the part to format the quantity //Otherwise use the unit of the part to format the quantity
@ -93,10 +83,10 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
'label' => $this->translator->trans('part.table.name'), 'label' => $this->translator->trans('part.table.name'),
'orderField' => 'part.name', 'orderField' => 'part.name',
'render' => function ($value, ProjectBOMEntry $context) { 'render' => function ($value, ProjectBOMEntry $context) {
if($context->getPart() === null) { if(!$context->getPart() instanceof \App\Entity\Parts\Part) {
return htmlspecialchars($context->getName()); return htmlspecialchars($context->getName());
} }
if($context->getPart() !== null) { if($context->getPart() instanceof \App\Entity\Parts\Part) {
$tmp = $this->partDataTableHelper->renderName($context->getPart()); $tmp = $this->partDataTableHelper->renderName($context->getPart());
if(!empty($context->getName())) { if(!empty($context->getName())) {
$tmp .= '<br><b>'.htmlspecialchars($context->getName()).'</b>'; $tmp .= '<br><b>'.htmlspecialchars($context->getName()).'</b>';
@ -110,7 +100,7 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
->add('description', MarkdownColumn::class, [ ->add('description', MarkdownColumn::class, [
'label' => $this->translator->trans('part.table.description'), 'label' => $this->translator->trans('part.table.description'),
'data' => function (ProjectBOMEntry $context) { 'data' => function (ProjectBOMEntry $context) {
if($context->getPart() !== null) { if($context->getPart() instanceof \App\Entity\Parts\Part) {
return $context->getPart()->getDescription(); return $context->getPart()->getDescription();
} }
//For non-part BOM entries show the comment field //For non-part BOM entries show the comment field

View file

@ -47,11 +47,8 @@ use function preg_match;
*/ */
class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface
{ {
public const PURGE_MODE_DELETE = 1; final public const PURGE_MODE_DELETE = 1;
public const PURGE_MODE_TRUNCATE = 2; final public const PURGE_MODE_TRUNCATE = 2;
/** @var EntityManagerInterface|null */
private ?EntityManagerInterface $em;
/** /**
* If the purge should be done through DELETE or TRUNCATE statements * If the purge should be done through DELETE or TRUNCATE statements
@ -60,31 +57,26 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface
*/ */
private int $purgeMode = self::PURGE_MODE_DELETE; private int $purgeMode = self::PURGE_MODE_DELETE;
/**
* Table/view names to be excluded from purge
*
* @var string[]
*/
private array $excluded;
/** /**
* Construct new purger instance. * Construct new purger instance.
* *
* @param EntityManagerInterface|null $em EntityManagerInterface instance used for persistence. * @param EntityManagerInterface|null $em EntityManagerInterface instance used for persistence.
* @param string[] $excluded array of table/view names to be excluded from purge * @param string[] $excluded array of table/view names to be excluded from purge
*/ */
public function __construct(?EntityManagerInterface $em = null, array $excluded = []) public function __construct(
private ?\Doctrine\ORM\EntityManagerInterface $em = null,
/**
* Table/view names to be excluded from purge
*/
private readonly array $excluded = []
)
{ {
$this->em = $em;
$this->excluded = $excluded;
} }
/** /**
* Set the purge mode * Set the purge mode
* *
* @param int $mode
* *
* @return void
*/ */
public function setPurgeMode(int $mode): void public function setPurgeMode(int $mode): void
{ {
@ -93,8 +85,6 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface
/** /**
* Get the purge mode * Get the purge mode
*
* @return int
*/ */
public function getPurgeMode(): int public function getPurgeMode(): int
{ {
@ -123,7 +113,7 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface
$classes = []; $classes = [];
foreach ($this->em->getMetadataFactory()->getAllMetadata() as $metadata) { foreach ($this->em->getMetadataFactory()->getAllMetadata() as $metadata) {
if ($metadata->isMappedSuperclass || (isset($metadata->isEmbeddedClass) && $metadata->isEmbeddedClass)) { if ($metadata->isMappedSuperclass || ($metadata->isEmbeddedClass !== null && $metadata->isEmbeddedClass)) {
continue; continue;
} }
@ -143,7 +133,7 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface
$class = $commitOrder[$i]; $class = $commitOrder[$i];
if ( if (
(isset($class->isEmbeddedClass) && $class->isEmbeddedClass) || ($class->isEmbeddedClass !== null && $class->isEmbeddedClass) ||
$class->isMappedSuperclass || $class->isMappedSuperclass ||
($class->isInheritanceTypeSingleTable() && $class->name !== $class->rootEntityName) ($class->isInheritanceTypeSingleTable() && $class->name !== $class->rootEntityName)
) { ) {
@ -172,13 +162,13 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface
foreach ($orderedTables as $tbl) { foreach ($orderedTables as $tbl) {
// If we have a filter expression, check it and skip if necessary // If we have a filter expression, check it and skip if necessary
if (! $emptyFilterExpression && ! preg_match($filterExpr, $tbl)) { if (! $emptyFilterExpression && ! preg_match($filterExpr, (string) $tbl)) {
continue; continue;
} }
// The table name might be quoted, we have to trim it // The table name might be quoted, we have to trim it
// See https://github.com/Part-DB/Part-DB-server/issues/299 // See https://github.com/Part-DB/Part-DB-server/issues/299
$tbl = trim($tbl, '"'); $tbl = trim((string) $tbl, '"');
$tbl = trim($tbl, '`'); $tbl = trim($tbl, '`');
// If the table is excluded, skip it as well // If the table is excluded, skip it as well
@ -276,11 +266,6 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface
return array_reverse($sorter->sort()); return array_reverse($sorter->sort());
} }
/**
* @param array $classes
*
* @return array
*/
private function getAssociationTables(array $classes, AbstractPlatform $platform): array private function getAssociationTables(array $classes, AbstractPlatform $platform): array
{ {
$associationTables = []; $associationTables = [];
@ -310,9 +295,6 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface
return $this->em->getConfiguration()->getQuoteStrategy()->getTableName($class, $platform); return $this->em->getConfiguration()->getQuoteStrategy()->getTableName($class, $platform);
} }
/**
* @param array $assoc
*/
private function getJoinTableName( private function getJoinTableName(
array $assoc, array $assoc,
ClassMetadata $class, ClassMetadata $class,

View file

@ -45,7 +45,7 @@ class SQLiteRegexExtension implements EventSubscriberInterface
if($native_connection instanceof \PDO && method_exists($native_connection, 'sqliteCreateFunction' )) { if($native_connection instanceof \PDO && method_exists($native_connection, 'sqliteCreateFunction' )) {
$native_connection->sqliteCreateFunction('REGEXP', function ($pattern, $value) { $native_connection->sqliteCreateFunction('REGEXP', function ($pattern, $value) {
try { try {
return (false !== mb_ereg($pattern, $value)) ? 1 : 0; return (mb_ereg($pattern, $value)) ? 1 : 0;
} catch (\ErrorException $e) { } catch (\ErrorException $e) {
throw InvalidRegexException::fromMBRegexError($e); throw InvalidRegexException::fromMBRegexError($e);
} }

View file

@ -27,7 +27,7 @@ use Doctrine\DBAL\Types\Type;
class BigDecimalType extends Type class BigDecimalType extends Type
{ {
public const BIG_DECIMAL = 'big_decimal'; final public const BIG_DECIMAL = 'big_decimal';
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
{ {
@ -53,7 +53,7 @@ class BigDecimalType extends Type
*/ */
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
{ {
if (null === $value) { if (!$value instanceof \Brick\Math\BigDecimal) {
return null; return null;
} }

View file

@ -47,7 +47,7 @@ class UTCDateTimeType extends DateTimeType
*/ */
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
{ {
if (!self::$utc_timezone) { if (!self::$utc_timezone instanceof \DateTimeZone) {
self::$utc_timezone = new DateTimeZone('UTC'); self::$utc_timezone = new DateTimeZone('UTC');
} }
@ -60,7 +60,7 @@ class UTCDateTimeType extends DateTimeType
public function convertToPHPValue($value, AbstractPlatform $platform): ?DateTime public function convertToPHPValue($value, AbstractPlatform $platform): ?DateTime
{ {
if (!self::$utc_timezone) { if (!self::$utc_timezone instanceof \DateTimeZone) {
self::$utc_timezone = new DateTimeZone('UTC'); self::$utc_timezone = new DateTimeZone('UTC');
} }

View file

@ -34,11 +34,11 @@ use LogicException;
/** /**
* Class Attachment. * Class Attachment.
*/ */
#[ORM\Entity(repositoryClass: 'App\Repository\AttachmentRepository')] #[ORM\Entity(repositoryClass: \App\Repository\AttachmentRepository::class)]
#[ORM\InheritanceType('SINGLE_TABLE')] #[ORM\InheritanceType('SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'class_name', type: 'string')] #[ORM\DiscriminatorColumn(name: 'class_name', type: 'string')]
#[ORM\DiscriminatorMap(['PartDB\Part' => 'PartAttachment', 'Part' => 'PartAttachment', 'PartDB\Device' => 'ProjectAttachment', 'Device' => 'ProjectAttachment', 'AttachmentType' => 'AttachmentTypeAttachment', 'Category' => 'CategoryAttachment', 'Footprint' => 'FootprintAttachment', 'Manufacturer' => 'ManufacturerAttachment', 'Currency' => 'CurrencyAttachment', 'Group' => 'GroupAttachment', 'MeasurementUnit' => 'MeasurementUnitAttachment', 'Storelocation' => 'StorelocationAttachment', 'Supplier' => 'SupplierAttachment', 'User' => 'UserAttachment', 'LabelProfile' => 'LabelAttachment'])] #[ORM\DiscriminatorMap(['PartDB\Part' => 'PartAttachment', 'Part' => 'PartAttachment', 'PartDB\Device' => 'ProjectAttachment', 'Device' => 'ProjectAttachment', 'AttachmentType' => 'AttachmentTypeAttachment', 'Category' => 'CategoryAttachment', 'Footprint' => 'FootprintAttachment', 'Manufacturer' => 'ManufacturerAttachment', 'Currency' => 'CurrencyAttachment', 'Group' => 'GroupAttachment', 'MeasurementUnit' => 'MeasurementUnitAttachment', 'Storelocation' => 'StorelocationAttachment', 'Supplier' => 'SupplierAttachment', 'User' => 'UserAttachment', 'LabelProfile' => 'LabelAttachment'])]
#[ORM\EntityListeners(['App\EntityListeners\AttachmentDeleteListener'])] #[ORM\EntityListeners([\App\EntityListeners\AttachmentDeleteListener::class])]
#[ORM\Table(name: '`attachments`')] #[ORM\Table(name: '`attachments`')]
#[ORM\Index(name: 'attachments_idx_id_element_id_class_name', columns: ['id', 'element_id', 'class_name'])] #[ORM\Index(name: 'attachments_idx_id_element_id_class_name', columns: ['id', 'element_id', 'class_name'])]
#[ORM\Index(name: 'attachments_idx_class_name_id', columns: ['class_name', 'id'])] #[ORM\Index(name: 'attachments_idx_class_name_id', columns: ['class_name', 'id'])]
@ -51,23 +51,23 @@ abstract class Attachment extends AbstractNamedDBElement
* Based on: https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types * Based on: https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types
* It will be used to determine if an attachment is a picture and therefore will be shown to user as preview. * It will be used to determine if an attachment is a picture and therefore will be shown to user as preview.
*/ */
public const PICTURE_EXTS = ['apng', 'bmp', 'gif', 'ico', 'cur', 'jpg', 'jpeg', 'jfif', 'pjpeg', 'pjp', 'png', final public const PICTURE_EXTS = ['apng', 'bmp', 'gif', 'ico', 'cur', 'jpg', 'jpeg', 'jfif', 'pjpeg', 'pjp', 'png',
'svg', 'webp', ]; 'svg', 'webp', ];
/** /**
* A list of extensions that will be treated as a 3D Model that can be shown to user directly in Part-DB. * A list of extensions that will be treated as a 3D Model that can be shown to user directly in Part-DB.
*/ */
public const MODEL_EXTS = ['x3d']; final public const MODEL_EXTS = ['x3d'];
/** /**
* When the path begins with one of the placeholders. * When the path begins with one of the placeholders.
*/ */
public const INTERNAL_PLACEHOLDER = ['%BASE%', '%MEDIA%', '%SECURE%']; final public const INTERNAL_PLACEHOLDER = ['%BASE%', '%MEDIA%', '%SECURE%'];
/** /**
* @var array placeholders for attachments which using built in files * @var array placeholders for attachments which using built in files
*/ */
public const BUILTIN_PLACEHOLDER = ['%FOOTPRINTS%', '%FOOTPRINTS3D%']; final public const BUILTIN_PLACEHOLDER = ['%FOOTPRINTS%', '%FOOTPRINTS3D%'];
/** /**
* @var string The class of the element that can be passed to this attachment. Must be overridden in subclasses. * @var string The class of the element that can be passed to this attachment. Must be overridden in subclasses.

View file

@ -33,7 +33,7 @@ use Symfony\Component\Validator\Constraints as Assert;
/** /**
* Class AttachmentType. * Class AttachmentType.
*/ */
#[ORM\Entity(repositoryClass: 'App\Repository\StructuralDBElementRepository')] #[ORM\Entity(repositoryClass: \App\Repository\StructuralDBElementRepository::class)]
#[ORM\Table(name: '`attachment_types`')] #[ORM\Table(name: '`attachment_types`')]
#[ORM\Index(name: 'attachment_types_idx_name', columns: ['name'])] #[ORM\Index(name: 'attachment_types_idx_name', columns: ['name'])]
#[ORM\Index(name: 'attachment_types_idx_parent_name', columns: ['parent_id', 'name'])] #[ORM\Index(name: 'attachment_types_idx_parent_name', columns: ['parent_id', 'name'])]
@ -45,7 +45,7 @@ class AttachmentType extends AbstractStructuralDBElement
#[ORM\ManyToOne(targetEntity: 'AttachmentType', inversedBy: 'children')] #[ORM\ManyToOne(targetEntity: 'AttachmentType', inversedBy: 'children')]
#[ORM\JoinColumn(name: 'parent_id')] #[ORM\JoinColumn(name: 'parent_id')]
protected ?AbstractStructuralDBElement $parent; protected ?AbstractStructuralDBElement $parent = null;
/** /**
* @var string * @var string
@ -57,14 +57,14 @@ class AttachmentType extends AbstractStructuralDBElement
* @var Collection<int, AttachmentTypeAttachment> * @var Collection<int, AttachmentTypeAttachment>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: 'App\Entity\Attachments\AttachmentTypeAttachment', mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(targetEntity: \App\Entity\Attachments\AttachmentTypeAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $attachments; protected Collection $attachments;
/** @var Collection<int, AttachmentTypeParameter> /** @var Collection<int, AttachmentTypeParameter>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: 'App\Entity\Parameters\AttachmentTypeParameter', mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(targetEntity: \App\Entity\Parameters\AttachmentTypeParameter::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])] #[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])]
protected Collection $parameters; protected Collection $parameters;

View file

@ -32,11 +32,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class AttachmentTypeAttachment extends Attachment class AttachmentTypeAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = AttachmentType::class; final public const ALLOWED_ELEMENT_CLASS = AttachmentType::class;
/** /**
* @var AttachmentContainingDBElement|null the element this attachment is associated with * @var AttachmentContainingDBElement|null the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\Attachments\AttachmentType', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\Attachments\AttachmentType::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -33,11 +33,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class CategoryAttachment extends Attachment class CategoryAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = Category::class; final public const ALLOWED_ELEMENT_CLASS = Category::class;
/** /**
* @var AttachmentContainingDBElement|null the element this attachment is associated with * @var AttachmentContainingDBElement|null the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\Parts\Category', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\Parts\Category::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -33,11 +33,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class CurrencyAttachment extends Attachment class CurrencyAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = Currency::class; final public const ALLOWED_ELEMENT_CLASS = Currency::class;
/** /**
* @var Currency|null the element this attachment is associated with * @var Currency|null the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\PriceInformations\Currency', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\PriceInformations\Currency::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -33,11 +33,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class FootprintAttachment extends Attachment class FootprintAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = Footprint::class; final public const ALLOWED_ELEMENT_CLASS = Footprint::class;
/** /**
* @var Footprint|null the element this attachment is associated with * @var Footprint|null the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\Parts\Footprint', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\Parts\Footprint::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -33,12 +33,12 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class GroupAttachment extends Attachment class GroupAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = Group::class; final public const ALLOWED_ELEMENT_CLASS = Group::class;
/** /**
* @var Group|null the element this attachment is associated with * @var Group|null the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\UserSystem\Group', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\UserSystem\Group::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -52,12 +52,12 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class LabelAttachment extends Attachment class LabelAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = LabelProfile::class; final public const ALLOWED_ELEMENT_CLASS = LabelProfile::class;
/** /**
* @var LabelProfile the element this attachment is associated with * @var LabelProfile the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\LabelSystem\LabelProfile', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\LabelSystem\LabelProfile::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -33,12 +33,12 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class ManufacturerAttachment extends Attachment class ManufacturerAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = Manufacturer::class; final public const ALLOWED_ELEMENT_CLASS = Manufacturer::class;
/** /**
* @var Manufacturer|null the element this attachment is associated with * @var Manufacturer|null the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\Parts\Manufacturer', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\Parts\Manufacturer::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -34,11 +34,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class MeasurementUnitAttachment extends Attachment class MeasurementUnitAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = MeasurementUnit::class; final public const ALLOWED_ELEMENT_CLASS = MeasurementUnit::class;
/** /**
* @var Manufacturer|null the element this attachment is associated with * @var Manufacturer|null the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\Parts\MeasurementUnit', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\Parts\MeasurementUnit::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -33,11 +33,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class PartAttachment extends Attachment class PartAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = Part::class; final public const ALLOWED_ELEMENT_CLASS = Part::class;
/** /**
* @var Part the element this attachment is associated with * @var Part the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\Parts\Part', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\Parts\Part::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -33,11 +33,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class ProjectAttachment extends Attachment class ProjectAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = Project::class; final public const ALLOWED_ELEMENT_CLASS = Project::class;
/** /**
* @var Project|null the element this attachment is associated with * @var Project|null the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\ProjectSystem\Project', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\ProjectSystem\Project::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -33,12 +33,12 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class StorelocationAttachment extends Attachment class StorelocationAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = Storelocation::class; final public const ALLOWED_ELEMENT_CLASS = Storelocation::class;
/** /**
* @var Storelocation|null the element this attachment is associated with * @var Storelocation|null the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\Parts\Storelocation', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\Parts\Storelocation::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -33,12 +33,12 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class SupplierAttachment extends Attachment class SupplierAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = Supplier::class; final public const ALLOWED_ELEMENT_CLASS = Supplier::class;
/** /**
* @var Supplier|null the element this attachment is associated with * @var Supplier|null the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\Parts\Supplier', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\Parts\Supplier::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -33,12 +33,12 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity] #[ORM\Entity]
class UserAttachment extends Attachment class UserAttachment extends Attachment
{ {
public const ALLOWED_ELEMENT_CLASS = User::class; final public const ALLOWED_ELEMENT_CLASS = User::class;
/** /**
* @var User|null the element this attachment is associated with * @var User|null the element this attachment is associated with
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\UserSystem\User', inversedBy: 'attachments')] #[ORM\ManyToOne(targetEntity: \App\Entity\UserSystem\User::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')] #[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
protected ?AttachmentContainingDBElement $element = null; protected ?AttachmentContainingDBElement $element = null;
} }

View file

@ -35,8 +35,8 @@ use Symfony\Component\Serializer\Annotation\Groups;
* Every database table which are managed with this class (or a subclass of it) * Every database table which are managed with this class (or a subclass of it)
* must have the table row "id"!! The ID is the unique key to identify the elements. * must have the table row "id"!! The ID is the unique key to identify the elements.
*/ */
#[DiscriminatorMap(typeProperty: 'type', mapping: ['attachment_type' => 'App\Entity\Attachments\AttachmentType', 'attachment' => 'App\Entity\Attachments\Attachment', 'attachment_type_attachment' => 'App\Entity\Attachments\AttachmentTypeAttachment', 'category_attachment' => 'App\Entity\Attachments\CategoryAttachment', 'currency_attachment' => 'App\Entity\Attachments\CurrencyAttachment', 'footprint_attachment' => 'App\Entity\Attachments\FootprintAttachment', 'group_attachment' => 'App\Entity\Attachments\GroupAttachment', 'label_attachment' => 'App\Entity\Attachments\LabelAttachment', 'manufacturer_attachment' => 'App\Entity\Attachments\ManufacturerAttachment', 'measurement_unit_attachment' => 'App\Entity\Attachments\MeasurementUnitAttachment', 'part_attachment' => 'App\Entity\Attachments\PartAttachment', 'project_attachment' => 'App\Entity\Attachments\ProjectAttachment', 'storelocation_attachment' => 'App\Entity\Attachments\StorelocationAttachment', 'supplier_attachment' => 'App\Entity\Attachments\SupplierAttachment', 'user_attachment' => 'App\Entity\Attachments\UserAttachment', 'category' => 'App\Entity\Parts\Category', 'project' => 'App\Entity\ProjectSystem\Project', 'project_bom_entry' => 'App\Entity\ProjectSystem\ProjectBOMEntry', 'footprint' => 'App\Entity\Parts\Footprint', 'group' => 'App\Entity\UserSystem\Group', 'manufacturer' => 'App\Entity\Parts\Manufacturer', 'orderdetail' => 'App\Entity\PriceInformations\Orderdetail', 'part' => 'App\Entity\Parts\Part', 'pricedetail' => 'App\Entity\PriceInformation\Pricedetail', 'storelocation' => 'App\Entity\Parts\Storelocation', 'part_lot' => 'App\Entity\Parts\PartLot', 'currency' => 'App\Entity\PriceInformations\Currency', 'measurement_unit' => 'App\Entity\Parts\MeasurementUnit', 'parameter' => 'App\Entity\Parts\AbstractParameter', 'supplier' => 'App\Entity\Parts\Supplier', 'user' => 'App\Entity\UserSystem\User'])] #[DiscriminatorMap(typeProperty: 'type', mapping: ['attachment_type' => \App\Entity\Attachments\AttachmentType::class, 'attachment' => \App\Entity\Attachments\Attachment::class, 'attachment_type_attachment' => \App\Entity\Attachments\AttachmentTypeAttachment::class, 'category_attachment' => \App\Entity\Attachments\CategoryAttachment::class, 'currency_attachment' => \App\Entity\Attachments\CurrencyAttachment::class, 'footprint_attachment' => \App\Entity\Attachments\FootprintAttachment::class, 'group_attachment' => \App\Entity\Attachments\GroupAttachment::class, 'label_attachment' => \App\Entity\Attachments\LabelAttachment::class, 'manufacturer_attachment' => \App\Entity\Attachments\ManufacturerAttachment::class, 'measurement_unit_attachment' => \App\Entity\Attachments\MeasurementUnitAttachment::class, 'part_attachment' => \App\Entity\Attachments\PartAttachment::class, 'project_attachment' => \App\Entity\Attachments\ProjectAttachment::class, 'storelocation_attachment' => \App\Entity\Attachments\StorelocationAttachment::class, 'supplier_attachment' => \App\Entity\Attachments\SupplierAttachment::class, 'user_attachment' => \App\Entity\Attachments\UserAttachment::class, 'category' => \App\Entity\Parts\Category::class, 'project' => \App\Entity\ProjectSystem\Project::class, 'project_bom_entry' => \App\Entity\ProjectSystem\ProjectBOMEntry::class, 'footprint' => \App\Entity\Parts\Footprint::class, 'group' => \App\Entity\UserSystem\Group::class, 'manufacturer' => \App\Entity\Parts\Manufacturer::class, 'orderdetail' => \App\Entity\PriceInformations\Orderdetail::class, 'part' => \App\Entity\Parts\Part::class, 'pricedetail' => 'App\Entity\PriceInformation\Pricedetail', 'storelocation' => \App\Entity\Parts\Storelocation::class, 'part_lot' => \App\Entity\Parts\PartLot::class, 'currency' => \App\Entity\PriceInformations\Currency::class, 'measurement_unit' => \App\Entity\Parts\MeasurementUnit::class, 'parameter' => 'App\Entity\Parts\AbstractParameter', 'supplier' => \App\Entity\Parts\Supplier::class, 'user' => \App\Entity\UserSystem\User::class])]
#[ORM\MappedSuperclass(repositoryClass: 'App\Repository\DBElementRepository')] #[ORM\MappedSuperclass(repositoryClass: \App\Repository\DBElementRepository::class)]
abstract class AbstractDBElement implements JsonSerializable abstract class AbstractDBElement implements JsonSerializable
{ {
/** @var int|null The Identification number for this part. This value is unique for the element in this table. /** @var int|null The Identification number for this part. This value is unique for the element in this table.

View file

@ -33,7 +33,7 @@ use Symfony\Component\Validator\Constraints as Assert;
*/ */
#[ORM\MappedSuperclass(repositoryClass: 'App\Repository\NamedDBElement')] #[ORM\MappedSuperclass(repositoryClass: 'App\Repository\NamedDBElement')]
#[ORM\HasLifecycleCallbacks] #[ORM\HasLifecycleCallbacks]
abstract class AbstractNamedDBElement extends AbstractDBElement implements NamedElementInterface, TimeStampableInterface abstract class AbstractNamedDBElement extends AbstractDBElement implements NamedElementInterface, TimeStampableInterface, \Stringable
{ {
use TimestampTrait; use TimestampTrait;
@ -51,7 +51,7 @@ abstract class AbstractNamedDBElement extends AbstractDBElement implements Named
* *
******************************************************************************/ ******************************************************************************/
public function __toString() public function __toString(): string
{ {
return $this->getName(); return $this->getName();
} }

View file

@ -29,7 +29,7 @@ use Symfony\Component\Serializer\Annotation\Groups;
/** /**
* Class PartsContainingDBElement. * Class PartsContainingDBElement.
*/ */
#[ORM\MappedSuperclass(repositoryClass: 'App\Repository\AbstractPartsContainingRepository')] #[ORM\MappedSuperclass(repositoryClass: \App\Repository\AbstractPartsContainingRepository::class)]
abstract class AbstractPartsContainingDBElement extends AbstractStructuralDBElement abstract class AbstractPartsContainingDBElement extends AbstractStructuralDBElement
{ {
#[Groups(['full'])] #[Groups(['full'])]

View file

@ -46,8 +46,8 @@ use Symfony\Component\Serializer\Annotation\Groups;
* *
*/ */
#[UniqueEntity(fields: ['name', 'parent'], ignoreNull: false, message: 'structural.entity.unique_name')] #[UniqueEntity(fields: ['name', 'parent'], ignoreNull: false, message: 'structural.entity.unique_name')]
#[ORM\MappedSuperclass(repositoryClass: 'App\Repository\StructuralDBElementRepository')] #[ORM\MappedSuperclass(repositoryClass: \App\Repository\StructuralDBElementRepository::class)]
#[ORM\EntityListeners(['App\EntityListeners\TreeCacheInvalidationListener'])] #[ORM\EntityListeners([\App\EntityListeners\TreeCacheInvalidationListener::class])]
abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
{ {
use ParametersTrait; use ParametersTrait;
@ -105,7 +105,6 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
parent::__construct(); parent::__construct();
$this->children = new ArrayCollection(); $this->children = new ArrayCollection();
$this->parameters = new ArrayCollection(); $this->parameters = new ArrayCollection();
$this->parent = null;
} }
public function __clone() public function __clone()
@ -141,11 +140,11 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
//Check if both elements compared, are from the same type //Check if both elements compared, are from the same type
// (we have to check inheritance, or we get exceptions when using doctrine entities (they have a proxy type): // (we have to check inheritance, or we get exceptions when using doctrine entities (they have a proxy type):
if (!is_a($another_element, $class_name) && !is_a($this, get_class($another_element))) { if (!$another_element instanceof $class_name && !is_a($this, $another_element::class)) {
throw new InvalidArgumentException('isChildOf() only works for objects of the same type!'); throw new InvalidArgumentException('isChildOf() only works for objects of the same type!');
} }
if (null === $this->getParent()) { // this is the root node if (!$this->getParent() instanceof \App\Entity\Base\AbstractStructuralDBElement) { // this is the root node
return false; return false;
} }
@ -170,7 +169,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
*/ */
public function isRoot(): bool public function isRoot(): bool
{ {
return null === $this->parent; return !$this->parent instanceof \App\Entity\Base\AbstractStructuralDBElement;
} }
/****************************************************************************** /******************************************************************************
@ -213,9 +212,9 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
/* /*
* Only check for nodes that have a parent. In the other cases zero is correct. * Only check for nodes that have a parent. In the other cases zero is correct.
*/ */
if (0 === $this->level && null !== $this->parent) { if (0 === $this->level && $this->parent instanceof \App\Entity\Base\AbstractStructuralDBElement) {
$element = $this->parent; $element = $this->parent;
while (null !== $element) { while ($element instanceof \App\Entity\Base\AbstractStructuralDBElement) {
/** @var AbstractStructuralDBElement $element */ /** @var AbstractStructuralDBElement $element */
$element = $element->parent; $element = $element->parent;
++$this->level; ++$this->level;
@ -234,14 +233,14 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
*/ */
public function getFullPath(string $delimiter = self::PATH_DELIMITER_ARROW): string public function getFullPath(string $delimiter = self::PATH_DELIMITER_ARROW): string
{ {
if (empty($this->full_path_strings)) { if ($this->full_path_strings === []) {
$this->full_path_strings = []; $this->full_path_strings = [];
$this->full_path_strings[] = $this->getName(); $this->full_path_strings[] = $this->getName();
$element = $this; $element = $this;
$overflow = 20; //We only allow 20 levels depth $overflow = 20; //We only allow 20 levels depth
while (null !== $element->parent && $overflow >= 0) { while ($element->parent instanceof \App\Entity\Base\AbstractStructuralDBElement && $overflow >= 0) {
$element = $element->parent; $element = $element->parent;
$this->full_path_strings[] = $element->getName(); $this->full_path_strings[] = $element->getName();
//Decrement to prevent mem overflow. //Decrement to prevent mem overflow.
@ -328,7 +327,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
$this->parent = $new_parent; $this->parent = $new_parent;
//Add this element as child to the new parent //Add this element as child to the new parent
if (null !== $new_parent) { if ($new_parent instanceof \App\Entity\Base\AbstractStructuralDBElement) {
$new_parent->getChildren()->add($this); $new_parent->getChildren()->add($this);
} }

View file

@ -35,7 +35,7 @@ trait MasterAttachmentTrait
* @var Attachment|null * @var Attachment|null
*/ */
#[Assert\Expression('value == null or value.isPicture()', message: 'part.master_attachment.must_be_picture')] #[Assert\Expression('value == null or value.isPicture()', message: 'part.master_attachment.must_be_picture')]
#[ORM\ManyToOne(targetEntity: 'App\Entity\Attachments\Attachment')] #[ORM\ManyToOne(targetEntity: \App\Entity\Attachments\Attachment::class)]
#[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')] #[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')]
protected ?Attachment $master_picture_attachment = null; protected ?Attachment $master_picture_attachment = null;

View file

@ -47,11 +47,11 @@ use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Embeddable] #[ORM\Embeddable]
class LabelOptions class LabelOptions
{ {
public const BARCODE_TYPES = ['none', /*'ean8',*/ 'qr', 'code39', 'datamatrix', 'code93', 'code128']; final public const BARCODE_TYPES = ['none', /*'ean8',*/ 'qr', 'code39', 'datamatrix', 'code93', 'code128'];
public const SUPPORTED_ELEMENTS = ['part', 'part_lot', 'storelocation']; final public const SUPPORTED_ELEMENTS = ['part', 'part_lot', 'storelocation'];
public const PICTURE_TYPES = ['none', 'element_picture', 'main_attachment']; final public const PICTURE_TYPES = ['none', 'element_picture', 'main_attachment'];
public const LINES_MODES = ['html', 'twig']; final public const LINES_MODES = ['html', 'twig'];
/** /**
* @var float The page size of the label in mm * @var float The page size of the label in mm
@ -111,9 +111,6 @@ class LabelOptions
return $this->width; return $this->width;
} }
/**
* @return LabelOptions
*/
public function setWidth(float $width): self public function setWidth(float $width): self
{ {
$this->width = $width; $this->width = $width;
@ -126,9 +123,6 @@ class LabelOptions
return $this->height; return $this->height;
} }
/**
* @return LabelOptions
*/
public function setHeight(float $height): self public function setHeight(float $height): self
{ {
$this->height = $height; $this->height = $height;
@ -141,9 +135,6 @@ class LabelOptions
return $this->barcode_type; return $this->barcode_type;
} }
/**
* @return LabelOptions
*/
public function setBarcodeType(string $barcode_type): self public function setBarcodeType(string $barcode_type): self
{ {
$this->barcode_type = $barcode_type; $this->barcode_type = $barcode_type;
@ -156,9 +147,6 @@ class LabelOptions
return $this->picture_type; return $this->picture_type;
} }
/**
* @return LabelOptions
*/
public function setPictureType(string $picture_type): self public function setPictureType(string $picture_type): self
{ {
$this->picture_type = $picture_type; $this->picture_type = $picture_type;
@ -171,9 +159,6 @@ class LabelOptions
return $this->supported_element; return $this->supported_element;
} }
/**
* @return LabelOptions
*/
public function setSupportedElement(string $supported_element): self public function setSupportedElement(string $supported_element): self
{ {
$this->supported_element = $supported_element; $this->supported_element = $supported_element;
@ -186,9 +171,6 @@ class LabelOptions
return $this->lines; return $this->lines;
} }
/**
* @return LabelOptions
*/
public function setLines(string $lines): self public function setLines(string $lines): self
{ {
$this->lines = $lines; $this->lines = $lines;
@ -204,9 +186,6 @@ class LabelOptions
return $this->additional_css; return $this->additional_css;
} }
/**
* @return LabelOptions
*/
public function setAdditionalCss(string $additional_css): self public function setAdditionalCss(string $additional_css): self
{ {
$this->additional_css = $additional_css; $this->additional_css = $additional_css;
@ -219,9 +198,6 @@ class LabelOptions
return $this->lines_mode; return $this->lines_mode;
} }
/**
* @return LabelOptions
*/
public function setLinesMode(string $lines_mode): self public function setLinesMode(string $lines_mode): self
{ {
$this->lines_mode = $lines_mode; $this->lines_mode = $lines_mode;

View file

@ -49,15 +49,15 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Constraints as Assert;
#[UniqueEntity(['name', 'options.supported_element'])] #[UniqueEntity(['name', 'options.supported_element'])]
#[ORM\Entity(repositoryClass: 'App\Repository\LabelProfileRepository')] #[ORM\Entity(repositoryClass: \App\Repository\LabelProfileRepository::class)]
#[ORM\EntityListeners(['App\EntityListeners\TreeCacheInvalidationListener'])] #[ORM\EntityListeners([\App\EntityListeners\TreeCacheInvalidationListener::class])]
#[ORM\Table(name: 'label_profiles')] #[ORM\Table(name: 'label_profiles')]
class LabelProfile extends AttachmentContainingDBElement class LabelProfile extends AttachmentContainingDBElement
{ {
/** /**
* @var Collection<int, LabelAttachment> * @var Collection<int, LabelAttachment>
*/ */
#[ORM\OneToMany(targetEntity: 'App\Entity\Attachments\LabelAttachment', mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(targetEntity: \App\Entity\Attachments\LabelAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $attachments; protected Collection $attachments;
@ -126,8 +126,6 @@ class LabelProfile extends AttachmentContainingDBElement
/** /**
* Sets the show in dropdown menu. * Sets the show in dropdown menu.
*
* @return LabelProfile
*/ */
public function setShowInDropdown(bool $show_in_dropdown): self public function setShowInDropdown(bool $show_in_dropdown): self
{ {

View file

@ -61,14 +61,14 @@ use App\Repository\LogEntryRepository;
#[ORM\Index(columns: ['datetime'], name: 'log_idx_datetime')] #[ORM\Index(columns: ['datetime'], name: 'log_idx_datetime')]
abstract class AbstractLogEntry extends AbstractDBElement abstract class AbstractLogEntry extends AbstractDBElement
{ {
public const LEVEL_EMERGENCY = 0; final public const LEVEL_EMERGENCY = 0;
public const LEVEL_ALERT = 1; final public const LEVEL_ALERT = 1;
public const LEVEL_CRITICAL = 2; final public const LEVEL_CRITICAL = 2;
public const LEVEL_ERROR = 3; final public const LEVEL_ERROR = 3;
public const LEVEL_WARNING = 4; final public const LEVEL_WARNING = 4;
public const LEVEL_NOTICE = 5; final public const LEVEL_NOTICE = 5;
public const LEVEL_INFO = 6; final public const LEVEL_INFO = 6;
public const LEVEL_DEBUG = 7; final public const LEVEL_DEBUG = 7;
protected const TARGET_TYPE_NONE = 0; protected const TARGET_TYPE_NONE = 0;
protected const TARGET_TYPE_USER = 1; protected const TARGET_TYPE_USER = 1;
@ -129,7 +129,7 @@ abstract class AbstractLogEntry extends AbstractDBElement
/** @var User|null The user which has caused this log entry /** @var User|null The user which has caused this log entry
*/ */
#[ORM\ManyToOne(targetEntity: 'App\Entity\UserSystem\User', fetch: 'EAGER')] #[ORM\ManyToOne(targetEntity: \App\Entity\UserSystem\User::class, fetch: 'EAGER')]
#[ORM\JoinColumn(name: 'id_user', onDelete: 'SET NULL')] #[ORM\JoinColumn(name: 'id_user', onDelete: 'SET NULL')]
protected ?User $user = null; protected ?User $user = null;
@ -147,7 +147,7 @@ abstract class AbstractLogEntry extends AbstractDBElement
/** @var int The priority level of the associated level. 0 is highest, 7 lowest /** @var int The priority level of the associated level. 0 is highest, 7 lowest
*/ */
#[ORM\Column(type: 'tinyint', name: 'level')] #[ORM\Column(type: 'tinyint', name: 'level')]
protected int $level; protected int $level = self::LEVEL_WARNING;
/** @var int The ID of the element targeted by this event /** @var int The ID of the element targeted by this event
*/ */
@ -173,7 +173,6 @@ abstract class AbstractLogEntry extends AbstractDBElement
public function __construct() public function __construct()
{ {
$this->timestamp = new DateTime(); $this->timestamp = new DateTime();
$this->level = self::LEVEL_WARNING;
} }
/** /**
@ -214,7 +213,6 @@ abstract class AbstractLogEntry extends AbstractDBElement
* Marks this log entry as a CLI entry, and set the username of the CLI user. * Marks this log entry as a CLI entry, and set the username of the CLI user.
* This removes the association to a user object in database, as CLI users are not really related to logged in * This removes the association to a user object in database, as CLI users are not really related to logged in
* Part-DB users. * Part-DB users.
* @param string $cli_username
* @return $this * @return $this
*/ */
public function setCLIUsername(string $cli_username): self public function setCLIUsername(string $cli_username): self
@ -372,14 +370,14 @@ abstract class AbstractLogEntry extends AbstractDBElement
*/ */
public function setTargetElement(?AbstractDBElement $element): self public function setTargetElement(?AbstractDBElement $element): self
{ {
if (null === $element) { if (!$element instanceof \App\Entity\Base\AbstractDBElement) {
$this->target_id = 0; $this->target_id = 0;
$this->target_type = self::TARGET_TYPE_NONE; $this->target_type = self::TARGET_TYPE_NONE;
return $this; return $this;
} }
$this->target_type = static::targetTypeClassToID(get_class($element)); $this->target_type = static::targetTypeClassToID($element::class);
$this->target_id = $element->getID(); $this->target_id = $element->getID();
return $this; return $this;

View file

@ -93,11 +93,9 @@ class CollectionElementDeleted extends AbstractLogEntry implements LogWithEventU
public function __construct(AbstractDBElement $changed_element, string $collection_name, AbstractDBElement $deletedElement) public function __construct(AbstractDBElement $changed_element, string $collection_name, AbstractDBElement $deletedElement)
{ {
parent::__construct(); parent::__construct();
$this->level = self::LEVEL_INFO;
$this->setTargetElement($changed_element); $this->setTargetElement($changed_element);
$this->extra['n'] = $collection_name; $this->extra['n'] = $collection_name;
$this->extra['c'] = self::targetTypeClassToID(get_class($deletedElement)); $this->extra['c'] = self::targetTypeClassToID($deletedElement::class);
$this->extra['i'] = $deletedElement->getID(); $this->extra['i'] = $deletedElement->getID();
if ($deletedElement instanceof NamedElementInterface) { if ($deletedElement instanceof NamedElementInterface) {
$this->extra['o'] = $deletedElement->getName(); $this->extra['o'] = $deletedElement->getName();
@ -141,8 +139,6 @@ class CollectionElementDeleted extends AbstractLogEntry implements LogWithEventU
/** /**
* This functions maps an abstract class name derived from the extra c element to an instantiable class name (based on the target element of this log entry). * This functions maps an abstract class name derived from the extra c element to an instantiable class name (based on the target element of this log entry).
* For example if the target element is a part and the extra c element is "App\Entity\Attachments\Attachment", this function will return "App\Entity\Attachments\PartAttachment". * For example if the target element is a part and the extra c element is "App\Entity\Attachments\Attachment", this function will return "App\Entity\Attachments\PartAttachment".
* @param string $abstract_class
* @return string
*/ */
private function resolveAbstractClassToInstantiableClass(string $abstract_class): string private function resolveAbstractClassToInstantiableClass(string $abstract_class): string
{ {

Some files were not shown because too many files have changed in this diff Show more