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
$rectorConfig->sets([
//PHP rules
//SetList::CODE_QUALITY,
//LevelSetList::UP_TO_PHP_81,
SetList::CODE_QUALITY,
LevelSetList::UP_TO_PHP_81,
//Symfony rules
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).')]
class CleanAttachmentsCommand extends Command
{
protected AttachmentManager $attachment_helper;
protected AttachmentReverseSearch $reverseSearch;
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();
parent::__construct();
}
@ -88,7 +82,7 @@ class CleanAttachmentsCommand extends Command
foreach ($finder as $file) {
//If not attachment object uses this file, print it
if (0 === count($this->reverseSearch->findAttachmentsByFile($file))) {
if ([] === $this->reverseSearch->findAttachmentsByFile($file)) {
$file_list[] = $file;
$table->addRow([
$fs->makePathRelative($file->getPathname(), $mediaPath),
@ -98,7 +92,7 @@ class CleanAttachmentsCommand extends Command
}
}
if (count($file_list) > 0) {
if ($file_list !== []) {
$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);

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')]
class BackupCommand extends Command
{
private string $project_dir;
private EntityManagerInterface $entityManager;
public function __construct(string $project_dir, EntityManagerInterface $entityManager)
public function __construct(private readonly string $project_dir, private readonly EntityManagerInterface $entityManager)
{
$this->project_dir = $project_dir;
$this->entityManager = $entityManager;
parent::__construct();
}
@ -69,13 +63,10 @@ class BackupCommand extends Command
$io->info('Backup Part-DB to '.$output_filepath);
//Check if the file already exists
if (file_exists($output_filepath)) {
//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)) {
$io->error('Backup aborted!');
return Command::FAILURE;
}
//Then ask the user, if he wants to overwrite the file
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!');
return Command::FAILURE;
}
$io->note('Starting backup...');
@ -113,8 +104,6 @@ class BackupCommand extends Command
/**
* Constructs the MySQL PDO DSN.
* 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
{

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.')]
class CheckRequirementsCommand extends Command
{
protected ContainerBagInterface $params;
public function __construct(ContainerBagInterface $params)
public function __construct(protected ContainerBagInterface $params)
{
$this->params = $params;
parent::__construct();
}
@ -66,40 +63,48 @@ class CheckRequirementsCommand extends Command
protected function checkPHP(SymfonyStyle $io, $only_issues = false): void
{
//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
if (PHP_VERSION_ID < 80200) {
$io->warning('You are using PHP '. PHP_VERSION .'. This will work, but a newer version is recommended.');
} else {
!$only_issues && $io->success('PHP version is sufficient.');
} elseif (!$only_issues) {
$io->success('PHP version is sufficient.');
}
//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';
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');
} else {
!$only_issues && $io->success('Opcache is enabled.');
} elseif (!$only_issues) {
$io->success('Opcache is enabled.');
}
//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)) {
$io->warning('Opcache configuration can be improved. See https://symfony.com/doc/current/performance.html for more info.');
} else {
!$only_issues && $io->success('Opcache configuration is already performance optimized.');
} elseif (!$only_issues) {
$io->success('Opcache configuration is already performance optimized.');
}
}
protected function checkPartDBConfig(SymfonyStyle $io, $only_issues = false): void
{
//Check if APP_ENV is set to prod
$io->isVerbose() && $io->comment('Checking debug mode...');
if($this->params->get('kernel.debug')) {
if ($io->isVerbose()) {
$io->comment('Checking debug mode...');
}
if ($this->params->get('kernel.debug')) {
$io->warning('You have activated debug mode, this is will leak informations in a production environment.');
} else {
!$only_issues && $io->success('Debug mode disabled.');
} elseif (!$only_issues) {
$io->success('Debug mode disabled.');
}
}
@ -108,61 +113,71 @@ class CheckRequirementsCommand extends Command
{
//Get all installed PHP 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;
if(!in_array('pdo_mysql', $extensions)) {
$io->error('pdo_mysql is not installed. You will not be able to use MySQL databases.');
} 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++;
}
if(!in_array('pdo_sqlite', $extensions)) {
$io->error('pdo_sqlite is not installed. You will not be able to use SQLite. databases');
} 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++;
}
$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) {
$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');
} else {
!$only_issues && $io->success('PHP extension curl is installed.');
} elseif (!$only_issues) {
$io->success('PHP extension curl is installed.');
}
$gd_installed = in_array('gd', $extensions);
if(!$gd_installed) {
if (!$gd_installed) {
$io->error('GD is not installed. GD is required for image processing.');
} else {
!$only_issues && $io->success('PHP extension GD is installed.');
} elseif (!$only_issues) {
$io->success('PHP extension GD is installed.');
}
//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) {
$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.');
} else {
!$only_issues && $io->success('GD has jpeg support.');
} elseif (!$only_issues) {
$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.');
} else {
!$only_issues && $io->success('GD has png support.');
} elseif (!$only_issues) {
$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.');
} else {
!$only_issues && $io->success('GD has WebP support.');
} elseif (!$only_issues) {
$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.')]
class UpdateExchangeRatesCommand extends Command
{
protected string $base_current;
protected EntityManagerInterface $em;
protected ExchangeRateUpdater $exchangeRateUpdater;
public function __construct(string $base_current, EntityManagerInterface $entityManager, ExchangeRateUpdater $exchangeRateUpdater)
public function __construct(protected string $base_current, protected EntityManagerInterface $em, protected ExchangeRateUpdater $exchangeRateUpdater)
{
//$this->swap = $swap;
$this->base_current = $base_current;
$this->em = $entityManager;
$this->exchangeRateUpdater = $exchangeRateUpdater;
parent::__construct();
}
@ -75,11 +65,7 @@ class UpdateExchangeRatesCommand extends Command
$iso_code = $input->getArgument('iso_code');
$repo = $this->em->getRepository(Currency::class);
if (!empty($iso_code)) {
$candidates = $repo->findBy(['iso_code' => $iso_code]);
} else {
$candidates = $repo->findAll();
}
$candidates = empty($iso_code) ? $repo->findAll() : $repo->findBy(['iso_code' => $iso_code]);
$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.')]
class ShowEventLogCommand extends Command
{
protected EntityManagerInterface $entityManager;
protected TranslatorInterface $translator;
protected ElementTypeNameGenerator $elementTypeNameGenerator;
protected LogEntryRepository $repo;
protected LogEntryExtraFormatter $formatter;
public function __construct(EntityManagerInterface $entityManager,
TranslatorInterface $translator, ElementTypeNameGenerator $elementTypeNameGenerator, LogEntryExtraFormatter $formatter)
public function __construct(protected EntityManagerInterface $entityManager,
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);
parent::__construct();
}
@ -145,14 +136,12 @@ class ShowEventLogCommand extends Command
$target_class = $this->elementTypeNameGenerator->getLocalizedTypeLabel($entry->getTargetClass());
}
if ($entry->getUser()) {
if ($entry->getUser() instanceof \App\Entity\UserSystem\User) {
$user = $entry->getUser()->getFullName(true);
} elseif ($entry->isCLIEntry()) {
$user = $entry->getCLIUsername() . ' [CLI]';
} else {
if ($entry->isCLIEntry()) {
$user = $entry->getCLIUsername() . ' [CLI]';
} else {
$user = $entry->getUsername() . ' [deleted]';
}
$user = $entry->getUsername() . ' [deleted]';
}
$row = [

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
*/
protected const BBCODE_REGEX = '/\\[.+\\].*\\[\\/.+\\]/';
protected EntityManagerInterface $em;
protected PropertyAccessorInterface $propertyAccessor;
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();
parent::__construct();
@ -126,25 +120,25 @@ class ConvertBBCodeCommand extends Command
//Fetch resulting classes
$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
foreach ($results as $result) {
/** @var AbstractNamedDBElement $result */
$io->writeln(
'Convert entity: '.$result->getName().' ('.get_class($result).': '.$result->getID().')',
'Convert entity: '.$result->getName().' ('.$result::class.': '.$result->getID().')',
OutputInterface::VERBOSITY_VERBOSE
);
foreach ($properties as $property) {
//Retrieve bbcode from entity
$bbcode = $this->propertyAccessor->getValue($result, $property);
//Check if the current property really contains BBCode
if (!preg_match(static::BBCODE_REGEX, $bbcode)) {
if (!preg_match(static::BBCODE_REGEX, (string) $bbcode)) {
continue;
}
$io->writeln(
'BBCode (old): '
.str_replace('\n', ' ', substr($bbcode, 0, 255)),
.str_replace('\n', ' ', substr((string) $bbcode, 0, 255)),
OutputInterface::VERBOSITY_VERY_VERBOSE
);
$markdown = $this->converter->convert($bbcode);

View file

@ -37,24 +37,11 @@ use Symfony\Component\Console\Style\SymfonyStyle;
class ImportPartKeeprCommand extends Command
{
protected EntityManagerInterface $em;
protected MySQLDumpXMLConverter $xml_converter;
protected PKDatastructureImporter $datastructureImporter;
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)
public function __construct(protected EntityManagerInterface $em, protected MySQLDumpXMLConverter $xml_converter,
protected PKDatastructureImporter $datastructureImporter, protected PKPartImporter $partImporter, protected PKImportHelper $importHelper,
protected PKOptionalImporter $optionalImporter)
{
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()

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)')]
class ConvertToSAMLUserCommand extends Command
{
protected EntityManagerInterface $entityManager;
protected bool $saml_enabled;
public function __construct(EntityManagerInterface $entityManager, bool $saml_enabled)
public function __construct(protected EntityManagerInterface $entityManager, protected bool $saml_enabled)
{
parent::__construct();
$this->entityManager = $entityManager;
$this->saml_enabled = $saml_enabled;
}
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')]
class SetPasswordCommand extends Command
{
protected EntityManagerInterface $entityManager;
protected UserPasswordHasherInterface $encoder;
protected EventDispatcherInterface $eventDispatcher;
public function __construct(EntityManagerInterface $entityManager, UserPasswordHasherInterface $passwordEncoder, EventDispatcherInterface $eventDispatcher)
public function __construct(protected EntityManagerInterface $entityManager, protected UserPasswordHasherInterface $encoder, protected EventDispatcherInterface $eventDispatcher)
{
$this->entityManager = $entityManager;
$this->encoder = $passwordEncoder;
$this->eventDispatcher = $eventDispatcher;
parent::__construct();
}
@ -64,7 +56,7 @@ class SetPasswordCommand extends Command
$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));
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.')]
final class UpgradePermissionsSchemaCommand extends Command
{
private PermissionSchemaUpdater $permissionSchemaUpdater;
private EntityManagerInterface $em;
private EventCommentHelper $eventCommentHelper;
public function __construct(PermissionSchemaUpdater $permissionSchemaUpdater, EntityManagerInterface $entityManager, EventCommentHelper $eventCommentHelper)
public function __construct(private readonly PermissionSchemaUpdater $permissionSchemaUpdater, private readonly EntityManagerInterface $em, private readonly EventCommentHelper $eventCommentHelper)
{
parent::__construct(self::$defaultName);
$this->permissionSchemaUpdater = $permissionSchemaUpdater;
$this->eventCommentHelper = $eventCommentHelper;
$this->em = $entityManager;
}
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.');
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.');
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
$io->section('Groups that need an update:');
$io->listing(array_map(static function (Group $group) {
return $group->getName() . ' (ID: '. $group->getID() .', Current version: ' . $group->getPermissions()->getSchemaVersion() . ')';
}, $groups_to_upgrade));
$io->listing(array_map(static fn(Group $group): string => $group->getName() . ' (ID: '. $group->getID() .', Current version: ' . $group->getPermissions()->getSchemaVersion() . ')', $groups_to_upgrade));
$io->section('Users that need an update:');
$io->listing(array_map(static function (User $user) {
return $user->getUsername() . ' (ID: '. $user->getID() .', Current version: ' . $user->getPermissions()->getSchemaVersion() . ')';
}, $users_to_upgrade));
$io->listing(array_map(static fn(User $user): string => $user->getUsername() . ' (ID: '. $user->getID() .', Current version: ' . $user->getPermissions()->getSchemaVersion() . ')', $users_to_upgrade));
if(!$io->confirm('Continue with the update?', false)) {
$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')]
class UserEnableCommand extends Command
{
protected EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager, string $name = null)
public function __construct(protected EntityManagerInterface $entityManager, string $name = null)
{
$this->entityManager = $entityManager;
parent::__construct($name);
}
@ -70,7 +66,7 @@ class UserEnableCommand extends Command
} else { //Otherwise, fetch the users from DB
foreach ($usernames as $username) {
$user = $repo->findByEmailOrName($username);
if ($user === null) {
if (!$user instanceof \App\Entity\UserSystem\User) {
$io->error('No user found with username: '.$username);
return self::FAILURE;
}
@ -84,9 +80,7 @@ class UserEnableCommand extends Command
$io->note('The following users will be enabled:');
}
$io->table(['Username', 'Enabled/Disabled'],
array_map(static function(User $user) {
return [$user->getFullName(true), $user->isDisabled() ? 'Disabled' : 'Enabled'];
}, $users));
array_map(static fn(User $user) => [$user->getFullName(true), $user->isDisabled() ? 'Disabled' : 'Enabled'], $users));
if(!$io->confirm('Do you want to continue?')) {
$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')]
class UserListCommand extends Command
{
protected EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
public function __construct(protected EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
parent::__construct();
}
@ -83,7 +79,7 @@ class UserListCommand extends Command
$user->getUsername(),
$user->getFullName(),
$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->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')]
class UsersPermissionsCommand extends Command
{
protected EntityManagerInterface $entityManager;
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->permissionResolver = $permissionResolver;
$this->translator = $translator;
parent::__construct(self::$defaultName);
}
@ -71,7 +65,7 @@ class UsersPermissionsCommand extends Command
//Find user
$io->note('Finding user with username: ' . $username);
$user = $this->userRepository->findByEmailOrName($username);
if ($user === null) {
if (!$user instanceof \App\Entity\UserSystem\User) {
$io->error('No user found with username: ' . $username);
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)');
switch (strtolower($new_value_str)) {
switch (strtolower((string) $new_value_str)) {
case 'a':
case 'allow':
$new_value = true;
@ -207,11 +201,11 @@ class UsersPermissionsCommand extends Command
if ($permission_value === true) {
return '<fg=green>Allow</>';
} else if ($permission_value === false) {
} elseif ($permission_value === false) {
return '<fg=red>Disallow</>';
} else if ($permission_value === null && !$inherit) {
} elseif ($permission_value === null && !$inherit) {
return '<fg=blue>Inherit</>';
} else if ($permission_value === null && $inherit) {
} elseif ($permission_value === null && $inherit) {
return '<fg=red>Disallow (Inherited)</>';
}

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.')]
class VersionCommand extends Command
{
protected VersionManagerInterface $versionManager;
protected GitVersionInfo $gitVersionInfo;
public function __construct(VersionManagerInterface $versionManager, GitVersionInfo $gitVersionInfo)
public function __construct(protected VersionManagerInterface $versionManager, protected GitVersionInfo $gitVersionInfo)
{
$this->versionManager = $versionManager;
$this->gitVersionInfo = $gitVersionInfo;
parent::__construct();
}

View file

@ -72,29 +72,16 @@ abstract class BaseAdminController extends AbstractController
protected string $route_base = '';
protected string $attachment_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
*/
protected $eventDispatcher;
protected LabelGenerator $labelGenerator;
protected LabelExampleElementsGenerator $barcodeExampleGenerator;
protected EntityManagerInterface $entityManager;
public function __construct(TranslatorInterface $translator, UserPasswordHasherInterface $passwordEncoder,
AttachmentSubmitHandler $attachmentSubmitHandler,
EventCommentHelper $commentHelper, HistoryHelper $historyHelper, TimeTravel $timeTravel,
DataTableFactory $dataTableFactory, EventDispatcherInterface $eventDispatcher, LabelExampleElementsGenerator $barcodeExampleGenerator,
LabelGenerator $labelGenerator, EntityManagerInterface $entityManager)
public function __construct(protected TranslatorInterface $translator, protected UserPasswordHasherInterface $passwordEncoder,
protected AttachmentSubmitHandler $attachmentSubmitHandler,
protected EventCommentHelper $commentHelper, protected HistoryHelper $historyHelper, protected TimeTravel $timeTravel,
protected DataTableFactory $dataTableFactory, EventDispatcherInterface $eventDispatcher, protected LabelExampleElementsGenerator $barcodeExampleGenerator,
protected LabelGenerator $labelGenerator, protected EntityManagerInterface $entityManager)
{
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!');
@ -107,18 +94,7 @@ abstract class BaseAdminController extends AbstractController
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!');
}
$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->barcodeExampleGenerator = $barcodeExampleGenerator;
$this->labelGenerator = $labelGenerator;
$this->entityManager = $entityManager;
}
protected function revertElementIfNeeded(AbstractDBElement $entity, ?string $timestamp): ?DateTime
@ -177,7 +153,7 @@ abstract class BaseAdminController extends AbstractController
$form_options = [
'attachment_class' => $this->attachment_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...
@ -269,13 +245,7 @@ abstract class BaseAdminController extends AbstractController
protected function _new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?AbstractNamedDBElement $entity = null)
{
if (null === $entity) {
/** @var AbstractStructuralDBElement|User $new_entity */
$new_entity = new $this->entity_class();
} else {
/** @var AbstractStructuralDBElement|User $new_entity */
$new_entity = clone $entity;
}
$new_entity = $entity instanceof \App\Entity\Base\AbstractNamedDBElement ? clone $entity : new $this->entity_class();
$this->denyAccessUnlessGranted('read', $new_entity);
@ -287,42 +257,37 @@ abstract class BaseAdminController extends AbstractController
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//Perform additional actions
if ($this->additionalActionNew($form, $new_entity)) {
//Upload passed files
$attachments = $form['attachments'];
foreach ($attachments as $attachment) {
/** @var FormInterface $attachment */
$options = [
'secure_attachment' => $attachment['secureFile']->getData(),
'download_url' => $attachment['downloadURL']->getData(),
];
//Perform additional actions
if ($form->isSubmitted() && $form->isValid() && $this->additionalActionNew($form, $new_entity)) {
//Upload passed files
$attachments = $form['attachments'];
foreach ($attachments as $attachment) {
/** @var FormInterface $attachment */
$options = [
'secure_attachment' => $attachment['secureFile']->getData(),
'download_url' => $attachment['downloadURL']->getData(),
];
try {
$this->attachmentSubmitHandler->handleFormSubmit(
$attachment->getData(),
$attachment['file']->getData(),
$options
);
} catch (AttachmentDownloadException $attachmentDownloadException) {
$this->addFlash(
'error',
$this->translator->trans(
'attachment.download_failed'
).' '.$attachmentDownloadException->getMessage()
);
}
try {
$this->attachmentSubmitHandler->handleFormSubmit(
$attachment->getData(),
$attachment['file']->getData(),
$options
);
} catch (AttachmentDownloadException $attachmentDownloadException) {
$this->addFlash(
'error',
$this->translator->trans(
'attachment.download_failed'
).' '.$attachmentDownloadException->getMessage()
);
}
$this->commentHelper->setMessage($form['log_comment']->getData());
$em->persist($new_entity);
$em->flush();
$this->addFlash('success', 'entity.created_flash');
return $this->redirectToRoute($this->route_base.'_edit', ['id' => $new_entity->getID()]);
}
$this->commentHelper->setMessage($form['log_comment']->getData());
$em->persist($new_entity);
$em->flush();
$this->addFlash('success', 'entity.created_flash');
return $this->redirectToRoute($this->route_base.'_edit', ['id' => $new_entity->getID()]);
}
if ($form->isSubmitted() && !$form->isValid()) {
@ -369,7 +334,7 @@ abstract class BaseAdminController extends AbstractController
}
}
}
catch (UnexpectedValueException $e) {
catch (UnexpectedValueException) {
$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 $parameter_class = CurrencyParameter::class;
protected ExchangeRateUpdater $exchangeRateUpdater;
public function __construct(
TranslatorInterface $translator,
UserPasswordHasherInterface $passwordEncoder,
@ -78,10 +76,8 @@ class CurrencyController extends BaseAdminController
LabelExampleElementsGenerator $barcodeExampleGenerator,
LabelGenerator $labelGenerator,
EntityManagerInterface $entityManager,
ExchangeRateUpdater $exchangeRateUpdater
protected ExchangeRateUpdater $exchangeRateUpdater
) {
$this->exchangeRateUpdater = $exchangeRateUpdater;
parent::__construct(
$translator,
$passwordEncoder,

View file

@ -45,9 +45,6 @@ class ManufacturerController extends BaseAdminController
protected string $attachment_class = ManufacturerAttachment::class;
protected ?string $parameter_class = ManufacturerParameter::class;
/**
* @return RedirectResponse
*/
#[Route(path: '/{id}', name: 'manufacturer_delete', methods: ['DELETE'])]
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
{
protected CacheInterface $cache;
protected KernelInterface $kernel;
protected DataTableFactory $dataTable;
public function __construct(CacheInterface $cache, KernelInterface $kernel, DataTableFactory $dataTable)
public function __construct(protected CacheInterface $cache, protected KernelInterface $kernel, protected DataTableFactory $dataTable)
{
$this->cache = $cache;
$this->kernel = $kernel;
$this->dataTable = $dataTable;
}
public function getBanner(): string

View file

@ -62,20 +62,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
#[Route(path: '/label')]
class LabelController extends AbstractController
{
protected LabelGenerator $labelGenerator;
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)
public function __construct(protected LabelGenerator $labelGenerator, protected EntityManagerInterface $em, protected ElementTypeNameGenerator $elementTypeNameGenerator, protected RangeParser $rangeParser, protected TranslatorInterface $translator)
{
$this->labelGenerator = $labelGenerator;
$this->em = $em;
$this->elementTypeNameGenerator = $elementTypeNameGenerator;
$this->rangeParser = $rangeParser;
$this->translator = $translator;
}
#[Route(path: '/dialog', name: 'label_dialog')]
@ -85,15 +73,11 @@ class LabelController extends AbstractController
$this->denyAccessUnlessGranted('@labels.create_labels');
//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);
}
if ($profile) {
$label_options = $profile->getOptions();
} else {
$label_options = new LabelOptions();
}
$label_options = $profile instanceof \App\Entity\LabelSystem\LabelProfile ? $profile->getOptions() : new LabelOptions();
//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');
@ -107,7 +91,7 @@ class LabelController extends AbstractController
$target_id = $request->query->get('target_id', null);
$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);
}
if (is_string($target_id)) {
@ -124,10 +108,10 @@ class LabelController extends AbstractController
$filename = 'invalid.pdf';
//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();
$targets = $this->findObjects($form_options->getSupportedElement(), $target_id);
if (!empty($targets)) {
if ($targets !== []) {
try {
$pdf_data = $this->labelGenerator->generateLabel($form_options, $targets);
$filename = $this->getLabelName($targets[0], $profile);

View file

@ -52,20 +52,13 @@ use Symfony\Component\Routing\Annotation\Route;
#[Route(path: '/log')]
class LogController extends AbstractController
{
protected EntityManagerInterface $entityManager;
protected TimeTravel $timeTravel;
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);
}
/**
* @return Response
*/
#[Route(path: '/', name: 'log_view')]
public function showLogs(Request $request, DataTableFactory $dataTable): Response
{
@ -96,8 +89,6 @@ class LogController extends AbstractController
/**
* @param Request $request
* @param AbstractLogEntry $logEntry
* @return Response
*/
#[Route(path: '/{id}/details', name: 'log_details')]
public function logDetails(AbstractLogEntry $logEntry, LogEntryExtraFormatter $logEntryExtraFormatter,
@ -150,7 +141,7 @@ class LogController extends AbstractController
}
$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!');
}

View file

@ -62,16 +62,8 @@ use function Symfony\Component\Translation\t;
#[Route(path: '/part')]
class PartController extends AbstractController
{
protected PricedetailHelper $pricedetailHelper;
protected PartPreviewGenerator $partPreviewGenerator;
protected EventCommentHelper $commentHelper;
public function __construct(PricedetailHelper $pricedetailHelper,
PartPreviewGenerator $partPreviewGenerator, EventCommentHelper $commentHelper)
public function __construct(protected PricedetailHelper $pricedetailHelper, protected PartPreviewGenerator $partPreviewGenerator, protected 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
{
if ($part) { //Clone part
if ($part instanceof \App\Entity\Parts\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
if ($project->getBuildPart() !== null) {
if ($project->getBuildPart() instanceof \App\Entity\Parts\Part) {
$this->addFlash('error', 'part.new_build_part.error.build_part_already_exists');
return $this->redirectToRoute('part_edit', ['id' => $project->getBuildPart()->getID()]);
}
@ -235,7 +229,7 @@ class PartController extends AbstractController
$cid = $request->get('category', 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->setDescription($category->getDefaultDescription());
$new_part->setComment($category->getDefaultComment());
@ -243,19 +237,19 @@ class PartController extends AbstractController
$fid = $request->get('footprint', 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);
}
$mid = $request->get('manufacturer', 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);
}
$store_id = $request->get('storelocation', 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->setStorageLocation($storelocation);
$partLot->setInstockUnknown(true);
@ -264,7 +258,7 @@ class PartController extends AbstractController
$supplier_id = $request->get('supplier', 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->setSupplier($supplier);
$new_part->addOrderdetail($orderdetail);
@ -335,7 +329,7 @@ class PartController extends AbstractController
if ($this->isCsrfTokenValid('part_withraw' . $part->getID(), $request->request->get('_csfr'))) {
//Retrieve partlot from the request
$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!');
}
//Ensure that the partlot belongs to the part
@ -375,7 +369,7 @@ class PartController extends AbstractController
default:
throw new \RuntimeException("Unknown action!");
}
} catch (AccessDeniedException $exception) {
} catch (AccessDeniedException) {
$this->addFlash('error', t('part.withdraw.access_denied'));
goto err;
}

View file

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

View file

@ -48,18 +48,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class PartListsController extends AbstractController
{
private EntityManagerInterface $entityManager;
private NodesListBuilder $nodesListBuilder;
private DataTableFactory $dataTableFactory;
private TranslatorInterface $translator;
public function __construct(EntityManagerInterface $entityManager, NodesListBuilder $nodesListBuilder, DataTableFactory $dataTableFactory, TranslatorInterface $translator)
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly NodesListBuilder $nodesListBuilder, private readonly DataTableFactory $dataTableFactory, private readonly TranslatorInterface $translator)
{
$this->entityManager = $entityManager;
$this->nodesListBuilder = $nodesListBuilder;
$this->dataTableFactory = $dataTableFactory;
$this->translator = $translator;
}
#[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.
* @param FormInterface $form
* @return void
*/
private function disableFormFieldAfterCreation(FormInterface $form, bool $disabled = true): void
{
@ -109,12 +97,12 @@ class PartListsController extends AbstractController
$attrs['disabled'] = $disabled;
$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!');
}
$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 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
* @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
{
@ -172,9 +159,6 @@ class PartListsController extends AbstractController
], $additonal_template_vars));
}
/**
* @return JsonResponse|Response
*/
#[Route(path: '/category/{id}/parts', name: 'part_list_category')]
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')]
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')]
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')]
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')]
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' => '.*'])]
public function showTag(string $tag, Request $request): Response
{
@ -321,9 +290,6 @@ class PartListsController extends AbstractController
return $filter;
}
/**
* @return JsonResponse|Response
*/
#[Route(path: '/parts/search', name: 'parts_search')]
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')]
public function showAll(Request $request): Response
{

View file

@ -50,11 +50,8 @@ use function Symfony\Component\Translation\t;
#[Route(path: '/project')]
class ProjectController extends AbstractController
{
private DataTableFactory $dataTableFactory;
public function __construct(DataTableFactory $dataTableFactory)
public function __construct(private readonly DataTableFactory $dataTableFactory)
{
$this->dataTableFactory = $dataTableFactory;
}
#[Route(path: '/{id}/info', name: 'project_info', requirements: ['id' => '\d+'])]
@ -180,9 +177,7 @@ class ProjectController extends AbstractController
if (count ($errors) > 0) {
$this->addFlash('error', t('project.bom_import.flash.invalid_entries'));
}
} catch (\UnexpectedValueException $e) {
$this->addFlash('error', t('project.bom_import.flash.invalid_file', ['%message%' => $e->getMessage()]));
} catch (SyntaxError $e) {
} catch (\UnexpectedValueException|SyntaxError $e) {
$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: '/{id}/add_parts', name: 'project_add_parts', requirements: ['id' => '\d+'])]
public function addPart(Request $request, EntityManagerInterface $entityManager, ?Project $project): Response
{
if($project) {
if($project instanceof \App\Entity\ProjectSystem\Project) {
$this->denyAccessUnlessGranted('edit', $project);
} else {
$this->denyAccessUnlessGranted('@projects.edit');
@ -212,7 +203,7 @@ class ProjectController extends AbstractController
$builder->add('project', StructuralEntityType::class, [
'class' => Project::class,
'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,
'constraints' => [
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_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);
if (null !== $part) {
//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
{
protected string $default_locale;
protected TranslatorInterface $translator;
protected bool $enforce_index_php;
public function __construct(string $default_locale, TranslatorInterface $translator, bool $enforce_index_php)
public function __construct(protected string $default_locale, protected TranslatorInterface $translator, protected 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')]
class ScanController extends AbstractController
{
protected BarcodeRedirector $barcodeParser;
protected BarcodeNormalizer $barcodeNormalizer;
public function __construct(BarcodeRedirector $barcodeParser, BarcodeNormalizer $barcodeNormalizer)
public function __construct(protected BarcodeRedirector $barcodeParser, protected BarcodeNormalizer $barcodeNormalizer)
{
$this->barcodeParser = $barcodeParser;
$this->barcodeNormalizer = $barcodeNormalizer;
}
#[Route(path: '', name: 'scan_dialog')]
@ -79,10 +74,10 @@ class ScanController extends AbstractController
try {
return $this->redirect($this->barcodeParser->getRedirectURL($type, $id));
} catch (EntityNotFoundException $exception) {
} catch (EntityNotFoundException) {
$this->addFlash('success', 'scan.qr_not_found');
}
} catch (InvalidArgumentException $exception) {
} catch (InvalidArgumentException) {
$this->addFlash('error', 'scan.format_unknown');
}
}
@ -101,7 +96,7 @@ class ScanController extends AbstractController
$this->addFlash('success', 'scan.qr_success');
return $this->redirect($this->barcodeParser->getRedirectURL($type, $id));
} catch (EntityNotFoundException $exception) {
} catch (EntityNotFoundException) {
$this->addFlash('success', 'scan.qr_not_found');
return $this->redirectToRoute('homepage');

View file

@ -48,13 +48,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class SecurityController extends AbstractController
{
protected TranslatorInterface $translator;
protected bool $allow_email_pw_reset;
public function __construct(TranslatorInterface $translator, bool $allow_email_pw_reset)
public function __construct(protected TranslatorInterface $translator, protected 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'])]
@ -76,7 +71,7 @@ class SecurityController extends AbstractController
* @return RedirectResponse|Response
*/
#[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) {
throw new AccessDeniedHttpException('The password reset via email is disabled!');
@ -119,7 +114,7 @@ class SecurityController extends AbstractController
* @return RedirectResponse|Response
*/
#[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) {
throw new AccessDeniedHttpException('The password reset via email is disabled!');
@ -189,7 +184,7 @@ class SecurityController extends AbstractController
}
#[Route(path: '/logout', name: 'logout')]
public function logout(): void
public function logout(): never
{
throw new RuntimeException('Will be intercepted before getting here');
}

View file

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

View file

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

View file

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

View file

@ -55,13 +55,8 @@ use Symfony\Component\Serializer\Serializer;
#[Route(path: '/typeahead')]
class TypeaheadController extends AbstractController
{
protected AttachmentURLGenerator $urlGenerator;
protected Packages $assets;
public function __construct(AttachmentURLGenerator $URLGenerator, Packages $assets)
public function __construct(protected AttachmentURLGenerator $urlGenerator, protected Packages $assets)
{
$this->urlGenerator = $URLGenerator;
$this->assets = $assets;
}
#[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
* @param string $type
* @return class-string
*/
private function typeToParameterClass(string $type): string
{
switch ($type) {
case 'category':
return CategoryParameter::class;
case 'part':
return PartParameter::class;
case 'device':
return ProjectParameter::class;
case 'footprint':
return FootprintParameter::class;
case 'manufacturer':
return ManufacturerParameter::class;
case 'storelocation':
return StorelocationParameter::class;
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);
}
return match ($type) {
'category' => CategoryParameter::class,
'part' => PartParameter::class,
'device' => ProjectParameter::class,
'footprint' => FootprintParameter::class,
'manufacturer' => ManufacturerParameter::class,
'storelocation' => StorelocationParameter::class,
'supplier' => SupplierParameter::class,
'attachment_type' => AttachmentTypeParameter::class,
'group' => GroupParameter::class,
'measurement_unit' => MeasurementUnitParameter::class,
'currency' => 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')]
public function parts(EntityManagerInterface $entityManager, PartPreviewGenerator $previewGenerator,
AttachmentURLGenerator $attachmentURLGenerator, string $query = ""): JsonResponse
@ -146,7 +122,7 @@ class TypeaheadController extends AbstractController
foreach ($parts as $part) {
//Determine the picture to show:
$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');
} else {
$preview_url = '';
@ -156,8 +132,8 @@ class TypeaheadController extends AbstractController
$data[] = [
'id' => $part->getID(),
'name' => $part->getName(),
'category' => $part->getCategory() ? $part->getCategory()->getName() : 'Unknown',
'footprint' => $part->getFootprint() ? $part->getFootprint()->getName() : '',
'category' => $part->getCategory() instanceof \App\Entity\Parts\Category ? $part->getCategory()->getName() : 'Unknown',
'footprint' => $part->getFootprint() instanceof \App\Entity\Parts\Footprint ? $part->getFootprint()->getName() : '',
'description' => mb_strimwidth($part->getDescription(), 0, 127, '...'),
'image' => $preview_url,
];
@ -166,10 +142,6 @@ class TypeaheadController extends AbstractController
return new JsonResponse($data);
}
/**
* @param string $query
* @return JsonResponse
*/
#[Route(path: '/parameters/{type}/search/{query}', name: 'typeahead_parameters', requirements: ['type' => '.+'])]
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
{
//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();
if (!$tmp instanceof User) {
throw new InvalidArgumentException('Userinfo only works for database users!');

View file

@ -54,16 +54,13 @@ use Symfony\Component\Validator\Constraints\Length;
#[Route(path: '/user')]
class UserSettingsController extends AbstractController
{
protected bool $demo_mode;
/**
* @var EventDispatcher|EventDispatcherInterface
*/
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;
}
@ -121,49 +118,43 @@ class UserSettingsController extends AbstractController
$key_repo = $entityManager->getRepository(U2FKey::class);
/** @var U2FKey|null $u2f */
$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');
return $this->redirectToRoute('user_settings');
}
//User can only delete its own U2F keys
if ($u2f->getUser() !== $user) {
$this->addFlash('danger', 'tfa_u2f.u2f_delete.access_denied');
return $this->redirectToRoute('user_settings');
}
$backupCodeManager->disableBackupCodesIfUnused($user);
$entityManager->remove($u2f);
$entityManager->flush();
$this->addFlash('success', 'tfa.u2f.u2f_delete.success');
$security_event = new SecurityEvent($user);
$this->eventDispatcher->dispatch($security_event, SecurityEvents::U2F_REMOVED);
} else if ($request->request->has('webauthn_key_id')) {
} elseif ($request->request->has('webauthn_key_id')) {
$key_id = $request->request->get('webauthn_key_id');
$key_repo = $entityManager->getRepository(WebauthnKey::class);
/** @var WebauthnKey|null $key */
$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');
return $this->redirectToRoute('user_settings');
}
//User can only delete its own U2F keys
if ($key->getUser() !== $user) {
$this->addFlash('error', 'tfa_u2f.u2f_delete.access_denied');
return $this->redirectToRoute('user_settings');
}
$backupCodeManager->disableBackupCodesIfUnused($user);
$entityManager->remove($key);
$entityManager->flush();
$this->addFlash('success', 'tfa.u2f.u2f_delete.success');
$security_event = new SecurityEvent($user);
$this->eventDispatcher->dispatch($security_event, SecurityEvents::U2F_REMOVED);
}
@ -174,11 +165,8 @@ class UserSettingsController extends AbstractController
return $this->redirectToRoute('user_settings');
}
/**
* @return RuntimeException|RedirectResponse
*/
#[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) {
throw new RuntimeException('You can not do 2FA things in demo mode');
@ -215,7 +203,7 @@ class UserSettingsController extends AbstractController
* @return RedirectResponse|Response
*/
#[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 */
$user = $this->getUser();
@ -254,13 +242,11 @@ class UserSettingsController extends AbstractController
}
/** @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
if ($user->getMasterPictureAttachment() !== null) {
$em->remove($user->getMasterPictureAttachment());
$user->setMasterPictureAttachment(null);
$page_need_reload = true;
}
//Remove the avatar attachment from the user if requested
if ($form->getClickedButton() && 'remove_avatar' === $form->getClickedButton()->getName() && $user->getMasterPictureAttachment() instanceof \App\Entity\Attachments\Attachment) {
$em->remove($user->getMasterPictureAttachment());
$user->setMasterPictureAttachment(null);
$page_need_reload = true;
}
$em->flush();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -33,15 +33,8 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class PartAttachmentsColumn extends AbstractColumn
{
protected FAIconGenerator $FAIconGenerator;
protected EntityURLGenerator $urlGenerator;
protected AttachmentManager $attachmentManager;
public function __construct(FAIconGenerator $FAIconGenerator, EntityURLGenerator $urlGenerator, AttachmentManager $attachmentManager)
public function __construct(protected FAIconGenerator $FAIconGenerator, protected EntityURLGenerator $urlGenerator, protected 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!');
}
$tmp = '';
$attachments = $context->getAttachments()->filter(function (Attachment $attachment) {
return $attachment->getShowInTable() && $this->attachmentManager->isFileExisting($attachment);
});
$attachments = $context->getAttachments()->filter(fn(Attachment $attachment) => $attachment->getShowInTable() && $this->attachmentManager->isFileExisting($attachment));
$count = 5;
foreach ($attachments as $attachment) {

View file

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

View file

@ -51,13 +51,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class RevertLogColumn extends AbstractColumn
{
protected TranslatorInterface $translator;
protected \Symfony\Bundle\SecurityBundle\Security $security;
public function __construct(TranslatorInterface $translator, \Symfony\Bundle\SecurityBundle\Security $security)
public function __construct(protected TranslatorInterface $translator, protected \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')
);
$tmp .= '</div>';
return $tmp;
return $tmp . '</div>';
}
}

View file

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

View file

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

View file

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

View file

@ -60,59 +60,38 @@ class AttachmentFilter implements FilterInterface
$this->applyAllChildFilters($queryBuilder);
}
/**
* @return NumberConstraint
*/
public function getDbId(): NumberConstraint
{
return $this->dbId;
}
/**
* @return TextConstraint
*/
public function getName(): TextConstraint
{
return $this->name;
}
/**
* @return DateTimeConstraint
*/
public function getLastModified(): DateTimeConstraint
{
return $this->lastModified;
}
/**
* @return DateTimeConstraint
*/
public function getAddedDate(): DateTimeConstraint
{
return $this->addedDate;
}
/**
* @return BooleanConstraint
*/
public function getShowInTable(): BooleanConstraint
{
return $this->showInTable;
}
/**
* @return EntityConstraint
*/
public function getAttachmentType(): EntityConstraint
{
return $this->attachmentType;
}
/**
* @return InstanceOfConstraint
*/
public function getTargetType(): InstanceOfConstraint
{
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.
* @param QueryBuilder $queryBuilder
* @return void
*/
protected function applyAllChildFilters(QueryBuilder $queryBuilder): void
{

View file

@ -26,11 +26,6 @@ abstract class AbstractConstraint implements FilterInterface
{
use FilterTrait;
/**
* @var string The property where this BooleanConstraint should apply to
*/
protected string $property;
/**
* @var string
*/
@ -43,9 +38,11 @@ abstract class AbstractConstraint implements FilterInterface
*/
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);
}
}

View file

@ -24,19 +24,14 @@ use Doctrine\ORM\QueryBuilder;
class BooleanConstraint extends AbstractConstraint
{
/** @var bool|null The value of our constraint */
protected ?bool $value;
public function __construct(string $property, string $identifier = null, ?bool $default_value = null)
public function __construct(string $property, string $identifier = null, /** @var bool|null The value of our constraint */
protected ?bool $value = null)
{
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".
* @return bool|null
*/
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".
* @param bool|null $value
*/
public function setValue(?bool $value): void
{

View file

@ -24,7 +24,7 @@ use Doctrine\ORM\QueryBuilder;
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
@ -46,7 +46,6 @@ class ChoiceConstraint extends AbstractConstraint
/**
* @param string[]|int[] $value
* @return ChoiceConstraint
*/
public function setValue(array $value): ChoiceConstraint
{
@ -54,18 +53,11 @@ class ChoiceConstraint extends AbstractConstraint
return $this;
}
/**
* @return string
*/
public function getOperator(): string
{
return $this->operator;
}
/**
* @param string $operator
* @return ChoiceConstraint
*/
public function setOperator(string $operator): ChoiceConstraint
{
$this->operator = $operator;

View file

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

View file

@ -35,7 +35,6 @@ trait FilterTrait
/**
* Checks if the given input is an aggregateFunction like COUNT(part.partsLot) or so
* @return 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.
* @param string $property
* @return 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.
* @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') {
$expression = sprintf("%s %s (:%s)", $property, $comparison_operator, $parameterIdentifier);

View file

@ -27,7 +27,7 @@ use Doctrine\ORM\QueryBuilder;
*/
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)
@ -57,16 +57,12 @@ class InstanceOfConstraint extends AbstractConstraint
return $this;
}
/**
* @return string
*/
public function getOperator(): string
{
return $this->operator;
}
/**
* @param string $operator
* @return $this
*/
public function setOperator(string $operator): self

View file

@ -25,61 +25,28 @@ use RuntimeException;
class NumberConstraint extends AbstractConstraint
{
public const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN'];
final public const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN'];
/**
* 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()
public function getValue1(): float|int|null|\DateTimeInterface
{
return $this->value1;
}
/**
* @param float|int|\DateTimeInterface|null $value1
*/
public function setValue1($value1): void
public function setValue1(float|int|\DateTimeInterface|null $value1): void
{
$this->value1 = $value1;
}
/**
* @return float|int|null
*/
public function getValue2()
public function getValue2(): float|int|null
{
return $this->value2;
}
/**
* @param float|int|null $value2
*/
public function setValue2($value2): void
public function setValue2(float|int|null $value2): void
{
$this->value2 = $value2;
}
/**
* @return string
*/
public function getOperator(): string
{
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);
$this->value1 = $value1;
$this->value2 = $value2;
$this->operator = $operator;
}
public function isEnabled(): bool

View file

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

View file

@ -25,18 +25,14 @@ use Doctrine\ORM\QueryBuilder;
class ParameterValueConstraint extends NumberConstraint
{
protected string $alias;
public const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN',
final public const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN',
//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'];
/**
* @param string $alias The alias which is used in the sub query of ParameterConstraint
*/
public function __construct(string $alias) {
$this->alias = $alias;
public function __construct(protected string $alias) {
parent::__construct($alias . '.value_typical');
}

View file

@ -26,23 +26,20 @@ use Doctrine\ORM\QueryBuilder;
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
*/
protected $value;
public function __construct(string $property, string $identifier = null, $value = null, string $operator = '')
protected $value = null, /**
* @var string|null The operator to use
*/
protected ?string $operator = '')
{
parent::__construct($property, $identifier);
$this->value = $value;
$this->operator = $operator;
}
/**
@ -62,17 +59,11 @@ class TagsConstraint extends AbstractConstraint
return $this;
}
/**
* @return string
*/
public function getValue(): string
{
return $this->value;
}
/**
* @param string $value
*/
public function setValue(string $value): self
{
$this->value = $value;
@ -96,9 +87,6 @@ class TagsConstraint extends AbstractConstraint
/**
* 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
{

View file

@ -25,23 +25,20 @@ use Doctrine\ORM\QueryBuilder;
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
*/
protected $value;
public function __construct(string $property, string $identifier = null, $value = null, string $operator = '')
protected $value = null, /**
* @var string|null The operator to use
*/
protected ?string $operator = '')
{
parent::__construct($property, $identifier);
$this->value = $value;
$this->operator = $operator;
}
/**
@ -61,17 +58,11 @@ class TextConstraint extends AbstractConstraint
return $this;
}
/**
* @return string
*/
public function getValue(): string
{
return $this->value;
}
/**
* @param string $value
*/
public function setValue(string $value): self
{
$this->value = $value;
@ -105,11 +96,11 @@ class TextConstraint extends AbstractConstraint
$like_value = null;
if ($this->operator === 'LIKE') {
$like_value = $this->value;
} else if ($this->operator === 'STARTS') {
} elseif ($this->operator === 'STARTS') {
$like_value = $this->value . '%';
} else if ($this->operator === 'ENDS') {
} elseif ($this->operator === 'ENDS') {
$like_value = '%' . $this->value;
} else if ($this->operator === 'CONTAINS') {
} elseif ($this->operator === 'CONTAINS') {
$like_value = '%' . $this->value . '%';
}

View file

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

View file

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

View file

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

View file

@ -31,19 +31,8 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class PartDataTableHelper
{
private PartPreviewGenerator $previewGenerator;
private AttachmentURLGenerator $attachmentURLGenerator;
private TranslatorInterface $translator;
private EntityURLGenerator $entityURLGenerator;
public function __construct(PartPreviewGenerator $previewGenerator, AttachmentURLGenerator $attachmentURLGenerator,
EntityURLGenerator $entityURLGenerator, TranslatorInterface $translator)
public function __construct(private readonly PartPreviewGenerator $previewGenerator, private readonly AttachmentURLGenerator $attachmentURLGenerator, private readonly EntityURLGenerator $entityURLGenerator, private readonly TranslatorInterface $translator)
{
$this->previewGenerator = $previewGenerator;
$this->attachmentURLGenerator = $attachmentURLGenerator;
$this->translator = $translator;
$this->entityURLGenerator = $entityURLGenerator;
}
public function renderName(Part $context): string
@ -57,7 +46,7 @@ class PartDataTableHelper
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'));
}
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>',
$this->translator->trans('part.info.projectBuildPart.hint') . ': ' . $context->getBuiltProject()->getName());
}
@ -74,7 +63,7 @@ class PartDataTableHelper
public function renderPicture(Part $context): string
{
$preview_attachment = $this->previewGenerator->getTablePreviewAttachment($context);
if (null === $preview_attachment) {
if (!$preview_attachment instanceof \App\Entity\Attachments\Attachment) {
return '';
}

View file

@ -61,27 +61,13 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class LogDataTable implements DataTableTypeInterface
{
protected ElementTypeNameGenerator $elementTypeNameGenerator;
protected TranslatorInterface $translator;
protected UrlGeneratorInterface $urlGenerator;
protected EntityURLGenerator $entityURLGenerator;
protected LogEntryRepository $logRepo;
protected \Symfony\Bundle\SecurityBundle\Security $security;
protected UserAvatarHelper $userAvatarHelper;
protected LogLevelHelper $logLevelHelper;
public function __construct(ElementTypeNameGenerator $elementTypeNameGenerator, TranslatorInterface $translator,
UrlGeneratorInterface $urlGenerator, EntityURLGenerator $entityURLGenerator, EntityManagerInterface $entityManager,
\Symfony\Bundle\SecurityBundle\Security $security, UserAvatarHelper $userAvatarHelper, LogLevelHelper $logLevelHelper)
public function __construct(protected ElementTypeNameGenerator $elementTypeNameGenerator, protected TranslatorInterface $translator,
protected UrlGeneratorInterface $urlGenerator, protected EntityURLGenerator $entityURLGenerator, EntityManagerInterface $entityManager,
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->security = $security;
$this->userAvatarHelper = $userAvatarHelper;
$this->logLevelHelper = $logLevelHelper;
}
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
$dataTable->add('dont_matter', RowClassColumn::class, [
'render' => function ($value, AbstractLogEntry $context) {
return $this->logLevelHelper->logLevelToTableColorClass($context->getLevelString());
},
'render' => fn($value, AbstractLogEntry $context) => $this->logLevelHelper->logLevelToTableColorClass($context->getLevelString()),
]);
$dataTable->add('symbol', TextColumn::class, [
'label' => '',
'className' => 'no-colvis',
'render' => function ($value, AbstractLogEntry $context) {
return sprintf(
'<i class="fas fa-fw %s" title="%s"></i>',
$this->logLevelHelper->logLevelToIconClass($context->getLevelString()),
$context->getLevelString()
);
},
'render' => fn($value, AbstractLogEntry $context): string => sprintf(
'<i class="fas fa-fw %s" title="%s"></i>',
$this->logLevelHelper->logLevelToIconClass($context->getLevelString()),
$context->getLevelString()
),
]);
$dataTable->add('id', TextColumn::class, [
@ -140,12 +122,10 @@ class LogDataTable implements DataTableTypeInterface
$dataTable->add('timestamp', LocaleDateTimeColumn::class, [
'label' => 'log.timestamp',
'timeFormat' => 'medium',
'render' => function (string $value, AbstractLogEntry $context) {
return sprintf('<a href="%s">%s</a>',
$this->urlGenerator->generate('log_details', ['id' => $context->getId()]),
$value
);
}
'render' => fn(string $value, AbstractLogEntry $context): string => sprintf('<a href="%s">%s</a>',
$this->urlGenerator->generate('log_details', ['id' => $context->getId()]),
$value
)
]);
$dataTable->add('type', TextColumn::class, [
@ -169,18 +149,16 @@ class LogDataTable implements DataTableTypeInterface
'label' => 'log.level',
'visible' => 'system_log' === $options['mode'],
'propertyPath' => 'levelString',
'render' => function (string $value, AbstractLogEntry $context) {
return $this->translator->trans('log.level.'.$value);
},
'render' => fn(string $value, AbstractLogEntry $context) => $this->translator->trans('log.level.'.$value),
]);
$dataTable->add('user', TextColumn::class, [
'label' => 'log.user',
'render' => function ($value, AbstractLogEntry $context) {
'render' => function ($value, AbstractLogEntry $context): string {
$user = $context->getUser();
//If user was deleted, show the info from the username field
if ($user === null) {
if (!$user instanceof \App\Entity\UserSystem\User) {
if ($context->isCLIEntry()) {
return sprintf('%s [%s]',
htmlentities($context->getCLIUsername()),
@ -241,19 +219,17 @@ class LogDataTable implements DataTableTypeInterface
) {
try {
$target = $this->logRepo->getTargetElement($context);
if (null !== $target) {
if ($target instanceof \App\Entity\Base\AbstractDBElement) {
return $this->entityURLGenerator->timeTravelURL($target, $context->getTimestamp());
}
} catch (EntityNotSupportedException $exception) {
} catch (EntityNotSupportedException) {
return null;
}
}
return null;
},
'disabled' => function ($value, AbstractLogEntry $context) {
return !$this->security->isGranted('show_history', $context->getTargetClass());
},
'disabled' => fn($value, AbstractLogEntry $context) => !$this->security->isGranted('show_history', $context->getTargetClass()),
]);
$dataTable->add('actionRevert', RevertLogColumn::class, [
@ -310,7 +286,7 @@ class LogDataTable implements DataTableTypeInterface
foreach ($options['filter_elements'] as $element) {
/** @var AbstractDBElement $element */
$target_type = AbstractLogEntry::targetTypeClassToID(get_class($element));
$target_type = AbstractLogEntry::targetTypeClassToID($element::class);
$target_id = $element->getID();
$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
{
private TranslatorInterface $translator;
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)
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)
{
$this->urlGenerator = $urlGenerator;
$this->translator = $translator;
$this->amountFormatter = $amountFormatter;
$this->security = $security;
$this->partDataTableHelper = $partDataTableHelper;
}
public function configureOptions(OptionsResolver $optionsResolver): void
@ -92,7 +78,7 @@ final class PartsDataTable implements DataTableTypeInterface
$dataTable
//Color the table rows depending on the review and favorite status
->add('dont_matter', RowClassColumn::class, [
'render' => function ($value, Part $context) {
'render' => function ($value, Part $context): string {
if ($context->isNeedsReview()) {
return 'table-secondary';
}
@ -108,15 +94,11 @@ final class PartsDataTable implements DataTableTypeInterface
->add('picture', TextColumn::class, [
'label' => '',
'className' => 'no-colvis',
'render' => function ($value, Part $context) {
return $this->partDataTableHelper->renderPicture($context);
},
'render' => fn($value, Part $context) => $this->partDataTableHelper->renderPicture($context),
])
->add('name', TextColumn::class, [
'label' => $this->translator->trans('part.table.name'),
'render' => function ($value, Part $context) {
return $this->partDataTableHelper->renderName($context);
},
'render' => fn($value, Part $context) => $this->partDataTableHelper->renderName($context),
])
->add('id', TextColumn::class, [
'label' => $this->translator->trans('part.table.id'),
@ -153,11 +135,11 @@ final class PartsDataTable implements DataTableTypeInterface
$dataTable->add('storelocation', TextColumn::class, [
'label' => $this->translator->trans('part.table.storeLocations'),
'orderField' => 'storelocations.name',
'render' => function ($value, Part $context) {
'render' => function ($value, Part $context): string {
$tmp = [];
foreach ($context->getPartLots() as $lot) {
//Ignore lots without storelocation
if (null === $lot->getStorageLocation()) {
if (!$lot->getStorageLocation() instanceof \App\Entity\Parts\Storelocation) {
continue;
}
$tmp[] = sprintf(
@ -216,9 +198,7 @@ final class PartsDataTable implements DataTableTypeInterface
->add('minamount', TextColumn::class, [
'label' => $this->translator->trans('part.table.minamount'),
'visible' => false,
'render' => function ($value, Part $context) {
return htmlspecialchars($this->amountFormatter->format($value, $context->getPartUnit()));
},
'render' => fn($value, Part $context): string => htmlspecialchars($this->amountFormatter->format($value, $context->getPartUnit())),
]);
if ($this->security->isGranted('@footprints.read')) {
@ -278,12 +258,8 @@ final class PartsDataTable implements DataTableTypeInterface
->add('edit', IconLinkColumn::class, [
'label' => $this->translator->trans('part.table.edit'),
'visible' => false,
'href' => function ($value, Part $context) {
return $this->urlGenerator->editURL($context);
},
'disabled' => function ($value, Part $context) {
return !$this->security->isGranted('edit', $context);
},
'href' => fn($value, Part $context) => $this->urlGenerator->editURL($context),
'disabled' => fn($value, Part $context) => !$this->security->isGranted('edit', $context),
'title' => $this->translator->trans('part.table.edit.title'),
])

View file

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

View file

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

View file

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

View file

@ -27,7 +27,7 @@ use Doctrine\DBAL\Types\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
{
@ -53,7 +53,7 @@ class BigDecimalType extends Type
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
{
if (null === $value) {
if (!$value instanceof \Brick\Math\BigDecimal) {
return null;
}

View file

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

View file

@ -34,11 +34,11 @@ use LogicException;
/**
* Class Attachment.
*/
#[ORM\Entity(repositoryClass: 'App\Repository\AttachmentRepository')]
#[ORM\Entity(repositoryClass: \App\Repository\AttachmentRepository::class)]
#[ORM\InheritanceType('SINGLE_TABLE')]
#[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\EntityListeners(['App\EntityListeners\AttachmentDeleteListener'])]
#[ORM\EntityListeners([\App\EntityListeners\AttachmentDeleteListener::class])]
#[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_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
* 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', ];
/**
* 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.
*/
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
*/
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.

View file

@ -33,7 +33,7 @@ use Symfony\Component\Validator\Constraints as Assert;
/**
* Class AttachmentType.
*/
#[ORM\Entity(repositoryClass: 'App\Repository\StructuralDBElementRepository')]
#[ORM\Entity(repositoryClass: \App\Repository\StructuralDBElementRepository::class)]
#[ORM\Table(name: '`attachment_types`')]
#[ORM\Index(name: 'attachment_types_idx_name', columns: ['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\JoinColumn(name: 'parent_id')]
protected ?AbstractStructuralDBElement $parent;
protected ?AbstractStructuralDBElement $parent = null;
/**
* @var string
@ -57,14 +57,14 @@ class AttachmentType extends AbstractStructuralDBElement
* @var Collection<int, AttachmentTypeAttachment>
*/
#[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'])]
protected Collection $attachments;
/** @var Collection<int, AttachmentTypeParameter>
*/
#[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'])]
protected Collection $parameters;

View file

@ -32,11 +32,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -33,11 +33,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -33,11 +33,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -33,11 +33,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -33,12 +33,12 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -52,12 +52,12 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -33,12 +33,12 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -34,11 +34,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -33,11 +33,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -33,11 +33,11 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -33,12 +33,12 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -33,12 +33,12 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -33,12 +33,12 @@ use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity]
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
*/
#[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')]
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)
* 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'])]
#[ORM\MappedSuperclass(repositoryClass: 'App\Repository\DBElementRepository')]
#[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::class)]
abstract class AbstractDBElement implements JsonSerializable
{
/** @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\HasLifecycleCallbacks]
abstract class AbstractNamedDBElement extends AbstractDBElement implements NamedElementInterface, TimeStampableInterface
abstract class AbstractNamedDBElement extends AbstractDBElement implements NamedElementInterface, TimeStampableInterface, \Stringable
{
use TimestampTrait;
@ -51,7 +51,7 @@ abstract class AbstractNamedDBElement extends AbstractDBElement implements Named
*
******************************************************************************/
public function __toString()
public function __toString(): string
{
return $this->getName();
}

View file

@ -29,7 +29,7 @@ use Symfony\Component\Serializer\Annotation\Groups;
/**
* Class PartsContainingDBElement.
*/
#[ORM\MappedSuperclass(repositoryClass: 'App\Repository\AbstractPartsContainingRepository')]
#[ORM\MappedSuperclass(repositoryClass: \App\Repository\AbstractPartsContainingRepository::class)]
abstract class AbstractPartsContainingDBElement extends AbstractStructuralDBElement
{
#[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')]
#[ORM\MappedSuperclass(repositoryClass: 'App\Repository\StructuralDBElementRepository')]
#[ORM\EntityListeners(['App\EntityListeners\TreeCacheInvalidationListener'])]
#[ORM\MappedSuperclass(repositoryClass: \App\Repository\StructuralDBElementRepository::class)]
#[ORM\EntityListeners([\App\EntityListeners\TreeCacheInvalidationListener::class])]
abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
{
use ParametersTrait;
@ -105,7 +105,6 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
parent::__construct();
$this->children = new ArrayCollection();
$this->parameters = new ArrayCollection();
$this->parent = null;
}
public function __clone()
@ -141,11 +140,11 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
//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):
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!');
}
if (null === $this->getParent()) { // this is the root node
if (!$this->getParent() instanceof \App\Entity\Base\AbstractStructuralDBElement) { // this is the root node
return false;
}
@ -170,7 +169,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
*/
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.
*/
if (0 === $this->level && null !== $this->parent) {
if (0 === $this->level && $this->parent instanceof \App\Entity\Base\AbstractStructuralDBElement) {
$element = $this->parent;
while (null !== $element) {
while ($element instanceof \App\Entity\Base\AbstractStructuralDBElement) {
/** @var AbstractStructuralDBElement $element */
$element = $element->parent;
++$this->level;
@ -234,14 +233,14 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
*/
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->getName();
$element = $this;
$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;
$this->full_path_strings[] = $element->getName();
//Decrement to prevent mem overflow.
@ -328,7 +327,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
$this->parent = $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);
}

View file

@ -35,7 +35,7 @@ trait MasterAttachmentTrait
* @var Attachment|null
*/
#[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')]
protected ?Attachment $master_picture_attachment = null;

View file

@ -47,11 +47,11 @@ use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Embeddable]
class LabelOptions
{
public const BARCODE_TYPES = ['none', /*'ean8',*/ 'qr', 'code39', 'datamatrix', 'code93', 'code128'];
public const SUPPORTED_ELEMENTS = ['part', 'part_lot', 'storelocation'];
public const PICTURE_TYPES = ['none', 'element_picture', 'main_attachment'];
final public const BARCODE_TYPES = ['none', /*'ean8',*/ 'qr', 'code39', 'datamatrix', 'code93', 'code128'];
final public const SUPPORTED_ELEMENTS = ['part', 'part_lot', 'storelocation'];
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
@ -111,9 +111,6 @@ class LabelOptions
return $this->width;
}
/**
* @return LabelOptions
*/
public function setWidth(float $width): self
{
$this->width = $width;
@ -126,9 +123,6 @@ class LabelOptions
return $this->height;
}
/**
* @return LabelOptions
*/
public function setHeight(float $height): self
{
$this->height = $height;
@ -141,9 +135,6 @@ class LabelOptions
return $this->barcode_type;
}
/**
* @return LabelOptions
*/
public function setBarcodeType(string $barcode_type): self
{
$this->barcode_type = $barcode_type;
@ -156,9 +147,6 @@ class LabelOptions
return $this->picture_type;
}
/**
* @return LabelOptions
*/
public function setPictureType(string $picture_type): self
{
$this->picture_type = $picture_type;
@ -171,9 +159,6 @@ class LabelOptions
return $this->supported_element;
}
/**
* @return LabelOptions
*/
public function setSupportedElement(string $supported_element): self
{
$this->supported_element = $supported_element;
@ -186,9 +171,6 @@ class LabelOptions
return $this->lines;
}
/**
* @return LabelOptions
*/
public function setLines(string $lines): self
{
$this->lines = $lines;
@ -204,9 +186,6 @@ class LabelOptions
return $this->additional_css;
}
/**
* @return LabelOptions
*/
public function setAdditionalCss(string $additional_css): self
{
$this->additional_css = $additional_css;
@ -219,9 +198,6 @@ class LabelOptions
return $this->lines_mode;
}
/**
* @return LabelOptions
*/
public function setLinesMode(string $lines_mode): self
{
$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;
#[UniqueEntity(['name', 'options.supported_element'])]
#[ORM\Entity(repositoryClass: 'App\Repository\LabelProfileRepository')]
#[ORM\EntityListeners(['App\EntityListeners\TreeCacheInvalidationListener'])]
#[ORM\Entity(repositoryClass: \App\Repository\LabelProfileRepository::class)]
#[ORM\EntityListeners([\App\EntityListeners\TreeCacheInvalidationListener::class])]
#[ORM\Table(name: 'label_profiles')]
class LabelProfile extends AttachmentContainingDBElement
{
/**
* @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'])]
protected Collection $attachments;
@ -126,8 +126,6 @@ class LabelProfile extends AttachmentContainingDBElement
/**
* Sets the show in dropdown menu.
*
* @return LabelProfile
*/
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')]
abstract class AbstractLogEntry extends AbstractDBElement
{
public const LEVEL_EMERGENCY = 0;
public const LEVEL_ALERT = 1;
public const LEVEL_CRITICAL = 2;
public const LEVEL_ERROR = 3;
public const LEVEL_WARNING = 4;
public const LEVEL_NOTICE = 5;
public const LEVEL_INFO = 6;
public const LEVEL_DEBUG = 7;
final public const LEVEL_EMERGENCY = 0;
final public const LEVEL_ALERT = 1;
final public const LEVEL_CRITICAL = 2;
final public const LEVEL_ERROR = 3;
final public const LEVEL_WARNING = 4;
final public const LEVEL_NOTICE = 5;
final public const LEVEL_INFO = 6;
final public const LEVEL_DEBUG = 7;
protected const TARGET_TYPE_NONE = 0;
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
*/
#[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')]
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
*/
#[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
*/
@ -173,7 +173,6 @@ abstract class AbstractLogEntry extends AbstractDBElement
public function __construct()
{
$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.
* This removes the association to a user object in database, as CLI users are not really related to logged in
* Part-DB users.
* @param string $cli_username
* @return $this
*/
public function setCLIUsername(string $cli_username): self
@ -372,14 +370,14 @@ abstract class AbstractLogEntry extends AbstractDBElement
*/
public function setTargetElement(?AbstractDBElement $element): self
{
if (null === $element) {
if (!$element instanceof \App\Entity\Base\AbstractDBElement) {
$this->target_id = 0;
$this->target_type = self::TARGET_TYPE_NONE;
return $this;
}
$this->target_type = static::targetTypeClassToID(get_class($element));
$this->target_type = static::targetTypeClassToID($element::class);
$this->target_id = $element->getID();
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)
{
parent::__construct();
$this->level = self::LEVEL_INFO;
$this->setTargetElement($changed_element);
$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();
if ($deletedElement instanceof NamedElementInterface) {
$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).
* 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
{

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