diff --git a/config/packages/dev/easy_log_handler.yaml b/config/packages/dev/easy_log_handler.yaml new file mode 100644 index 00000000..27bfc608 --- /dev/null +++ b/config/packages/dev/easy_log_handler.yaml @@ -0,0 +1,16 @@ +services: + EasyCorp\EasyLog\EasyLogHandler: + public: false + arguments: ['%kernel.logs_dir%/%kernel.environment%.log'] + +#// FIXME: How to add this configuration automatically without messing up with the monolog configuration? +#monolog: +# handlers: +# buffered: +# type: buffer +# handler: easylog +# channels: ['!event'] +# level: debug +# easylog: +# type: service +# id: EasyCorp\EasyLog\EasyLogHandler diff --git a/config/packages/doctrine.php b/config/packages/doctrine.php index f62cdcfb..e5be011f 100644 --- a/config/packages/doctrine.php +++ b/config/packages/doctrine.php @@ -20,16 +20,16 @@ declare(strict_types=1); +use Symfony\Config\DoctrineConfig; + /** - * This file enables native lazy objects on PHP 8.4+. + * This class extends the default doctrine ORM configuration to enable native lazy objects on PHP 8.4+. * We have to do this in a PHP file, because the yaml file does not support conditionals on PHP version. - * - * TODO: Remove this file when we drop support for PHP < 8.4 */ -// On PHP 8.4+ we can use native lazy objects, which are much more efficient than proxies. -if (PHP_VERSION_ID >= 80400) { - return ['doctrine' => ['orm' => ['enable_native_lazy_objects' => true]]]; -} - -return []; +return static function(DoctrineConfig $doctrine) { + //On PHP 8.4+ we can use native lazy objects, which are much more efficient than proxies. + if (PHP_VERSION_ID >= 80400) { + $doctrine->orm()->enableNativeLazyObjects(true); + } +}; diff --git a/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php b/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php index f721f986..aa6108c9 100644 --- a/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php +++ b/src/Doctrine/Middleware/SQLiteRegexExtensionMiddlewareDriver.php @@ -27,7 +27,6 @@ use App\Doctrine\Functions\SiValueSort; use App\Exceptions\InvalidRegexException; use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware; -use Pdo\Sqlite; /** * This middleware is used to add the regexp operator to the SQLite platform. @@ -45,30 +44,17 @@ class SQLiteRegexExtensionMiddlewareDriver extends AbstractDriverMiddleware if ($params['driver'] === 'pdo_sqlite') { $native_connection = $connection->getNativeConnection(); + //Ensure that the function really exists on the connection, as it is marked as experimental according to PHP documentation if($native_connection instanceof \PDO) { + $native_connection->sqliteCreateFunction('REGEXP', self::regexp(...), 2, \PDO::SQLITE_DETERMINISTIC); + $native_connection->sqliteCreateFunction('FIELD', self::field(...), -1, \PDO::SQLITE_DETERMINISTIC); + $native_connection->sqliteCreateFunction('FIELD2', self::field2(...), 2, \PDO::SQLITE_DETERMINISTIC); - //Use the new PDO::createFunction and PDO::createCollation methods if available (PHP 8.4+) - if (is_a($native_connection, Sqlite::class)) { #TODO: Remove this check when PHP 8.4 is the minimum requirement - $native_connection->createFunction('REGEXP', self::regexp(...), 2, Sqlite::DETERMINISTIC); - $native_connection->createFunction('FIELD', self::field(...), -1, Sqlite::DETERMINISTIC); - $native_connection->createFunction('FIELD2', self::field2(...), 2, Sqlite::DETERMINISTIC); + //Create a new collation for natural sorting + $native_connection->sqliteCreateCollation('NATURAL_CMP', strnatcmp(...)); - //Create a new collation for natural sorting - $native_connection->createCollation('NATURAL_CMP', strnatcmp(...)); - - //Create a function for SI prefix value sorting - $native_connection->createFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, Sqlite::DETERMINISTIC); - } else { - $native_connection->sqliteCreateFunction('REGEXP', self::regexp(...), 2, \PDO::SQLITE_DETERMINISTIC); - $native_connection->sqliteCreateFunction('FIELD', self::field(...), -1, \PDO::SQLITE_DETERMINISTIC); - $native_connection->sqliteCreateFunction('FIELD2', self::field2(...), 2, \PDO::SQLITE_DETERMINISTIC); - - //Create a new collation for natural sorting - $native_connection->sqliteCreateCollation('NATURAL_CMP', strnatcmp(...)); - - //Create a function for SI prefix value sorting - $native_connection->sqliteCreateFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, \PDO::SQLITE_DETERMINISTIC); - } + //Create a function for SI prefix value sorting + $native_connection->sqliteCreateFunction('SI_VALUE', SiValueSort::sqliteSiValue(...), 1, \PDO::SQLITE_DETERMINISTIC); } } @@ -132,4 +118,4 @@ class SQLiteRegexExtensionMiddlewareDriver extends AbstractDriverMiddleware return $index + 1; } -} +} \ No newline at end of file diff --git a/src/Entity/ProjectSystem/Project.php b/src/Entity/ProjectSystem/Project.php index f4a3e9de..a103d694 100644 --- a/src/Entity/ProjectSystem/Project.php +++ b/src/Entity/ProjectSystem/Project.php @@ -117,7 +117,7 @@ class Project extends AbstractStructuralDBElement /** * @var string|null The current status of the project */ - #[Assert\Choice(choices: ['draft', 'planning', 'in_production', 'finished', 'archived'])] + #[Assert\Choice(['draft', 'planning', 'in_production', 'finished', 'archived'])] #[Groups(['extended', 'full', 'project:read', 'project:write', 'import'])] #[ORM\Column(type: Types::STRING, length: 64, nullable: true)] protected ?string $status = null; diff --git a/src/Form/Type/AttachmentTypeType.php b/src/Form/Type/AttachmentTypeType.php index 95b5a254..099ed282 100644 --- a/src/Form/Type/AttachmentTypeType.php +++ b/src/Form/Type/AttachmentTypeType.php @@ -38,7 +38,7 @@ class AttachmentTypeType extends AbstractType return StructuralEntityType::class; } - public function configureOptions(OptionsResolver $resolver): void + public function configureOptions(OptionsResolver $resolver) { $resolver->define('attachment_filter_class')->allowedTypes('null', 'string')->default(null); diff --git a/src/Helpers/Projects/ProjectBuildRequest.php b/src/Helpers/Projects/ProjectBuildRequest.php index d2dfe139..430d37b5 100644 --- a/src/Helpers/Projects/ProjectBuildRequest.php +++ b/src/Helpers/Projects/ProjectBuildRequest.php @@ -84,10 +84,8 @@ final class ProjectBuildRequest $remaining_amount = $this->getNeededAmountForBOMEntry($bom_entry); foreach($this->getPartLotsForBOMEntry($bom_entry) as $lot) { //If the lot has instock use it for the build - $id = $lot->getID() ?? throw new \RuntimeException("Part lot needs to have an ID!"); - - $this->withdraw_amounts[$id] = min($remaining_amount, $lot->getAmount()); - $remaining_amount -= max(0, $this->withdraw_amounts[$id]); + $this->withdraw_amounts[$lot->getID()] = min($remaining_amount, $lot->getAmount()); + $remaining_amount -= max(0, $this->withdraw_amounts[$lot->getID()]); } } } @@ -178,10 +176,6 @@ final class ProjectBuildRequest { $lot_id = $lot instanceof PartLot ? $lot->getID() : $lot; - if ($lot_id === null) { - throw new \InvalidArgumentException('The given lot must have an ID!'); - } - if (! array_key_exists($lot_id, $this->withdraw_amounts)) { throw new \InvalidArgumentException('The given lot is not in the withdraw amounts array!'); } @@ -198,12 +192,10 @@ final class ProjectBuildRequest { if ($lot instanceof PartLot) { $lot_id = $lot->getID(); - } else { + } elseif (is_int($lot)) { $lot_id = $lot; - } - - if ($lot_id === null) { - throw new \InvalidArgumentException('The given lot must have an ID!'); + } else { + throw new \InvalidArgumentException('The given lot must be an instance of PartLot or an ID of a PartLot!'); } $this->withdraw_amounts[$lot_id] = $amount; @@ -304,7 +296,7 @@ final class ProjectBuildRequest * @param bool $dont_check_quantity * @return $this */ - public function setDontCheckQuantity(bool $dont_check_quantity): self + public function setDontCheckQuantity(bool $dont_check_quantity): ProjectBuildRequest { $this->dont_check_quantity = $dont_check_quantity; return $this; diff --git a/src/Repository/DBElementRepository.php b/src/Repository/DBElementRepository.php index 6e13aff7..f737d91d 100644 --- a/src/Repository/DBElementRepository.php +++ b/src/Repository/DBElementRepository.php @@ -158,6 +158,7 @@ class DBElementRepository extends EntityRepository { $reflection = new ReflectionClass($element::class); $property = $reflection->getProperty($field); + $property->setAccessible(true); $property->setValue($element, $new_value); } } diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php index cbfb4ee0..08b1c301 100644 --- a/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php +++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php @@ -233,6 +233,7 @@ trait PKImportHelperTrait $reflectionClass = new \ReflectionClass($entity); $property = $reflectionClass->getProperty('addedDate'); + $property->setAccessible(true); $property->setValue($entity, $date); } diff --git a/templates/log_system/details/_extra_collection_element_deleted.html.twig b/templates/log_system/details/_extra_collection_element_deleted.html.twig index 1c2ce2f3..221fae95 100644 --- a/templates/log_system/details/_extra_collection_element_deleted.html.twig +++ b/templates/log_system/details/_extra_collection_element_deleted.html.twig @@ -4,7 +4,7 @@

{% trans %}log.collection_deleted.deleted{% endtrans %}: - {{ type_label(entry.deletedElementClass) }} #{{ entry.deletedElementID }} + {{ entity_type_label(entry.deletedElementClass) }} #{{ entry.deletedElementID }} {% if entry.oldName is not empty %} ({{ entry.oldName }}) {% endif %} @@ -12,4 +12,4 @@

{% trans %}log.collection_deleted.on_collection{% endtrans %}: {{ log_helper.translate_field(entry.collectionName) }} -

+

\ No newline at end of file diff --git a/templates/parts/info/_picture.html.twig b/templates/parts/info/_picture.html.twig index db6c59ca..e6aa74b3 100644 --- a/templates/parts/info/_picture.html.twig +++ b/templates/parts/info/_picture.html.twig @@ -19,7 +19,7 @@ {% endif %} @@ -41,4 +41,4 @@ {% else %} Part main image -{% endif %} +{% endif %} \ No newline at end of file diff --git a/templates/parts/info/show_part_info.html.twig b/templates/parts/info/show_part_info.html.twig index b36ab047..96b5e209 100644 --- a/templates/parts/info/show_part_info.html.twig +++ b/templates/parts/info/show_part_info.html.twig @@ -22,7 +22,7 @@ ({{ timeTravel | format_datetime('short') }}) {% endif %} {% if part.projectBuildPart %} - ({{ type_label(part.builtProject) }}: {{ part.builtProject.name }}) + ({{ entity_type_label(part.builtProject) }}: {{ part.builtProject.name }}) {% endif %} diff --git a/tests/Doctrine/Functions/SiValueSortTest.php b/tests/Doctrine/Functions/SiValueSortTest.php index 4ef8a397..dbdd9d28 100644 --- a/tests/Doctrine/Functions/SiValueSortTest.php +++ b/tests/Doctrine/Functions/SiValueSortTest.php @@ -26,7 +26,6 @@ use App\Doctrine\Functions\SiValueSort; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Platforms\SQLitePlatform; -use PHPUnit\Framework\Attributes\DataProvider; final class SiValueSortTest extends AbstractDoctrineFunctionTestCase { @@ -72,7 +71,9 @@ final class SiValueSortTest extends AbstractDoctrineFunctionTestCase $this->assertSame('SI_VALUE(part_name)', $sql); } - #[DataProvider('sqliteSiValueProvider')] + /** + * @dataProvider sqliteSiValueProvider + */ public function testSqliteSiValue(?string $input, ?float $expected): void { $result = SiValueSort::sqliteSiValue($input); diff --git a/tests/Entity/Attachments/AttachmentTest.php b/tests/Entity/Attachments/AttachmentTest.php index bef2df16..9e912b97 100644 --- a/tests/Entity/Attachments/AttachmentTest.php +++ b/tests/Entity/Attachments/AttachmentTest.php @@ -276,6 +276,7 @@ final class AttachmentTest extends TestCase { $reflection = new ReflectionClass($object); $reflection_property = $reflection->getProperty($property); + $reflection_property->setAccessible(true); $reflection_property->setValue($object, $value); } diff --git a/tests/Helpers/Projects/ProjectBuildRequestTest.php b/tests/Helpers/Projects/ProjectBuildRequestTest.php index 3014f762..c1fd1498 100644 --- a/tests/Helpers/Projects/ProjectBuildRequestTest.php +++ b/tests/Helpers/Projects/ProjectBuildRequestTest.php @@ -82,12 +82,7 @@ final class ProjectBuildRequestTest extends TestCase $part2->setName('Part 2'); $part2->setPartUnit($float_unit); - $this->lot2 = new class extends PartLot { - public function getID(): ?int - { - return 3; - } - };; + $this->lot2 = new PartLot(); $part2->addPartLot($this->lot2); $this->lot2->setAmount(2.5); $this->lot2->setDescription('Lot 2'); diff --git a/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php b/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php index d466f048..ef446c9a 100644 --- a/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php +++ b/tests/Services/InfoProviderSystem/Providers/BuerklinProviderTest.php @@ -77,6 +77,7 @@ final class BuerklinProviderTest extends TestCase public function testAttributesToParametersParsesUnitsAndValues(): void { $method = new \ReflectionMethod(BuerklinProvider::class, 'attributesToParameters'); + $method->setAccessible(true); $features = [ [ @@ -126,6 +127,7 @@ final class BuerklinProviderTest extends TestCase public function testComplianceParameters(): void { $method = new \ReflectionMethod(BuerklinProvider::class, 'complianceToParameters'); + $method->setAccessible(true); $product = [ 'labelRoHS' => 'Yes', @@ -156,6 +158,7 @@ final class BuerklinProviderTest extends TestCase public function testImageSelectionPrefersZoomAndDeduplicates(): void { $method = new \ReflectionMethod(BuerklinProvider::class, 'getProductImages'); + $method->setAccessible(true); $images = [ ['format' => 'product', 'url' => '/img/a.webp'], @@ -173,6 +176,7 @@ final class BuerklinProviderTest extends TestCase public function testFootprintExtraction(): void { $method = new \ReflectionMethod(BuerklinProvider::class, 'getPartDetail'); + $method->setAccessible(true); $product = [ 'code' => 'TEST1', @@ -208,6 +212,7 @@ final class BuerklinProviderTest extends TestCase ]; $method = new \ReflectionMethod(BuerklinProvider::class, 'pricesToVendorInfo'); + $method->setAccessible(true); $vendorInfo = $method->invoke($this->provider, 'SKU1', 'https://x', $detailPrice); @@ -255,6 +260,7 @@ final class BuerklinProviderTest extends TestCase ); $method = new \ReflectionMethod(BuerklinProvider::class, 'convertPartDetailToSearchResult'); + $method->setAccessible(true); $dto = $method->invoke($this->provider, $detail); @@ -267,13 +273,13 @@ final class BuerklinProviderTest extends TestCase { $this->assertSame(['buerklin.com'], $this->provider->getHandledDomains()); } - + #[DataProvider('buerklinIdFromUrlProvider')] public function testGetIDFromURLExtractsId(string $url, ?string $expected): void { $this->assertSame($expected, $this->provider->getIDFromURL($url)); } - + public static function buerklinIdFromUrlProvider(): \Iterator { yield 'de long path' => [ diff --git a/tests/Services/InfoProviderSystem/Providers/LCSCProviderTest.php b/tests/Services/InfoProviderSystem/Providers/LCSCProviderTest.php index 9ec3abe1..dc19de6b 100644 --- a/tests/Services/InfoProviderSystem/Providers/LCSCProviderTest.php +++ b/tests/Services/InfoProviderSystem/Providers/LCSCProviderTest.php @@ -367,6 +367,7 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('sanitizeField'); + $method->setAccessible(true); $this->assertNull($method->invokeArgs($this->provider, [null])); $this->assertEquals('Clean text', $method->invokeArgs($this->provider, ['Clean text'])); @@ -377,6 +378,7 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('getUsedCurrency'); + $method->setAccessible(true); $this->assertEquals('USD', $method->invokeArgs($this->provider, ['US$'])); $this->assertEquals('USD', $method->invokeArgs($this->provider, ['$'])); @@ -389,6 +391,7 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('getProductShortURL'); + $method->setAccessible(true); $result = $method->invokeArgs($this->provider, ['C123456']); $this->assertEquals('https://www.lcsc.com/product-detail/C123456.html', $result); @@ -398,6 +401,7 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('getProductDatasheets'); + $method->setAccessible(true); $result = $method->invokeArgs($this->provider, [null]); $this->assertIsArray($result); @@ -413,6 +417,7 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('getProductImages'); + $method->setAccessible(true); $result = $method->invokeArgs($this->provider, [null]); $this->assertIsArray($result); @@ -429,6 +434,7 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('attributesToParameters'); + $method->setAccessible(true); $attributes = [ ['paramNameEn' => 'Resistance', 'paramValueEn' => '1kΩ'], @@ -448,6 +454,7 @@ final class LCSCProviderTest extends TestCase { $reflection = new \ReflectionClass($this->provider); $method = $reflection->getMethod('pricesToVendorInfo'); + $method->setAccessible(true); $prices = [ ['ladder' => 1, 'productPrice' => '0.10', 'currencySymbol' => 'US$'],