Fixed some inspection issues

This commit is contained in:
Jan Böhmer 2024-03-03 19:57:31 +01:00
parent 33475dca66
commit 42e604245c
85 changed files with 272 additions and 291 deletions

View file

@ -30,8 +30,9 @@ use Symfony\Component\PropertyInfo\Type;
class EntityFilterHelper class EntityFilterHelper
{ {
public function __construct(private NodesListBuilder $nodesListBuilder, public function __construct(
private EntityManagerInterface $entityManager) private readonly NodesListBuilder $nodesListBuilder,
private readonly EntityManagerInterface $entityManager)
{ {
} }
@ -86,7 +87,7 @@ class EntityFilterHelper
$description = []; $description = [];
foreach ($properties as $property => $strategy) { foreach ($properties as $property => $strategy) {
$description["$property"] = [ $description[(string)$property] = [
'property' => $property, 'property' => $property,
'type' => Type::BUILTIN_TYPE_STRING, 'type' => Type::BUILTIN_TYPE_STRING,
'required' => false, 'required' => false,

View file

@ -62,7 +62,7 @@ final class LikeFilter extends AbstractFilter
$description = []; $description = [];
foreach ($this->properties as $property => $strategy) { foreach ($this->properties as $property => $strategy) {
$description["$property"] = [ $description[(string)$property] = [
'property' => $property, 'property' => $property,
'type' => Type::BUILTIN_TYPE_STRING, 'type' => Type::BUILTIN_TYPE_STRING,
'required' => false, 'required' => false,

View file

@ -62,8 +62,6 @@ final class HandleAttachmentsUploadsProcessor implements ProcessorInterface
$this->attachmentSubmitHandler->handleUpload($data, $upload); $this->attachmentSubmitHandler->handleUpload($data, $upload);
} }
$result = $this->persistProcessor->process($data, $operation, $uriVariables, $context); return $this->persistProcessor->process($data, $operation, $uriVariables, $context);
return $result;
} }
} }

View file

@ -55,7 +55,7 @@ class AttachmentTypeController extends BaseAdminController
return $this->_delete($request, $entity, $recursionHelper); return $this->_delete($request, $entity, $recursionHelper);
} }
#[Route(path: '/{id}/edit/{timestamp}', requirements: ['id' => '\d+'], name: 'attachment_type_edit')] #[Route(path: '/{id}/edit/{timestamp}', name: 'attachment_type_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}', requirements: ['id' => '\d+'])] #[Route(path: '/{id}', requirements: ['id' => '\d+'])]
public function edit(AttachmentType $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response public function edit(AttachmentType $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
{ {

View file

@ -259,10 +259,6 @@ abstract class BaseAdminController extends AbstractController
$attachments = $form['attachments']; $attachments = $form['attachments'];
foreach ($attachments as $attachment) { foreach ($attachments as $attachment) {
/** @var FormInterface $attachment */ /** @var FormInterface $attachment */
$options = [
'secure_attachment' => $attachment['secureFile']->getData(),
'download_url' => $attachment['downloadURL']->getData(),
];
try { try {
$this->attachmentSubmitHandler->handleUpload( $this->attachmentSubmitHandler->handleUpload(

View file

@ -54,7 +54,7 @@ class CategoryController extends BaseAdminController
return $this->_delete($request, $entity, $recursionHelper); return $this->_delete($request, $entity, $recursionHelper);
} }
#[Route(path: '/{id}/edit/{timestamp}', requirements: ['id' => '\d+'], name: 'category_edit')] #[Route(path: '/{id}/edit/{timestamp}', name: 'category_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}', requirements: ['id' => '\d+'])] #[Route(path: '/{id}', requirements: ['id' => '\d+'])]
public function edit(Category $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response public function edit(Category $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
{ {

View file

@ -124,7 +124,7 @@ class CurrencyController extends BaseAdminController
return true; return true;
} }
#[Route(path: '/{id}/edit/{timestamp}', requirements: ['id' => '\d+'], name: 'currency_edit')] #[Route(path: '/{id}/edit/{timestamp}', name: 'currency_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}', requirements: ['id' => '\d+'])] #[Route(path: '/{id}', requirements: ['id' => '\d+'])]
public function edit(Currency $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response public function edit(Currency $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
{ {

View file

@ -55,7 +55,7 @@ class FootprintController extends BaseAdminController
return $this->_delete($request, $entity, $recursionHelper); return $this->_delete($request, $entity, $recursionHelper);
} }
#[Route(path: '/{id}/edit/{timestamp}', requirements: ['id' => '\d+'], name: 'footprint_edit')] #[Route(path: '/{id}/edit/{timestamp}', name: 'footprint_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}', requirements: ['id' => '\d+'])] #[Route(path: '/{id}', requirements: ['id' => '\d+'])]
public function edit(Footprint $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response public function edit(Footprint $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
{ {

View file

@ -55,7 +55,7 @@ class LabelProfileController extends BaseAdminController
return $this->_delete($request, $entity, $recursionHelper); return $this->_delete($request, $entity, $recursionHelper);
} }
#[Route(path: '/{id}/edit/{timestamp}', requirements: ['id' => '\d+'], name: 'label_profile_edit')] #[Route(path: '/{id}/edit/{timestamp}', name: 'label_profile_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}', requirements: ['id' => '\d+'])] #[Route(path: '/{id}', requirements: ['id' => '\d+'])]
public function edit(LabelProfile $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response public function edit(LabelProfile $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
{ {

View file

@ -54,7 +54,7 @@ class ManufacturerController extends BaseAdminController
return $this->_delete($request, $entity, $recursionHelper); return $this->_delete($request, $entity, $recursionHelper);
} }
#[Route(path: '/{id}/edit/{timestamp}', requirements: ['id' => '\d+'], name: 'manufacturer_edit')] #[Route(path: '/{id}/edit/{timestamp}', name: 'manufacturer_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}', requirements: ['id' => '\d+'])] #[Route(path: '/{id}', requirements: ['id' => '\d+'])]
public function edit(Manufacturer $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response public function edit(Manufacturer $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
{ {

View file

@ -55,7 +55,7 @@ class MeasurementUnitController extends BaseAdminController
return $this->_delete($request, $entity, $recursionHelper); return $this->_delete($request, $entity, $recursionHelper);
} }
#[Route(path: '/{id}/edit/{timestamp}', requirements: ['id' => '\d+'], name: 'measurement_unit_edit')] #[Route(path: '/{id}/edit/{timestamp}', name: 'measurement_unit_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}', requirements: ['id' => '\d+'])] #[Route(path: '/{id}', requirements: ['id' => '\d+'])]
public function edit(MeasurementUnit $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response public function edit(MeasurementUnit $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
{ {

View file

@ -51,7 +51,7 @@ class ProjectAdminController extends BaseAdminController
return $this->_delete($request, $entity, $recursionHelper); return $this->_delete($request, $entity, $recursionHelper);
} }
#[Route(path: '/{id}/edit/{timestamp}', requirements: ['id' => '\d+'], name: 'project_edit')] #[Route(path: '/{id}/edit/{timestamp}', name: 'project_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}/edit', requirements: ['id' => '\d+'])] #[Route(path: '/{id}/edit', requirements: ['id' => '\d+'])]
public function edit(Project $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response public function edit(Project $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
{ {

View file

@ -54,7 +54,7 @@ class StorageLocationController extends BaseAdminController
return $this->_delete($request, $entity, $recursionHelper); return $this->_delete($request, $entity, $recursionHelper);
} }
#[Route(path: '/{id}/edit/{timestamp}', requirements: ['id' => '\d+'], name: 'store_location_edit')] #[Route(path: '/{id}/edit/{timestamp}', name: 'store_location_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}', requirements: ['id' => '\d+'])] #[Route(path: '/{id}', requirements: ['id' => '\d+'])]
public function edit(StorageLocation $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response public function edit(StorageLocation $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
{ {

View file

@ -54,7 +54,7 @@ class SupplierController extends BaseAdminController
return $this->_delete($request, $entity, $recursionHelper); return $this->_delete($request, $entity, $recursionHelper);
} }
#[Route(path: '/{id}/edit/{timestamp}', requirements: ['id' => '\d+'], name: 'supplier_edit')] #[Route(path: '/{id}/edit/{timestamp}', name: 'supplier_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}', requirements: ['id' => '\d+'])] #[Route(path: '/{id}', requirements: ['id' => '\d+'])]
public function edit(Supplier $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response public function edit(Supplier $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
{ {

View file

@ -49,7 +49,7 @@ class GroupController extends BaseAdminController
protected string $attachment_class = GroupAttachment::class; protected string $attachment_class = GroupAttachment::class;
protected ?string $parameter_class = GroupParameter::class; protected ?string $parameter_class = GroupParameter::class;
#[Route(path: '/{id}/edit/{timestamp}', requirements: ['id' => '\d+'], name: 'group_edit')] #[Route(path: '/{id}/edit/{timestamp}', name: 'group_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}/', requirements: ['id' => '\d+'])] #[Route(path: '/{id}/', requirements: ['id' => '\d+'])]
public function edit(Group $entity, Request $request, EntityManagerInterface $em, PermissionPresetsHelper $permissionPresetsHelper, PermissionSchemaUpdater $permissionSchemaUpdater, ?string $timestamp = null): Response public function edit(Group $entity, Request $request, EntityManagerInterface $em, PermissionPresetsHelper $permissionPresetsHelper, PermissionSchemaUpdater $permissionSchemaUpdater, ?string $timestamp = null): Response
{ {

View file

@ -58,7 +58,7 @@ class RedirectController extends AbstractController
//If either mod_rewrite is not enabled or the index.php version is enforced, add index.php to the string //If either mod_rewrite is not enabled or the index.php version is enforced, add index.php to the string
if (($this->enforce_index_php || !$this->checkIfModRewriteAvailable()) if (($this->enforce_index_php || !$this->checkIfModRewriteAvailable())
&& !str_contains((string) $new_url, 'index.php')) { && !str_contains($new_url, 'index.php')) {
//Like Request::getUriForPath only with index.php //Like Request::getUriForPath only with index.php
$new_url = $request->getSchemeAndHttpHost().$request->getBaseUrl().'/index.php/'.$locale.$request->getPathInfo(); $new_url = $request->getSchemeAndHttpHost().$request->getBaseUrl().'/index.php/'.$locale.$request->getPathInfo();
} }

View file

@ -105,7 +105,7 @@ class ToolsController extends AbstractController
$this->denyAccessUnlessGranted('@tools.builtin_footprints_viewer'); $this->denyAccessUnlessGranted('@tools.builtin_footprints_viewer');
$grouped_footprints = $builtinAttachmentsFinder->getListOfFootprintsGroupedByFolder(); $grouped_footprints = $builtinAttachmentsFinder->getListOfFootprintsGroupedByFolder();
$grouped_footprints = array_map(fn($group) => array_map(fn($placeholder_filepath) => [ $grouped_footprints = array_map(static fn($group) => array_map(static fn($placeholder_filepath) => [
'filename' => basename((string) $placeholder_filepath), 'filename' => basename((string) $placeholder_filepath),
'assets_path' => $urlGenerator->placeholderPathToAssetPath($placeholder_filepath), 'assets_path' => $urlGenerator->placeholderPathToAssetPath($placeholder_filepath),
], $group), $grouped_footprints); ], $group), $grouped_footprints);

View file

@ -78,7 +78,7 @@ class UserController extends BaseAdminController
* *
* @throws Exception * @throws Exception
*/ */
#[Route(path: '/{id}/edit/{timestamp}', requirements: ['id' => '\d+'], name: 'user_edit')] #[Route(path: '/{id}/edit/{timestamp}', name: 'user_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}/', requirements: ['id' => '\d+'])] #[Route(path: '/{id}/', requirements: ['id' => '\d+'])]
public function edit(User $entity, Request $request, EntityManagerInterface $em, PermissionPresetsHelper $permissionPresetsHelper, public function edit(User $entity, Request $request, EntityManagerInterface $em, PermissionPresetsHelper $permissionPresetsHelper,
PermissionSchemaUpdater $permissionSchemaUpdater, ValidatorInterface $validator, ?string $timestamp = null): Response PermissionSchemaUpdater $permissionSchemaUpdater, ValidatorInterface $validator, ?string $timestamp = null): Response
@ -166,7 +166,7 @@ class UserController extends BaseAdminController
return $this->_new($request, $em, $importer, $entity); return $this->_new($request, $em, $importer, $entity);
} }
#[Route(path: '/{id}', name: 'user_delete', methods: ['DELETE'], requirements: ['id' => '\d+'])] #[Route(path: '/{id}', name: 'user_delete', requirements: ['id' => '\d+'], methods: ['DELETE'])]
public function delete(Request $request, User $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse public function delete(Request $request, User $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse
{ {
//Disallow deleting the anonymous user //Disallow deleting the anonymous user

View file

@ -221,7 +221,7 @@ final class AttachmentDataTable implements DataTableTypeInterface
//We do the most stuff here in the filter class //We do the most stuff here in the filter class
if (isset($options['filter'])) { if (isset($options['filter'])) {
if(!$options['filter'] instanceof AttachmentFilter) { if(!$options['filter'] instanceof AttachmentFilter) {
throw new \Exception('filter must be an instance of AttachmentFilter!'); throw new \RuntimeException('filter must be an instance of AttachmentFilter!');
} }
$filter = $options['filter']; $filter = $options['filter'];

View file

@ -35,9 +35,9 @@ class MarkdownColumn extends AbstractColumn
* The normalize function is responsible for converting parsed and processed data to a datatables-appropriate type. * The normalize function is responsible for converting parsed and processed data to a datatables-appropriate type.
* *
* @param mixed $value The single value of the column * @param mixed $value The single value of the column
* @return mixed * @return string
*/ */
public function normalize($value): mixed public function normalize($value): string
{ {
return $this->markdown->markForRendering($value, true); return $this->markdown->markForRendering($value, true);
} }

View file

@ -45,6 +45,7 @@ use App\Entity\Parts\PartLot;
use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\Project;
use App\Services\EntityURLGenerator; use App\Services\EntityURLGenerator;
use App\Services\Formatters\AmountFormatter; use App\Services\Formatters\AmountFormatter;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider; use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider;
@ -235,7 +236,7 @@ final class PartsDataTable implements DataTableTypeInterface
'filter_query' => $this->getFilterQuery(...), 'filter_query' => $this->getFilterQuery(...),
'detail_query' => $this->getDetailQuery(...), 'detail_query' => $this->getDetailQuery(...),
'entity' => Part::class, 'entity' => Part::class,
'hydrate' => Query::HYDRATE_OBJECT, 'hydrate' => AbstractQuery::HYDRATE_OBJECT,
//Use the simple total query, as we just want to get the total number of parts without any conditions //Use the simple total query, as we just want to get the total number of parts without any conditions
//For this the normal query would be pretty slow //For this the normal query would be pretty slow
'simple_total_query' => true, 'simple_total_query' => true,
@ -270,7 +271,7 @@ final class PartsDataTable implements DataTableTypeInterface
private function getDetailQuery(QueryBuilder $builder, array $filter_results): void private function getDetailQuery(QueryBuilder $builder, array $filter_results): void
{ {
$ids = array_map(fn($row) => $row['id'], $filter_results); $ids = array_map(static fn($row) => $row['id'], $filter_results);
/* /*
* In this query we take the IDs which were filtered, paginated and sorted in the filter query, and fetch the * In this query we take the IDs which were filtered, paginated and sorted in the filter query, and fetch the

View file

@ -97,7 +97,7 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
} }
//@phpstan-ignore-next-line //@phpstan-ignore-next-line
throw new \Exception('This should never happen!'); throw new \RuntimeException('This should never happen!');
}, },
]) ])
->add('ipn', TextColumn::class, [ ->add('ipn', TextColumn::class, [

View file

@ -25,6 +25,7 @@ namespace App\Doctrine\Functions;
use Doctrine\ORM\Query\AST\Functions\FunctionNode; use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer; use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\TokenType;
/** /**
* Basically the same as the original Field function, but uses FIELD2 for the SQL query. * Basically the same as the original Field function, but uses FIELD2 for the SQL query.
@ -38,8 +39,8 @@ class Field2 extends FunctionNode
public function parse(\Doctrine\ORM\Query\Parser $parser): void public function parse(\Doctrine\ORM\Query\Parser $parser): void
{ {
$parser->match(Lexer::T_IDENTIFIER); $parser->match(TokenType::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS); $parser->match(TokenType::T_OPEN_PARENTHESIS);
// Do the field. // Do the field.
$this->field = $parser->ArithmeticPrimary(); $this->field = $parser->ArithmeticPrimary();
@ -50,12 +51,12 @@ class Field2 extends FunctionNode
$lexer = $parser->getLexer(); $lexer = $parser->getLexer();
while (count($this->values) < 1 || while (count($this->values) < 1 ||
$lexer->lookahead['type'] != Lexer::T_CLOSE_PARENTHESIS) { $lexer->lookahead['type'] != TokenType::T_CLOSE_PARENTHESIS) {
$parser->match(Lexer::T_COMMA); $parser->match(TokenType::T_COMMA);
$this->values[] = $parser->ArithmeticPrimary(); $this->values[] = $parser->ArithmeticPrimary();
} }
$parser->match(Lexer::T_CLOSE_PARENTHESIS); $parser->match(TokenType::T_CLOSE_PARENTHESIS);
} }
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker): string public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker): string

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiFilter;
@ -61,10 +62,10 @@ use LogicException;
#[ORM\DiscriminatorMap(self::ORM_DISCRIMINATOR_MAP)] #[ORM\DiscriminatorMap(self::ORM_DISCRIMINATOR_MAP)]
#[ORM\EntityListeners([AttachmentDeleteListener::class])] #[ORM\EntityListeners([AttachmentDeleteListener::class])]
#[ORM\Table(name: '`attachments`')] #[ORM\Table(name: '`attachments`')]
#[ORM\Index(name: 'attachments_idx_id_element_id_class_name', columns: ['id', 'element_id', 'class_name'])] #[ORM\Index(columns: ['id', 'element_id', 'class_name'], name: 'attachments_idx_id_element_id_class_name')]
#[ORM\Index(name: 'attachments_idx_class_name_id', columns: ['class_name', 'id'])] #[ORM\Index(columns: ['class_name', 'id'], name: 'attachments_idx_class_name_id')]
#[ORM\Index(name: 'attachment_name_idx', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'attachment_name_idx')]
#[ORM\Index(name: 'attachment_element_idx', columns: ['class_name', 'element_id'])] #[ORM\Index(columns: ['class_name', 'element_id'], name: 'attachment_element_idx')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)'), new Get(security: 'is_granted("read", object)'),
@ -84,7 +85,7 @@ use LogicException;
description: 'The URL to a thumbnail version of this file. This only exists for internal picture attachments.')] description: 'The URL to a thumbnail version of this file. This only exists for internal picture attachments.')]
#[ApiFilter(LikeFilter::class, properties: ["name"])] #[ApiFilter(LikeFilter::class, properties: ["name"])]
#[ApiFilter(EntityFilter::class, properties: ["attachment_type"])] #[ApiFilter(EntityFilter::class, properties: ["attachment_type"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
//This discriminator map is required for API platform to know which class to use for deserialization, when creating a new attachment. //This discriminator map is required for API platform to know which class to use for deserialization, when creating a new attachment.
#[DiscriminatorMap(typeProperty: '_type', mapping: self::API_DISCRIMINATOR_MAP)] #[DiscriminatorMap(typeProperty: '_type', mapping: self::API_DISCRIMINATOR_MAP)]
@ -151,7 +152,7 @@ abstract class Attachment extends AbstractNamedDBElement
/** /**
* @var string The path to the file relative to a placeholder path like %MEDIA% * @var string The path to the file relative to a placeholder path like %MEDIA%
*/ */
#[ORM\Column(type: Types::STRING, name: 'path')] #[ORM\Column(name: 'path', type: Types::STRING)]
protected string $path = ''; protected string $path = '';
/** /**
@ -176,7 +177,7 @@ abstract class Attachment extends AbstractNamedDBElement
#[Assert\NotNull(message: 'validator.attachment.must_not_be_null')] #[Assert\NotNull(message: 'validator.attachment.must_not_be_null')]
#[ORM\ManyToOne(targetEntity: AttachmentType::class, inversedBy: 'attachments_with_type')] #[ORM\ManyToOne(targetEntity: AttachmentType::class, inversedBy: 'attachments_with_type')]
#[ORM\JoinColumn(name: 'type_id', nullable: false)] #[ORM\JoinColumn(name: 'type_id', nullable: false)]
#[Selectable()] #[Selectable]
#[Groups(['attachment:read', 'attachment:write'])] #[Groups(['attachment:read', 'attachment:write'])]
protected ?AttachmentType $attachment_type = null; protected ?AttachmentType $attachment_type = null;
@ -246,12 +247,7 @@ abstract class Attachment extends AbstractNamedDBElement
$extension = pathinfo(parse_url($this->path, PHP_URL_PATH) ?? '', PATHINFO_EXTENSION); $extension = pathinfo(parse_url($this->path, PHP_URL_PATH) ?? '', PATHINFO_EXTENSION);
//If no extension is found or it is known picture extension, we assume that this is a picture extension //If no extension is found or it is known picture extension, we assume that this is a picture extension
if ($extension === '' || in_array(strtolower($extension), static::PICTURE_EXTS, true)) { return $extension === '' || in_array(strtolower($extension), static::PICTURE_EXTS, true);
return true;
}
//Otherwise we assume that the file is not a picture
return false;
} }
$extension = pathinfo($this->getPath(), PATHINFO_EXTENSION); $extension = pathinfo($this->getPath(), PATHINFO_EXTENSION);
@ -521,6 +517,11 @@ abstract class Attachment extends AbstractNamedDBElement
#[SerializedName('url')] #[SerializedName('url')]
public function setURL(?string $url): self public function setURL(?string $url): self
{ {
//Do nothing if the URL is empty
if ($url === null || $url === '') {
return $this;
}
$url = trim($url); $url = trim($url);
//Escape spaces in URL //Escape spaces in URL
$url = str_replace(' ', '%20', $url); $url = str_replace(' ', '%20', $url);

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\Attachments; namespace App\Entity\Attachments;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiFilter;
@ -55,8 +56,8 @@ use Symfony\Component\Validator\Constraints as Assert;
*/ */
#[ORM\Entity(repositoryClass: StructuralDBElementRepository::class)] #[ORM\Entity(repositoryClass: StructuralDBElementRepository::class)]
#[ORM\Table(name: '`attachment_types`')] #[ORM\Table(name: '`attachment_types`')]
#[ORM\Index(name: 'attachment_types_idx_name', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'attachment_types_idx_name')]
#[ORM\Index(name: 'attachment_types_idx_parent_name', columns: ['parent_id', 'name'])] #[ORM\Index(columns: ['parent_id', 'name'], name: 'attachment_types_idx_parent_name')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)'), new Get(security: 'is_granted("read", object)'),
@ -81,11 +82,11 @@ use Symfony\Component\Validator\Constraints as Assert;
)] )]
#[ApiFilter(PropertyFilter::class)] #[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] #[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
class AttachmentType extends AbstractStructuralDBElement class AttachmentType extends AbstractStructuralDBElement
{ {
#[ORM\OneToMany(targetEntity: AttachmentType::class, mappedBy: 'parent', cascade: ['persist'])] #[ORM\OneToMany(mappedBy: 'parent', targetEntity: AttachmentType::class, cascade: ['persist'])]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $children; protected Collection $children;
@ -109,7 +110,7 @@ class AttachmentType extends AbstractStructuralDBElement
* @var Collection<int, AttachmentTypeAttachment> * @var Collection<int, AttachmentTypeAttachment>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: AttachmentTypeAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: AttachmentTypeAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
#[Groups(['attachment_type:read', 'attachment_type:write'])] #[Groups(['attachment_type:read', 'attachment_type:write'])]
protected Collection $attachments; protected Collection $attachments;
@ -122,7 +123,7 @@ class AttachmentType extends AbstractStructuralDBElement
/** @var Collection<int, AttachmentTypeParameter> /** @var Collection<int, AttachmentTypeParameter>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: AttachmentTypeParameter::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: AttachmentTypeParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])] #[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])]
#[Groups(['attachment_type:read', 'attachment_type:write'])] #[Groups(['attachment_type:read', 'attachment_type:write'])]
protected Collection $parameters; protected Collection $parameters;
@ -130,7 +131,7 @@ class AttachmentType extends AbstractStructuralDBElement
/** /**
* @var Collection<Attachment> * @var Collection<Attachment>
*/ */
#[ORM\OneToMany(targetEntity: Attachment::class, mappedBy: 'attachment_type')] #[ORM\OneToMany(mappedBy: 'attachment_type', targetEntity: Attachment::class)]
protected Collection $attachments_with_type; protected Collection $attachments_with_type;
#[Groups(['attachment_type:read'])] #[Groups(['attachment_type:read'])]

View file

@ -62,7 +62,7 @@ use Symfony\Component\Serializer\Annotation\Groups;
* @extends AttachmentContainingDBElement<AT> * @extends AttachmentContainingDBElement<AT>
* @uses ParametersTrait<PT> * @uses ParametersTrait<PT>
*/ */
#[UniqueEntity(fields: ['name', 'parent'], ignoreNull: false, message: 'structural.entity.unique_name')] #[UniqueEntity(fields: ['name', 'parent'], message: 'structural.entity.unique_name', ignoreNull: false)]
#[ORM\MappedSuperclass(repositoryClass: StructuralDBElementRepository::class)] #[ORM\MappedSuperclass(repositoryClass: StructuralDBElementRepository::class)]
#[ORM\EntityListeners([TreeCacheInvalidationListener::class])] #[ORM\EntityListeners([TreeCacheInvalidationListener::class])]
abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
@ -118,7 +118,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
* @var Collection<int, AbstractParameter> * @var Collection<int, AbstractParameter>
* @phpstan-var Collection<int, PT> * @phpstan-var Collection<int, PT>
*/ */
#[Assert\Valid()] #[Assert\Valid]
protected Collection $parameters; protected Collection $parameters;
/** @var string[] all names of all parent elements as an array of strings, /** @var string[] all names of all parent elements as an array of strings,
@ -176,7 +176,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
throw new InvalidArgumentException('isChildOf() only works for objects of the same type!'); throw new InvalidArgumentException('isChildOf() only works for objects of the same type!');
} }
if (!$this->getParent() instanceof \App\Entity\Base\AbstractStructuralDBElement) { // this is the root node if (!$this->getParent() instanceof self) { // this is the root node
return false; return false;
} }
@ -244,9 +244,9 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
/* /*
* Only check for nodes that have a parent. In the other cases zero is correct. * Only check for nodes that have a parent. In the other cases zero is correct.
*/ */
if (0 === $this->level && $this->parent instanceof \App\Entity\Base\AbstractStructuralDBElement) { if (0 === $this->level && $this->parent instanceof self) {
$element = $this->parent; $element = $this->parent;
while ($element instanceof \App\Entity\Base\AbstractStructuralDBElement) { while ($element instanceof self) {
/** @var AbstractStructuralDBElement $element */ /** @var AbstractStructuralDBElement $element */
$element = $element->parent; $element = $element->parent;
++$this->level; ++$this->level;
@ -274,7 +274,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
$overflow = 20; //We only allow 20 levels depth $overflow = 20; //We only allow 20 levels depth
while ($element->parent instanceof \App\Entity\Base\AbstractStructuralDBElement && $overflow >= 0) { while ($element->parent instanceof self && $overflow >= 0) {
$element = $element->parent; $element = $element->parent;
$this->full_path_strings[] = $element->getName(); $this->full_path_strings[] = $element->getName();
//Decrement to prevent mem overflow. //Decrement to prevent mem overflow.
@ -360,7 +360,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
$this->parent = $new_parent; $this->parent = $new_parent;
//Add this element as child to the new parent //Add this element as child to the new parent
if ($new_parent instanceof \App\Entity\Base\AbstractStructuralDBElement) { if ($new_parent instanceof self) {
$new_parent->getChildren()->add($this); $new_parent->getChildren()->add($this);
} }
@ -445,7 +445,7 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
public function setAlternativeNames(?string $new_value): self public function setAlternativeNames(?string $new_value): self
{ {
//Add a trailing comma, if not already there (makes it easier to find in the database) //Add a trailing comma, if not already there (makes it easier to find in the database)
if (is_string($new_value) && substr($new_value, -1) !== ',') { if (is_string($new_value) && !str_ends_with($new_value, ',')) {
$new_value .= ','; $new_value .= ',';
} }

View file

@ -85,7 +85,7 @@ class LabelOptions
/** @var LabelProcessMode The mode that will be used to interpret the lines /** @var LabelProcessMode The mode that will be used to interpret the lines
*/ */
#[ORM\Column(type: Types::STRING, enumType: LabelProcessMode::class, name: 'lines_mode')] #[ORM\Column(name: 'lines_mode', type: Types::STRING, enumType: LabelProcessMode::class)]
protected LabelProcessMode $process_mode = LabelProcessMode::PLACEHOLDER; protected LabelProcessMode $process_mode = LabelProcessMode::PLACEHOLDER;
/** /**

View file

@ -66,7 +66,7 @@ class LabelProfile extends AttachmentContainingDBElement
/** /**
* @var Collection<int, LabelAttachment> * @var Collection<int, LabelAttachment>
*/ */
#[ORM\OneToMany(targetEntity: LabelAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: LabelAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $attachments; protected Collection $attachments;

View file

@ -147,65 +147,38 @@ class CollectionElementDeleted extends AbstractLogEntry implements LogWithEventU
private function resolveAbstractClassToInstantiableClass(string $abstract_class): string private function resolveAbstractClassToInstantiableClass(string $abstract_class): string
{ {
if (is_a($abstract_class, AbstractParameter::class, true)) { if (is_a($abstract_class, AbstractParameter::class, true)) {
switch ($this->getTargetClass()) { return match ($this->getTargetClass()) {
case AttachmentType::class: AttachmentType::class => AttachmentTypeParameter::class,
return AttachmentTypeParameter::class; Category::class => CategoryParameter::class,
case Category::class: Currency::class => CurrencyParameter::class,
return CategoryParameter::class; Project::class => ProjectParameter::class,
case Currency::class: Footprint::class => FootprintParameter::class,
return CurrencyParameter::class; Group::class => GroupParameter::class,
case Project::class: Manufacturer::class => ManufacturerParameter::class,
return ProjectParameter::class; MeasurementUnit::class => MeasurementUnitParameter::class,
case Footprint::class: Part::class => PartParameter::class,
return FootprintParameter::class; StorageLocation::class => StorageLocationParameter::class,
case Group::class: Supplier::class => SupplierParameter::class,
return GroupParameter::class; default => throw new \RuntimeException('Unknown target class for parameter: '.$this->getTargetClass()),
case Manufacturer::class: };
return ManufacturerParameter::class;
case MeasurementUnit::class:
return MeasurementUnitParameter::class;
case Part::class:
return PartParameter::class;
case StorageLocation::class:
return StorageLocationParameter::class;
case Supplier::class:
return SupplierParameter::class;
default:
throw new \RuntimeException('Unknown target class for parameter: '.$this->getTargetClass());
}
} }
if (is_a($abstract_class, Attachment::class, true)) { if (is_a($abstract_class, Attachment::class, true)) {
switch ($this->getTargetClass()) { return match ($this->getTargetClass()) {
case AttachmentType::class: AttachmentType::class => AttachmentTypeAttachment::class,
return AttachmentTypeAttachment::class; Category::class => CategoryAttachment::class,
case Category::class: Currency::class => CurrencyAttachment::class,
return CategoryAttachment::class; Project::class => ProjectAttachment::class,
case Currency::class: Footprint::class => FootprintAttachment::class,
return CurrencyAttachment::class; Group::class => GroupAttachment::class,
case Project::class: Manufacturer::class => ManufacturerAttachment::class,
return ProjectAttachment::class; MeasurementUnit::class => MeasurementUnitAttachment::class,
case Footprint::class: Part::class => PartAttachment::class,
return FootprintAttachment::class; StorageLocation::class => StorageLocationAttachment::class,
case Group::class: Supplier::class => SupplierAttachment::class,
return GroupAttachment::class; User::class => UserAttachment::class,
case Manufacturer::class: default => throw new \RuntimeException('Unknown target class for parameter: '.$this->getTargetClass()),
return ManufacturerAttachment::class; };
case MeasurementUnit::class:
return MeasurementUnitAttachment::class;
case Part::class:
return PartAttachment::class;
case StorageLocation::class:
return StorageLocationAttachment::class;
case Supplier::class:
return SupplierAttachment::class;
case User::class:
return UserAttachment::class;
default:
throw new \RuntimeException('Unknown target class for parameter: '.$this->getTargetClass());
}
} }
throw new \RuntimeException('The class '.$abstract_class.' is abstract and no explicit resolving to an concrete type is defined!'); throw new \RuntimeException('The class '.$abstract_class.' is abstract and no explicit resolving to an concrete type is defined!');

View file

@ -20,7 +20,7 @@
namespace App\Entity\LogSystem; namespace App\Entity\LogSystem;
use \Psr\Log\LogLevel as PSRLogLevel; use Psr\Log\LogLevel as PSRLogLevel;
enum LogLevel: int enum LogLevel: int
{ {

View file

@ -32,7 +32,7 @@ use League\OAuth2\Client\Token\AccessTokenInterface;
/** /**
* This entity represents a OAuth token pair (access and refresh token), for an application * This entity represents a OAuth token pair (access and refresh token), for an application
*/ */
#[ORM\Entity()] #[ORM\Entity]
#[ORM\Table(name: 'oauth_tokens')] #[ORM\Table(name: 'oauth_tokens')]
#[ORM\UniqueConstraint(name: 'oauth_tokens_unique_name', columns: ['name'])] #[ORM\UniqueConstraint(name: 'oauth_tokens_unique_name', columns: ['name'])]
#[ORM\Index(columns: ['name'], name: 'oauth_tokens_name_idx')] #[ORM\Index(columns: ['name'], name: 'oauth_tokens_name_idx')]

View file

@ -41,6 +41,7 @@ declare(strict_types=1);
namespace App\Entity\Parameters; namespace App\Entity\Parameters;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\RangeFilter; use ApiPlatform\Doctrine\Orm\Filter\RangeFilter;
@ -76,9 +77,9 @@ use function sprintf;
6 => MeasurementUnitParameter::class, 7 => PartParameter::class, 8 => StorageLocationParameter::class, 6 => MeasurementUnitParameter::class, 7 => PartParameter::class, 8 => StorageLocationParameter::class,
9 => SupplierParameter::class, 10 => AttachmentTypeParameter::class])] 9 => SupplierParameter::class, 10 => AttachmentTypeParameter::class])]
#[ORM\Table('parameters')] #[ORM\Table('parameters')]
#[ORM\Index(name: 'parameter_name_idx', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'parameter_name_idx')]
#[ORM\Index(name: 'parameter_group_idx', columns: ['param_group'])] #[ORM\Index(columns: ['param_group'], name: 'parameter_group_idx')]
#[ORM\Index(name: 'parameter_type_element_idx', columns: ['type', 'element_id'])] #[ORM\Index(columns: ['type', 'element_id'], name: 'parameter_type_element_idx')]
#[ApiResource( #[ApiResource(
shortName: 'Parameter', shortName: 'Parameter',
operations: [ operations: [
@ -91,7 +92,7 @@ use function sprintf;
denormalizationContext: ['groups' => ['parameter:write', 'parameter:write:standalone', 'api:basic:write'], 'openapi_definition_name' => 'Write'], denormalizationContext: ['groups' => ['parameter:write', 'parameter:write:standalone', 'api:basic:write'], 'openapi_definition_name' => 'Write'],
)] )]
#[ApiFilter(LikeFilter::class, properties: ["name", "symbol", "unit", "group", "value_text"])] #[ApiFilter(LikeFilter::class, properties: ["name", "symbol", "unit", "group", "value_text"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(RangeFilter::class, properties: ["value_min", "value_typical", "value_max"])] #[ApiFilter(RangeFilter::class, properties: ["value_min", "value_typical", "value_max"])]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
//This discriminator map is required for API platform to know which class to use for deserialization, when creating a new parameter. //This discriminator map is required for API platform to know which class to use for deserialization, when creating a new parameter.
@ -166,7 +167,7 @@ abstract class AbstractParameter extends AbstractNamedDBElement
* @var string the group this parameter belongs to * @var string the group this parameter belongs to
*/ */
#[Groups(['full', 'parameter:read', 'parameter:write'])] #[Groups(['full', 'parameter:read', 'parameter:write'])]
#[ORM\Column(type: Types::STRING, name: 'param_group')] #[ORM\Column(name: 'param_group', type: Types::STRING)]
protected string $group = ''; protected string $group = '';
/** /**

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\Parts; namespace App\Entity\Parts;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
@ -60,8 +61,8 @@ use Symfony\Component\Validator\Constraints as Assert;
*/ */
#[ORM\Entity(repositoryClass: CategoryRepository::class)] #[ORM\Entity(repositoryClass: CategoryRepository::class)]
#[ORM\Table(name: '`categories`')] #[ORM\Table(name: '`categories`')]
#[ORM\Index(name: 'category_idx_name', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'category_idx_name')]
#[ORM\Index(name: 'category_idx_parent_name', columns: ['parent_id', 'name'])] #[ORM\Index(columns: ['parent_id', 'name'], name: 'category_idx_parent_name')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)'), new Get(security: 'is_granted("read", object)'),
@ -88,11 +89,11 @@ use Symfony\Component\Validator\Constraints as Assert;
)] )]
#[ApiFilter(PropertyFilter::class)] #[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] #[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
class Category extends AbstractPartsContainingDBElement class Category extends AbstractPartsContainingDBElement
{ {
#[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent')] #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $children; protected Collection $children;
@ -166,7 +167,7 @@ class Category extends AbstractPartsContainingDBElement
*/ */
#[Assert\Valid] #[Assert\Valid]
#[Groups(['full', 'category:read', 'category:write'])] #[Groups(['full', 'category:read', 'category:write'])]
#[ORM\OneToMany(targetEntity: CategoryAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: CategoryAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $attachments; protected Collection $attachments;
@ -179,7 +180,7 @@ class Category extends AbstractPartsContainingDBElement
*/ */
#[Assert\Valid] #[Assert\Valid]
#[Groups(['full', 'category:read', 'category:write'])] #[Groups(['full', 'category:read', 'category:write'])]
#[ORM\OneToMany(targetEntity: CategoryParameter::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: CategoryParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])] #[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])]
protected Collection $parameters; protected Collection $parameters;

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\Parts; namespace App\Entity\Parts;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
@ -61,8 +62,8 @@ use Symfony\Component\Validator\Constraints as Assert;
*/ */
#[ORM\Entity(repositoryClass: FootprintRepository::class)] #[ORM\Entity(repositoryClass: FootprintRepository::class)]
#[ORM\Table('`footprints`')] #[ORM\Table('`footprints`')]
#[ORM\Index(name: 'footprint_idx_name', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'footprint_idx_name')]
#[ORM\Index(name: 'footprint_idx_parent_name', columns: ['parent_id', 'name'])] #[ORM\Index(columns: ['parent_id', 'name'], name: 'footprint_idx_parent_name')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)'), new Get(security: 'is_granted("read", object)'),
@ -89,7 +90,7 @@ use Symfony\Component\Validator\Constraints as Assert;
)] )]
#[ApiFilter(PropertyFilter::class)] #[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] #[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
class Footprint extends AbstractPartsContainingDBElement class Footprint extends AbstractPartsContainingDBElement
{ {
@ -99,7 +100,7 @@ class Footprint extends AbstractPartsContainingDBElement
#[ApiProperty(readableLink: false, writableLink: false)] #[ApiProperty(readableLink: false, writableLink: false)]
protected ?AbstractStructuralDBElement $parent = null; protected ?AbstractStructuralDBElement $parent = null;
#[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent')] #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $children; protected Collection $children;
@ -110,7 +111,7 @@ class Footprint extends AbstractPartsContainingDBElement
* @var Collection<int, FootprintAttachment> * @var Collection<int, FootprintAttachment>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: FootprintAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: FootprintAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
#[Groups(['footprint:read', 'footprint:write'])] #[Groups(['footprint:read', 'footprint:write'])]
protected Collection $attachments; protected Collection $attachments;
@ -131,7 +132,7 @@ class Footprint extends AbstractPartsContainingDBElement
/** @var Collection<int, FootprintParameter> /** @var Collection<int, FootprintParameter>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: FootprintParameter::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: FootprintParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])] #[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])]
#[Groups(['footprint:read', 'footprint:write'])] #[Groups(['footprint:read', 'footprint:write'])]
protected Collection $parameters; protected Collection $parameters;

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\Parts; namespace App\Entity\Parts;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiFilter;
@ -56,8 +57,8 @@ use Symfony\Component\Validator\Constraints as Assert;
*/ */
#[ORM\Entity(repositoryClass: ManufacturerRepository::class)] #[ORM\Entity(repositoryClass: ManufacturerRepository::class)]
#[ORM\Table('`manufacturers`')] #[ORM\Table('`manufacturers`')]
#[ORM\Index(name: 'manufacturer_name', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'manufacturer_name')]
#[ORM\Index(name: 'manufacturer_idx_parent_name', columns: ['parent_id', 'name'])] #[ORM\Index(columns: ['parent_id', 'name'], name: 'manufacturer_idx_parent_name')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)'), new Get(security: 'is_granted("read", object)'),
@ -84,7 +85,7 @@ use Symfony\Component\Validator\Constraints as Assert;
)] )]
#[ApiFilter(PropertyFilter::class)] #[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] #[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
class Manufacturer extends AbstractCompany class Manufacturer extends AbstractCompany
{ {
@ -94,7 +95,7 @@ class Manufacturer extends AbstractCompany
#[ApiProperty(readableLink: false, writableLink: false)] #[ApiProperty(readableLink: false, writableLink: false)]
protected ?AbstractStructuralDBElement $parent = null; protected ?AbstractStructuralDBElement $parent = null;
#[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent')] #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $children; protected Collection $children;
@ -102,7 +103,7 @@ class Manufacturer extends AbstractCompany
* @var Collection<int, ManufacturerAttachment> * @var Collection<int, ManufacturerAttachment>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: ManufacturerAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: ManufacturerAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
#[Groups(['manufacturer:read', 'manufacturer:write'])] #[Groups(['manufacturer:read', 'manufacturer:write'])]
#[ApiProperty(readableLink: false, writableLink: true)] #[ApiProperty(readableLink: false, writableLink: true)]
@ -117,7 +118,7 @@ class Manufacturer extends AbstractCompany
/** @var Collection<int, ManufacturerParameter> /** @var Collection<int, ManufacturerParameter>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: ManufacturerParameter::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: ManufacturerParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])] #[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])]
#[Groups(['manufacturer:read', 'manufacturer:write'])] #[Groups(['manufacturer:read', 'manufacturer:write'])]
#[ApiProperty(readableLink: false, writableLink: true)] #[ApiProperty(readableLink: false, writableLink: true)]

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\Parts; namespace App\Entity\Parts;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiFilter;
@ -60,8 +61,8 @@ use Symfony\Component\Validator\Constraints as Assert;
#[UniqueEntity('unit')] #[UniqueEntity('unit')]
#[ORM\Entity(repositoryClass: MeasurementUnitRepository::class)] #[ORM\Entity(repositoryClass: MeasurementUnitRepository::class)]
#[ORM\Table(name: '`measurement_units`')] #[ORM\Table(name: '`measurement_units`')]
#[ORM\Index(name: 'unit_idx_name', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'unit_idx_name')]
#[ORM\Index(name: 'unit_idx_parent_name', columns: ['parent_id', 'name'])] #[ORM\Index(columns: ['parent_id', 'name'], name: 'unit_idx_parent_name')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)'), new Get(security: 'is_granted("read", object)'),
@ -88,7 +89,7 @@ use Symfony\Component\Validator\Constraints as Assert;
)] )]
#[ApiFilter(PropertyFilter::class)] #[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment", "unit"])] #[ApiFilter(LikeFilter::class, properties: ["name", "comment", "unit"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
class MeasurementUnit extends AbstractPartsContainingDBElement class MeasurementUnit extends AbstractPartsContainingDBElement
{ {
@ -98,7 +99,7 @@ class MeasurementUnit extends AbstractPartsContainingDBElement
*/ */
#[Assert\Length(max: 10)] #[Assert\Length(max: 10)]
#[Groups(['extended', 'full', 'import', 'measurement_unit:read', 'measurement_unit:write'])] #[Groups(['extended', 'full', 'import', 'measurement_unit:read', 'measurement_unit:write'])]
#[ORM\Column(type: Types::STRING, name: 'unit', nullable: true)] #[ORM\Column(name: 'unit', type: Types::STRING, nullable: true)]
protected ?string $unit = null; protected ?string $unit = null;
#[Groups(['measurement_unit:read', 'measurement_unit:write'])] #[Groups(['measurement_unit:read', 'measurement_unit:write'])]
@ -109,7 +110,7 @@ class MeasurementUnit extends AbstractPartsContainingDBElement
* Set to false, to measure continuous sizes likes masses or lengths. * Set to false, to measure continuous sizes likes masses or lengths.
*/ */
#[Groups(['extended', 'full', 'import', 'measurement_unit:read', 'measurement_unit:write'])] #[Groups(['extended', 'full', 'import', 'measurement_unit:read', 'measurement_unit:write'])]
#[ORM\Column(type: Types::BOOLEAN, name: 'is_integer')] #[ORM\Column(name: 'is_integer', type: Types::BOOLEAN)]
protected bool $is_integer = false; protected bool $is_integer = false;
/** /**
@ -118,10 +119,10 @@ class MeasurementUnit extends AbstractPartsContainingDBElement
*/ */
#[Assert\Expression('this.isUseSIPrefix() == false or this.getUnit() != null', message: 'validator.measurement_unit.use_si_prefix_needs_unit')] #[Assert\Expression('this.isUseSIPrefix() == false or this.getUnit() != null', message: 'validator.measurement_unit.use_si_prefix_needs_unit')]
#[Groups(['full', 'import', 'measurement_unit:read', 'measurement_unit:write'])] #[Groups(['full', 'import', 'measurement_unit:read', 'measurement_unit:write'])]
#[ORM\Column(type: Types::BOOLEAN, name: 'use_si_prefix')] #[ORM\Column(name: 'use_si_prefix', type: Types::BOOLEAN)]
protected bool $use_si_prefix = false; protected bool $use_si_prefix = false;
#[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent', cascade: ['persist'])] #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class, cascade: ['persist'])]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $children; protected Collection $children;
@ -135,7 +136,7 @@ class MeasurementUnit extends AbstractPartsContainingDBElement
* @var Collection<int, MeasurementUnitAttachment> * @var Collection<int, MeasurementUnitAttachment>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: MeasurementUnitAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: MeasurementUnitAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
#[Groups(['measurement_unit:read', 'measurement_unit:write'])] #[Groups(['measurement_unit:read', 'measurement_unit:write'])]
protected Collection $attachments; protected Collection $attachments;
@ -148,7 +149,7 @@ class MeasurementUnit extends AbstractPartsContainingDBElement
/** @var Collection<int, MeasurementUnitParameter> /** @var Collection<int, MeasurementUnitParameter>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: MeasurementUnitParameter::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: MeasurementUnitParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])] #[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])]
#[Groups(['measurement_unit:read', 'measurement_unit:write'])] #[Groups(['measurement_unit:read', 'measurement_unit:write'])]
protected Collection $parameters; protected Collection $parameters;

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\Parts; namespace App\Entity\Parts;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter; use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
@ -74,9 +75,9 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
#[ORM\Entity(repositoryClass: PartRepository::class)] #[ORM\Entity(repositoryClass: PartRepository::class)]
#[ORM\EntityListeners([TreeCacheInvalidationListener::class])] #[ORM\EntityListeners([TreeCacheInvalidationListener::class])]
#[ORM\Table('`parts`')] #[ORM\Table('`parts`')]
#[ORM\Index(name: 'parts_idx_datet_name_last_id_needs', columns: ['datetime_added', 'name', 'last_modified', 'id', 'needs_review'])] #[ORM\Index(columns: ['datetime_added', 'name', 'last_modified', 'id', 'needs_review'], name: 'parts_idx_datet_name_last_id_needs')]
#[ORM\Index(name: 'parts_idx_name', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'parts_idx_name')]
#[ORM\Index(name: 'parts_idx_ipn', columns: ['ipn'])] #[ORM\Index(columns: ['ipn'], name: 'parts_idx_ipn')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(normalizationContext: ['groups' => ['part:read', 'provider_reference:read', 'api:basic:read', 'part_lot:read', new Get(normalizationContext: ['groups' => ['part:read', 'provider_reference:read', 'api:basic:read', 'part_lot:read',
@ -97,7 +98,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
#[ApiFilter(LikeFilter::class, properties: ["name", "comment", "description", "ipn", "tags", "manufacturer_product_number"])] #[ApiFilter(LikeFilter::class, properties: ["name", "comment", "description", "ipn", "tags", "manufacturer_product_number"])]
#[ApiFilter(BooleanFilter::class, properties: ["favorite" , "needs_review"])] #[ApiFilter(BooleanFilter::class, properties: ["favorite" , "needs_review"])]
#[ApiFilter(RangeFilter::class, properties: ["mass", "minamount"])] #[ApiFilter(RangeFilter::class, properties: ["mass", "minamount"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
class Part extends AttachmentContainingDBElement class Part extends AttachmentContainingDBElement
{ {
@ -116,7 +117,7 @@ class Part extends AttachmentContainingDBElement
*/ */
#[Assert\Valid] #[Assert\Valid]
#[Groups(['full', 'part:read', 'part:write'])] #[Groups(['full', 'part:read', 'part:write'])]
#[ORM\OneToMany(targetEntity: PartParameter::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: PartParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])] #[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])]
protected Collection $parameters; protected Collection $parameters;
@ -136,7 +137,7 @@ class Part extends AttachmentContainingDBElement
*/ */
#[Assert\Valid] #[Assert\Valid]
#[Groups(['full', 'part:read', 'part:write'])] #[Groups(['full', 'part:read', 'part:write'])]
#[ORM\OneToMany(targetEntity: PartAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: PartAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $attachments; protected Collection $attachments;

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace App\Entity\Parts; namespace App\Entity\Parts;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter; use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
@ -67,7 +68,7 @@ use Symfony\Component\Validator\Constraints as Assert;
)] )]
#[ApiFilter(PropertyFilter::class)] #[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["other_type", "comment"])] #[ApiFilter(LikeFilter::class, properties: ["other_type", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['comment', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['comment', 'addedDate', 'lastModified'])]
class PartAssociation extends AbstractDBElement implements TimeStampableInterface class PartAssociation extends AbstractDBElement implements TimeStampableInterface
{ {

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\Parts; namespace App\Entity\Parts;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter; use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
@ -80,7 +81,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
)] )]
#[ApiFilter(PropertyFilter::class)] #[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["description", "comment"])] #[ApiFilter(LikeFilter::class, properties: ["description", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(BooleanFilter::class, properties: ['instock_unknown', 'needs_refill'])] #[ApiFilter(BooleanFilter::class, properties: ['instock_unknown', 'needs_refill'])]
#[ApiFilter(RangeFilter::class, properties: ['amount'])] #[ApiFilter(RangeFilter::class, properties: ['amount'])]
#[ApiFilter(OrderFilter::class, properties: ['description', 'comment', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['description', 'comment', 'addedDate', 'lastModified'])]
@ -107,7 +108,7 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
* Set to null, if the lot can be used indefinitely. * Set to null, if the lot can be used indefinitely.
*/ */
#[Groups(['extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])] #[Groups(['extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])]
#[ORM\Column(type: Types::DATETIME_MUTABLE, name: 'expiration_date', nullable: true)] #[ORM\Column(name: 'expiration_date', type: Types::DATETIME_MUTABLE, nullable: true)]
protected ?\DateTimeInterface $expiration_date = null; protected ?\DateTimeInterface $expiration_date = null;
/** /**
@ -116,7 +117,7 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
#[Groups(['simple', 'extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])] #[Groups(['simple', 'extended', 'full', 'import', 'part_lot:read', 'part_lot:write'])]
#[ORM\ManyToOne(targetEntity: StorageLocation::class, fetch: 'EAGER')] #[ORM\ManyToOne(targetEntity: StorageLocation::class, fetch: 'EAGER')]
#[ORM\JoinColumn(name: 'id_store_location')] #[ORM\JoinColumn(name: 'id_store_location')]
#[Selectable()] #[Selectable]
protected ?StorageLocation $storage_location = null; protected ?StorageLocation $storage_location = null;
/** /**

View file

@ -64,7 +64,7 @@ trait BasicPropertyTrait
* Every part must have a category. * Every part must have a category.
*/ */
#[Assert\NotNull(message: 'validator.select_valid_category')] #[Assert\NotNull(message: 'validator.select_valid_category')]
#[Selectable()] #[Selectable]
#[Groups(['simple', 'extended', 'full', 'import', "part:read", "part:write"])] #[Groups(['simple', 'extended', 'full', 'import', "part:read", "part:write"])]
#[ORM\ManyToOne(targetEntity: Category::class)] #[ORM\ManyToOne(targetEntity: Category::class)]
#[ORM\JoinColumn(name: 'id_category', nullable: false)] #[ORM\JoinColumn(name: 'id_category', nullable: false)]
@ -76,7 +76,7 @@ trait BasicPropertyTrait
#[Groups(['simple', 'extended', 'full', 'import', 'part:read', 'part:write'])] #[Groups(['simple', 'extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\ManyToOne(targetEntity: Footprint::class)] #[ORM\ManyToOne(targetEntity: Footprint::class)]
#[ORM\JoinColumn(name: 'id_footprint')] #[ORM\JoinColumn(name: 'id_footprint')]
#[Selectable()] #[Selectable]
protected ?Footprint $footprint = null; protected ?Footprint $footprint = null;
/** /**

View file

@ -41,7 +41,7 @@ trait InstockTrait
*/ */
#[Assert\Valid] #[Assert\Valid]
#[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\OneToMany(targetEntity: PartLot::class, mappedBy: 'part', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'part', targetEntity: PartLot::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['amount' => 'DESC'])] #[ORM\OrderBy(['amount' => 'DESC'])]
protected Collection $partLots; protected Collection $partLots;

View file

@ -42,7 +42,7 @@ trait ManufacturerTrait
#[Groups(['simple', 'extended', 'full', 'import', 'part:read', 'part:write'])] #[Groups(['simple', 'extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\ManyToOne(targetEntity: Manufacturer::class)] #[ORM\ManyToOne(targetEntity: Manufacturer::class)]
#[ORM\JoinColumn(name: 'id_manufacturer')] #[ORM\JoinColumn(name: 'id_manufacturer')]
#[Selectable()] #[Selectable]
protected ?Manufacturer $manufacturer = null; protected ?Manufacturer $manufacturer = null;
/** /**

View file

@ -40,7 +40,7 @@ trait OrderTrait
*/ */
#[Assert\Valid] #[Assert\Valid]
#[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])] #[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\OneToMany(targetEntity: Orderdetail::class, mappedBy: 'part', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'part', targetEntity: Orderdetail::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['supplierpartnr' => 'ASC'])] #[ORM\OrderBy(['supplierpartnr' => 'ASC'])]
protected Collection $orderdetails; protected Collection $orderdetails;

View file

@ -16,13 +16,13 @@ trait ProjectTrait
/** /**
* @var Collection<ProjectBOMEntry> $project_bom_entries * @var Collection<ProjectBOMEntry> $project_bom_entries
*/ */
#[ORM\OneToMany(targetEntity: ProjectBOMEntry::class, mappedBy: 'part', cascade: ['remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'part', targetEntity: ProjectBOMEntry::class, cascade: ['remove'], orphanRemoval: true)]
protected Collection $project_bom_entries; protected Collection $project_bom_entries;
/** /**
* @var Project|null If a project is set here, then this part is special and represents the builds of a project. * @var Project|null If a project is set here, then this part is special and represents the builds of a project.
*/ */
#[ORM\OneToOne(targetEntity: Project::class, inversedBy: 'build_part')] #[ORM\OneToOne(inversedBy: 'build_part', targetEntity: Project::class)]
#[ORM\JoinColumn] #[ORM\JoinColumn]
protected ?Project $built_project = null; protected ?Project $built_project = null;

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\Parts; namespace App\Entity\Parts;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiFilter;
@ -56,8 +57,8 @@ use Symfony\Component\Validator\Constraints as Assert;
*/ */
#[ORM\Entity(repositoryClass: StorelocationRepository::class)] #[ORM\Entity(repositoryClass: StorelocationRepository::class)]
#[ORM\Table('`storelocations`')] #[ORM\Table('`storelocations`')]
#[ORM\Index(name: 'location_idx_name', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'location_idx_name')]
#[ORM\Index(name: 'location_idx_parent_name', columns: ['parent_id', 'name'])] #[ORM\Index(columns: ['parent_id', 'name'], name: 'location_idx_parent_name')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)'), new Get(security: 'is_granted("read", object)'),
@ -84,11 +85,11 @@ use Symfony\Component\Validator\Constraints as Assert;
)] )]
#[ApiFilter(PropertyFilter::class)] #[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] #[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
class StorageLocation extends AbstractPartsContainingDBElement class StorageLocation extends AbstractPartsContainingDBElement
{ {
#[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent')] #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $children; protected Collection $children;
@ -112,7 +113,7 @@ class StorageLocation extends AbstractPartsContainingDBElement
/** @var Collection<int, StorageLocationParameter> /** @var Collection<int, StorageLocationParameter>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: StorageLocationParameter::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: StorageLocationParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])] #[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])]
#[Groups(['location:read', 'location:write'])] #[Groups(['location:read', 'location:write'])]
protected Collection $parameters; protected Collection $parameters;
@ -158,7 +159,7 @@ class StorageLocation extends AbstractPartsContainingDBElement
* @var Collection<int, StorageLocationAttachment> * @var Collection<int, StorageLocationAttachment>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: StorageLocationAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: StorageLocationAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[Groups(['location:read', 'location:write'])] #[Groups(['location:read', 'location:write'])]
protected Collection $attachments; protected Collection $attachments;

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\Parts; namespace App\Entity\Parts;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiFilter;
@ -61,8 +62,8 @@ use Symfony\Component\Validator\Constraints as Assert;
*/ */
#[ORM\Entity(repositoryClass: SupplierRepository::class)] #[ORM\Entity(repositoryClass: SupplierRepository::class)]
#[ORM\Table('`suppliers`')] #[ORM\Table('`suppliers`')]
#[ORM\Index(name: 'supplier_idx_name', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'supplier_idx_name')]
#[ORM\Index(name: 'supplier_idx_parent_name', columns: ['parent_id', 'name'])] #[ORM\Index(columns: ['parent_id', 'name'], name: 'supplier_idx_parent_name')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)'), new Get(security: 'is_granted("read", object)'),
@ -81,17 +82,17 @@ use Symfony\Component\Validator\Constraints as Assert;
security: 'is_granted("@manufacturers.read")' security: 'is_granted("@manufacturers.read")'
)], )],
uriVariables: [ uriVariables: [
'id' => new Link(fromClass: Supplier::class, fromProperty: 'children') 'id' => new Link(fromProperty: 'children', fromClass: Supplier::class)
], ],
normalizationContext: ['groups' => ['supplier:read', 'company:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] normalizationContext: ['groups' => ['supplier:read', 'company:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
)] )]
#[ApiFilter(PropertyFilter::class)] #[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])] #[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
class Supplier extends AbstractCompany class Supplier extends AbstractCompany
{ {
#[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent')] #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $children; protected Collection $children;
@ -104,7 +105,7 @@ class Supplier extends AbstractCompany
/** /**
* @var Collection<int, Orderdetail>|Orderdetail[] * @var Collection<int, Orderdetail>|Orderdetail[]
*/ */
#[ORM\OneToMany(targetEntity: Orderdetail::class, mappedBy: 'supplier')] #[ORM\OneToMany(mappedBy: 'supplier', targetEntity: Orderdetail::class)]
protected Collection $orderdetails; protected Collection $orderdetails;
/** /**
@ -113,22 +114,22 @@ class Supplier extends AbstractCompany
*/ */
#[ORM\ManyToOne(targetEntity: Currency::class)] #[ORM\ManyToOne(targetEntity: Currency::class)]
#[ORM\JoinColumn(name: 'default_currency_id')] #[ORM\JoinColumn(name: 'default_currency_id')]
#[Selectable()] #[Selectable]
protected ?Currency $default_currency = null; protected ?Currency $default_currency = null;
/** /**
* @var BigDecimal|null The shipping costs that have to be paid, when ordering via this supplier * @var BigDecimal|null The shipping costs that have to be paid, when ordering via this supplier
*/ */
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import'])]
#[ORM\Column(name: 'shipping_costs', nullable: true, type: 'big_decimal', precision: 11, scale: 5)] #[ORM\Column(name: 'shipping_costs', type: 'big_decimal', precision: 11, scale: 5, nullable: true)]
#[BigDecimalPositiveOrZero()] #[BigDecimalPositiveOrZero]
protected ?BigDecimal $shipping_costs = null; protected ?BigDecimal $shipping_costs = null;
/** /**
* @var Collection<int, SupplierAttachment> * @var Collection<int, SupplierAttachment>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: SupplierAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: SupplierAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
#[Groups(['supplier:read', 'supplier:write'])] #[Groups(['supplier:read', 'supplier:write'])]
#[ApiProperty(readableLink: false, writableLink: true)] #[ApiProperty(readableLink: false, writableLink: true)]
@ -143,7 +144,7 @@ class Supplier extends AbstractCompany
/** @var Collection<int, SupplierParameter> /** @var Collection<int, SupplierParameter>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: SupplierParameter::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: SupplierParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])] #[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])]
#[Groups(['supplier:read', 'supplier:write'])] #[Groups(['supplier:read', 'supplier:write'])]
#[ApiProperty(readableLink: false, writableLink: true)] #[ApiProperty(readableLink: false, writableLink: true)]

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\PriceInformations; namespace App\Entity\PriceInformations;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiFilter;
@ -60,8 +61,8 @@ use Symfony\Component\Validator\Constraints as Assert;
#[UniqueEntity('iso_code')] #[UniqueEntity('iso_code')]
#[ORM\Entity(repositoryClass: CurrencyRepository::class)] #[ORM\Entity(repositoryClass: CurrencyRepository::class)]
#[ORM\Table(name: 'currencies')] #[ORM\Table(name: 'currencies')]
#[ORM\Index(name: 'currency_idx_name', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'currency_idx_name')]
#[ORM\Index(name: 'currency_idx_parent_name', columns: ['parent_id', 'name'])] #[ORM\Index(columns: ['parent_id', 'name'], name: 'currency_idx_parent_name')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)'), new Get(security: 'is_granted("read", object)'),
@ -88,7 +89,7 @@ use Symfony\Component\Validator\Constraints as Assert;
)] )]
#[ApiFilter(PropertyFilter::class)] #[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment", "iso_code"])] #[ApiFilter(LikeFilter::class, properties: ["name", "comment", "iso_code"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
class Currency extends AbstractStructuralDBElement class Currency extends AbstractStructuralDBElement
{ {
@ -99,7 +100,7 @@ class Currency extends AbstractStructuralDBElement
* (how many base units the current currency is worth) * (how many base units the current currency is worth)
*/ */
#[ORM\Column(type: 'big_decimal', precision: 11, scale: 5, nullable: true)] #[ORM\Column(type: 'big_decimal', precision: 11, scale: 5, nullable: true)]
#[BigDecimalPositive()] #[BigDecimalPositive]
#[Groups(['currency:read', 'currency:write'])] #[Groups(['currency:read', 'currency:write'])]
#[ApiProperty(readableLink: false, writableLink: false)] #[ApiProperty(readableLink: false, writableLink: false)]
protected ?BigDecimal $exchange_rate = null; protected ?BigDecimal $exchange_rate = null;
@ -116,7 +117,7 @@ class Currency extends AbstractStructuralDBElement
#[ORM\Column(type: Types::STRING)] #[ORM\Column(type: Types::STRING)]
protected string $iso_code = ""; protected string $iso_code = "";
#[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent', cascade: ['persist'])] #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class, cascade: ['persist'])]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $children; protected Collection $children;
@ -130,7 +131,7 @@ class Currency extends AbstractStructuralDBElement
* @var Collection<int, CurrencyAttachment> * @var Collection<int, CurrencyAttachment>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: CurrencyAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: CurrencyAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
#[Groups(['currency:read', 'currency:write'])] #[Groups(['currency:read', 'currency:write'])]
protected Collection $attachments; protected Collection $attachments;
@ -143,14 +144,14 @@ class Currency extends AbstractStructuralDBElement
/** @var Collection<int, CurrencyParameter> /** @var Collection<int, CurrencyParameter>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: CurrencyParameter::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: CurrencyParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])] #[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])]
#[Groups(['currency:read', 'currency:write'])] #[Groups(['currency:read', 'currency:write'])]
protected Collection $parameters; protected Collection $parameters;
/** @var Collection<int, Pricedetail> /** @var Collection<int, Pricedetail>
*/ */
#[ORM\OneToMany(targetEntity: Pricedetail::class, mappedBy: 'currency')] #[ORM\OneToMany(mappedBy: 'currency', targetEntity: Pricedetail::class)]
protected Collection $pricedetails; protected Collection $pricedetails;
#[Groups(['currency:read'])] #[Groups(['currency:read'])]

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace App\Entity\PriceInformations; namespace App\Entity\PriceInformations;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter; use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
@ -59,7 +60,7 @@ use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity] #[ORM\Entity]
#[ORM\HasLifecycleCallbacks] #[ORM\HasLifecycleCallbacks]
#[ORM\Table('`orderdetails`')] #[ORM\Table('`orderdetails`')]
#[ORM\Index(name: 'orderdetails_supplier_part_nr', columns: ['supplierpartnr'])] #[ORM\Index(columns: ['supplierpartnr'], name: 'orderdetails_supplier_part_nr')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)'), new Get(security: 'is_granted("read", object)'),
@ -88,7 +89,7 @@ use Symfony\Component\Validator\Constraints as Assert;
#[ApiFilter(PropertyFilter::class)] #[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["supplierpartnr", "supplier_product_url"])] #[ApiFilter(LikeFilter::class, properties: ["supplierpartnr", "supplier_product_url"])]
#[ApiFilter(BooleanFilter::class, properties: ["obsolete"])] #[ApiFilter(BooleanFilter::class, properties: ["obsolete"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['supplierpartnr', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['supplierpartnr', 'id', 'addedDate', 'lastModified'])]
class Orderdetail extends AbstractDBElement implements TimeStampableInterface, NamedElementInterface class Orderdetail extends AbstractDBElement implements TimeStampableInterface, NamedElementInterface
{ {
@ -96,7 +97,7 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N
#[Assert\Valid] #[Assert\Valid]
#[Groups(['extended', 'full', 'import', 'orderdetail:read', 'orderdetail:write'])] #[Groups(['extended', 'full', 'import', 'orderdetail:read', 'orderdetail:write'])]
#[ORM\OneToMany(targetEntity: Pricedetail::class, mappedBy: 'orderdetail', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'orderdetail', targetEntity: Pricedetail::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['min_discount_quantity' => 'ASC'])] #[ORM\OrderBy(['min_discount_quantity' => 'ASC'])]
protected Collection $pricedetails; protected Collection $pricedetails;

View file

@ -55,8 +55,8 @@ use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity] #[ORM\Entity]
#[ORM\HasLifecycleCallbacks] #[ORM\HasLifecycleCallbacks]
#[ORM\Table('`pricedetails`')] #[ORM\Table('`pricedetails`')]
#[ORM\Index(name: 'pricedetails_idx_min_discount', columns: ['min_discount_quantity'])] #[ORM\Index(columns: ['min_discount_quantity'], name: 'pricedetails_idx_min_discount')]
#[ORM\Index(name: 'pricedetails_idx_min_discount_price_qty', columns: ['min_discount_quantity', 'price_related_quantity'])] #[ORM\Index(columns: ['min_discount_quantity', 'price_related_quantity'], name: 'pricedetails_idx_min_discount_price_qty')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)'), new Get(security: 'is_granted("read", object)'),
@ -80,7 +80,7 @@ class Pricedetail extends AbstractDBElement implements TimeStampableInterface
*/ */
#[Groups(['extended', 'full', 'pricedetail:read', 'pricedetail:write'])] #[Groups(['extended', 'full', 'pricedetail:read', 'pricedetail:write'])]
#[ORM\Column(type: 'big_decimal', precision: 11, scale: 5)] #[ORM\Column(type: 'big_decimal', precision: 11, scale: 5)]
#[BigDecimalPositive()] #[BigDecimalPositive]
protected BigDecimal $price; protected BigDecimal $price;
/** /**
@ -90,7 +90,7 @@ class Pricedetail extends AbstractDBElement implements TimeStampableInterface
#[Groups(['extended', 'full', 'import', 'pricedetail:read', 'pricedetail:write'])] #[Groups(['extended', 'full', 'import', 'pricedetail:read', 'pricedetail:write'])]
#[ORM\ManyToOne(targetEntity: Currency::class, inversedBy: 'pricedetails')] #[ORM\ManyToOne(targetEntity: Currency::class, inversedBy: 'pricedetails')]
#[ORM\JoinColumn(name: 'id_currency')] #[ORM\JoinColumn(name: 'id_currency')]
#[Selectable()] #[Selectable]
protected ?Currency $currency = null; protected ?Currency $currency = null;
/** /**

View file

@ -90,7 +90,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
class Project extends AbstractStructuralDBElement class Project extends AbstractStructuralDBElement
{ {
#[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent')] #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $children; protected Collection $children;
@ -105,9 +105,9 @@ class Project extends AbstractStructuralDBElement
#[Assert\Valid] #[Assert\Valid]
#[Groups(['extended', 'full'])] #[Groups(['extended', 'full'])]
#[ORM\OneToMany(targetEntity: ProjectBOMEntry::class, mappedBy: 'project', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'project', targetEntity: ProjectBOMEntry::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[UniqueObjectCollection(fields: ['part'], message: 'project.bom_entry.part_already_in_bom')] #[UniqueObjectCollection(message: 'project.bom_entry.part_already_in_bom', fields: ['part'])]
#[UniqueObjectCollection(fields: ['name'], message: 'project.bom_entry.name_already_in_bom')] #[UniqueObjectCollection(message: 'project.bom_entry.name_already_in_bom', fields: ['name'])]
protected Collection $bom_entries; protected Collection $bom_entries;
#[ORM\Column(type: Types::INTEGER)] #[ORM\Column(type: Types::INTEGER)]
@ -125,7 +125,7 @@ class Project extends AbstractStructuralDBElement
/** /**
* @var Part|null The (optional) part that represents the builds of this project in the stock * @var Part|null The (optional) part that represents the builds of this project in the stock
*/ */
#[ORM\OneToOne(targetEntity: Part::class, mappedBy: 'built_project', cascade: ['persist'], orphanRemoval: true)] #[ORM\OneToOne(mappedBy: 'built_project', targetEntity: Part::class, cascade: ['persist'], orphanRemoval: true)]
#[Groups(['project:read', 'project:write'])] #[Groups(['project:read', 'project:write'])]
protected ?Part $build_part = null; protected ?Part $build_part = null;
@ -139,7 +139,7 @@ class Project extends AbstractStructuralDBElement
/** /**
* @var Collection<int, ProjectAttachment> * @var Collection<int, ProjectAttachment>
*/ */
#[ORM\OneToMany(targetEntity: ProjectAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: ProjectAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
#[Groups(['project:read', 'project:write'])] #[Groups(['project:read', 'project:write'])]
protected Collection $attachments; protected Collection $attachments;
@ -151,7 +151,7 @@ class Project extends AbstractStructuralDBElement
/** @var Collection<int, ProjectParameter> /** @var Collection<int, ProjectParameter>
*/ */
#[ORM\OneToMany(targetEntity: ProjectParameter::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: ProjectParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])] #[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])]
#[Groups(['project:read', 'project:write'])] #[Groups(['project:read', 'project:write'])]
protected Collection $parameters; protected Collection $parameters;

View file

@ -59,11 +59,11 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
#[ORM\Table('project_bom_entries')] #[ORM\Table('project_bom_entries')]
#[ApiResource( #[ApiResource(
operations: [ operations: [
new Get(security: 'is_granted("read", object)', uriTemplate: '/project_bom_entries/{id}.{_format}',), new Get(uriTemplate: '/project_bom_entries/{id}.{_format}', security: 'is_granted("read", object)',),
new GetCollection(security: 'is_granted("@projects.read")', uriTemplate: '/project_bom_entries.{_format}',), new GetCollection(uriTemplate: '/project_bom_entries.{_format}', security: 'is_granted("@projects.read")',),
new Post(securityPostDenormalize: 'is_granted("create", object)', uriTemplate: '/project_bom_entries.{_format}',), new Post(uriTemplate: '/project_bom_entries.{_format}', securityPostDenormalize: 'is_granted("create", object)',),
new Patch(security: 'is_granted("edit", object)', uriTemplate: '/project_bom_entries/{id}.{_format}',), new Patch(uriTemplate: '/project_bom_entries/{id}.{_format}', security: 'is_granted("edit", object)',),
new Delete(security: 'is_granted("delete", object)', uriTemplate: '/project_bom_entries/{id}.{_format}',), new Delete(uriTemplate: '/project_bom_entries/{id}.{_format}', security: 'is_granted("delete", object)',),
], ],
normalizationContext: ['groups' => ['bom_entry:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'], normalizationContext: ['groups' => ['bom_entry:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['bom_entry:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'], denormalizationContext: ['groups' => ['bom_entry:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'],
@ -90,14 +90,14 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte
use TimestampTrait; use TimestampTrait;
#[Assert\Positive] #[Assert\Positive]
#[ORM\Column(type: Types::FLOAT, name: 'quantity')] #[ORM\Column(name: 'quantity', type: Types::FLOAT)]
#[Groups(['bom_entry:read', 'bom_entry:write'])] #[Groups(['bom_entry:read', 'bom_entry:write'])]
protected float $quantity = 1.0; protected float $quantity = 1.0;
/** /**
* @var string A comma separated list of the names, where this parts should be placed * @var string A comma separated list of the names, where this parts should be placed
*/ */
#[ORM\Column(type: Types::TEXT, name: 'mountnames')] #[ORM\Column(name: 'mountnames', type: Types::TEXT)]
#[Groups(['bom_entry:read', 'bom_entry:write'])] #[Groups(['bom_entry:read', 'bom_entry:write'])]
protected string $mountnames = ''; protected string $mountnames = '';

View file

@ -45,12 +45,12 @@ use Symfony\Component\Validator\Constraints as Assert;
*/ */
#[ORM\Entity] #[ORM\Entity]
#[ORM\Table('`groups`')] #[ORM\Table('`groups`')]
#[ORM\Index(name: 'group_idx_name', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'group_idx_name')]
#[ORM\Index(name: 'group_idx_parent_name', columns: ['parent_id', 'name'])] #[ORM\Index(columns: ['parent_id', 'name'], name: 'group_idx_parent_name')]
#[NoLockout()] #[NoLockout]
class Group extends AbstractStructuralDBElement implements HasPermissionsInterface class Group extends AbstractStructuralDBElement implements HasPermissionsInterface
{ {
#[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent')] #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $children; protected Collection $children;
@ -61,21 +61,21 @@ class Group extends AbstractStructuralDBElement implements HasPermissionsInterfa
/** /**
* @var Collection<int, User> * @var Collection<int, User>
*/ */
#[ORM\OneToMany(targetEntity: User::class, mappedBy: 'group')] #[ORM\OneToMany(mappedBy: 'group', targetEntity: User::class)]
protected Collection $users; protected Collection $users;
/** /**
* @var bool If true all users associated with this group must have enabled some kind of two-factor authentication * @var bool If true all users associated with this group must have enabled some kind of two-factor authentication
*/ */
#[Groups(['extended', 'full', 'import'])] #[Groups(['extended', 'full', 'import'])]
#[ORM\Column(type: Types::BOOLEAN, name: 'enforce_2fa')] #[ORM\Column(name: 'enforce_2fa', type: Types::BOOLEAN)]
protected bool $enforce2FA = false; protected bool $enforce2FA = false;
/** /**
* @var Collection<int, GroupAttachment> * @var Collection<int, GroupAttachment>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: GroupAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: GroupAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])] #[ORM\OrderBy(['name' => 'ASC'])]
protected Collection $attachments; protected Collection $attachments;
@ -85,14 +85,14 @@ class Group extends AbstractStructuralDBElement implements HasPermissionsInterfa
#[Groups(['full'])] #[Groups(['full'])]
#[ORM\Embedded(class: PermissionData::class, columnPrefix: 'permissions_')] #[ORM\Embedded(class: PermissionData::class, columnPrefix: 'permissions_')]
#[ValidPermission()] #[ValidPermission]
protected ?PermissionData $permissions = null; protected ?PermissionData $permissions = null;
/** /**
* @var Collection<int, GroupParameter> * @var Collection<int, GroupParameter>
*/ */
#[Assert\Valid] #[Assert\Valid]
#[ORM\OneToMany(targetEntity: GroupParameter::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'element', targetEntity: GroupParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])] #[ORM\OrderBy(['group' => 'ASC', 'name' => 'ASC'])]
protected Collection $parameters; protected Collection $parameters;

View file

@ -57,7 +57,7 @@ final class PermissionData implements \JsonSerializable
* operation => value, * operation => value,
* ] * ]
*/ */
#[ORM\Column(type: Types::JSON, name: 'data')] #[ORM\Column(name: 'data', type: Types::JSON)]
protected array $data = [] protected array $data = []
) )
{ {

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Entity\UserSystem; namespace App\Entity\UserSystem;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
@ -80,7 +81,7 @@ use Jbtronics\TFAWebauthn\Model\TwoFactorInterface as WebauthnTwoFactorInterface
#[ORM\Entity(repositoryClass: UserRepository::class)] #[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\EntityListeners([TreeCacheInvalidationListener::class])] #[ORM\EntityListeners([TreeCacheInvalidationListener::class])]
#[ORM\Table('`users`')] #[ORM\Table('`users`')]
#[ORM\Index(name: 'user_idx_username', columns: ['name'])] #[ORM\Index(columns: ['name'], name: 'user_idx_username')]
#[ORM\AttributeOverrides([ #[ORM\AttributeOverrides([
new ORM\AttributeOverride(name: 'name', column: new ORM\Column(type: Types::STRING, length: 180, unique: true)) new ORM\AttributeOverride(name: 'name', column: new ORM\Column(type: Types::STRING, length: 180, unique: true))
])] ])]
@ -100,9 +101,9 @@ use Jbtronics\TFAWebauthn\Model\TwoFactorInterface as WebauthnTwoFactorInterface
)] )]
#[ApiFilter(PropertyFilter::class)] #[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "aboutMe"])] #[ApiFilter(LikeFilter::class, properties: ["name", "aboutMe"])]
#[ApiFilter(DateFilter::class, strategy: DateFilter::EXCLUDE_NULL)] #[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
#[NoLockout()] #[NoLockout]
class User extends AttachmentContainingDBElement implements UserInterface, HasPermissionsInterface, TwoFactorInterface, class User extends AttachmentContainingDBElement implements UserInterface, HasPermissionsInterface, TwoFactorInterface,
BackupCodeInterface, TrustedDeviceInterface, WebauthnTwoFactorInterface, PreferredProviderInterface, PasswordAuthenticatedUserInterface, SamlUserInterface BackupCodeInterface, TrustedDeviceInterface, WebauthnTwoFactorInterface, PreferredProviderInterface, PasswordAuthenticatedUserInterface, SamlUserInterface
{ {
@ -133,8 +134,8 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
* @var string|null The theme * @var string|null The theme
*/ */
#[Groups(['full', 'import', 'user:read'])] #[Groups(['full', 'import', 'user:read'])]
#[ORM\Column(type: Types::STRING, name: 'config_theme', nullable: true)] #[ORM\Column(name: 'config_theme', type: Types::STRING, nullable: true)]
#[ValidTheme()] #[ValidTheme]
protected ?string $theme = null; protected ?string $theme = null;
/** /**
@ -143,10 +144,10 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
#[ORM\Column(type: Types::STRING, nullable: true)] #[ORM\Column(type: Types::STRING, nullable: true)]
protected ?string $pw_reset_token = null; protected ?string $pw_reset_token = null;
#[ORM\Column(type: Types::TEXT, name: 'config_instock_comment_a')] #[ORM\Column(name: 'config_instock_comment_a', type: Types::TEXT)]
protected string $instock_comment_a = ''; protected string $instock_comment_a = '';
#[ORM\Column(type: Types::TEXT, name: 'config_instock_comment_w')] #[ORM\Column(name: 'config_instock_comment_w', type: Types::TEXT)]
protected string $instock_comment_w = ''; protected string $instock_comment_w = '';
/** /**
@ -189,7 +190,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
*/ */
#[Assert\Timezone] #[Assert\Timezone]
#[Groups(['full', 'import', 'user:read'])] #[Groups(['full', 'import', 'user:read'])]
#[ORM\Column(type: Types::STRING, name: 'config_timezone', nullable: true)] #[ORM\Column(name: 'config_timezone', type: Types::STRING, nullable: true)]
protected ?string $timezone = ''; protected ?string $timezone = '';
/** /**
@ -197,7 +198,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
*/ */
#[Assert\Language] #[Assert\Language]
#[Groups(['full', 'import', 'user:read'])] #[Groups(['full', 'import', 'user:read'])]
#[ORM\Column(type: Types::STRING, name: 'config_language', nullable: true)] #[ORM\Column(name: 'config_language', type: Types::STRING, nullable: true)]
protected ?string $language = ''; protected ?string $language = '';
/** /**
@ -310,7 +311,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
#[Groups(['simple', 'extended', 'full', 'import'])] #[Groups(['simple', 'extended', 'full', 'import'])]
#[ORM\Embedded(class: 'PermissionData', columnPrefix: 'permissions_')] #[ORM\Embedded(class: 'PermissionData', columnPrefix: 'permissions_')]
#[ValidPermission()] #[ValidPermission]
protected ?PermissionData $permissions = null; protected ?PermissionData $permissions = null;
/** /**

View file

@ -27,7 +27,7 @@ use App\Services\LogSystem\EventCommentHelper;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener; use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\Event\RequestEvent;
#[AsEventListener()] #[AsEventListener]
class AddEditCommentRequestListener class AddEditCommentRequestListener
{ {
public function __construct(private readonly EventCommentHelper $helper) public function __construct(private readonly EventCommentHelper $helper)

View file

@ -47,8 +47,8 @@ class ProviderSelectType extends AbstractType
{ {
$resolver->setDefaults([ $resolver->setDefaults([
'choices' => $this->providerRegistry->getActiveProviders(), 'choices' => $this->providerRegistry->getActiveProviders(),
'choice_label' => ChoiceList::label($this, fn (?InfoProviderInterface $choice) => $choice?->getProviderInfo()['name']), 'choice_label' => ChoiceList::label($this, static fn (?InfoProviderInterface $choice) => $choice?->getProviderInfo()['name']),
'choice_value' => ChoiceList::value($this, fn(?InfoProviderInterface $choice) => $choice?->getProviderKey()), 'choice_value' => ChoiceList::value($this, static fn(?InfoProviderInterface $choice) => $choice?->getProviderKey()),
'multiple' => true, 'multiple' => true,
]); ]);

View file

@ -49,13 +49,15 @@ class ProjectAddPartsType extends AbstractType
$builder->add('bom_entries', ProjectBOMEntryCollectionType::class, [ $builder->add('bom_entries', ProjectBOMEntryCollectionType::class, [
'entry_options' => [ 'entry_options' => [
'constraints' => [ 'constraints' => [
new UniqueEntity(fields: ['part', 'project'], entityClass: ProjectBOMEntry::class, message: 'project.bom_entry.part_already_in_bom'), new UniqueEntity(fields: ['part', 'project'], message: 'project.bom_entry.part_already_in_bom',
new UniqueEntity(fields: ['name', 'project'], entityClass: ProjectBOMEntry::class, message: 'project.bom_entry.name_already_in_bom', ignoreNull: true), entityClass: ProjectBOMEntry::class),
new UniqueEntity(fields: ['name', 'project'], message: 'project.bom_entry.name_already_in_bom',
entityClass: ProjectBOMEntry::class, ignoreNull: true),
] ]
], ],
'constraints' => [ 'constraints' => [
new UniqueObjectCollection(fields: ['part'], message: 'project.bom_entry.part_already_in_bom'), new UniqueObjectCollection(message: 'project.bom_entry.part_already_in_bom', fields: ['part']),
new UniqueObjectCollection(fields: ['name'], message: 'project.bom_entry.name_already_in_bom'), new UniqueObjectCollection(message: 'project.bom_entry.name_already_in_bom', fields: ['name']),
] ]
]); ]);
$builder->add('submit', SubmitType::class, ['label' => 'save']); $builder->add('submit', SubmitType::class, ['label' => 'save']);

View file

@ -34,7 +34,7 @@ class UserSelectType extends AbstractType
{ {
$resolver->setDefaults([ $resolver->setDefaults([
'class' => User::class, 'class' => User::class,
'choice_label' => fn(Options $options) => fn(User $choice, $key, $value) => $choice->getFullName(true), 'choice_label' => fn(Options $options) => static fn(User $choice, $key, $value) => $choice->getFullName(true),
]); ]);
} }

View file

@ -50,7 +50,6 @@ class FilenameSanatizer
$filename = ltrim($filename, '.-'); $filename = ltrim($filename, '.-');
//Limit filename length to 255 bytes //Limit filename length to 255 bytes
$ext = pathinfo($filename, PATHINFO_EXTENSION); $ext = pathinfo($filename, PATHINFO_EXTENSION);
$filename = mb_strcut(pathinfo($filename, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($filename)) . ($ext ? '.' . $ext : ''); return mb_strcut(pathinfo($filename, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($filename)) . ($ext ? '.' . $ext : '');
return $filename;
} }
} }

View file

@ -35,6 +35,9 @@ abstract class AbstractMultiPlatformMigration extends AbstractMigration
final public const ADMIN_PW_LENGTH = 10; final public const ADMIN_PW_LENGTH = 10;
protected string $admin_pw = ''; protected string $admin_pw = '';
/** @noinspection SenselessProxyMethodInspection
* This method is required to redefine the logger type hint to protected
*/
public function __construct(Connection $connection, protected LoggerInterface $logger) public function __construct(Connection $connection, protected LoggerInterface $logger)
{ {
parent::__construct($connection, $logger); parent::__construct($connection, $logger);

View file

@ -43,7 +43,7 @@ use function Symfony\Component\Translation\t;
class AuthenticationEntryPoint implements AuthenticationEntryPointInterface class AuthenticationEntryPoint implements AuthenticationEntryPointInterface
{ {
public function __construct( public function __construct(
private UrlGeneratorInterface $urlGenerator, private readonly UrlGeneratorInterface $urlGenerator,
) { ) {
} }

View file

@ -41,7 +41,7 @@ use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
*/ */
class DetermineTypeFromElementIRIDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface class DetermineTypeFromElementIRIDenormalizer implements DenormalizerInterface, DenormalizerAwareInterface
{ {
const SUPPORTED_CLASSES = [ private const SUPPORTED_CLASSES = [
Attachment::class, Attachment::class,
AbstractParameter::class AbstractParameter::class
]; ];

View file

@ -54,7 +54,7 @@ class StructuralElementNormalizer implements NormalizerInterface
/** /**
* @return array<string, mixed> * @return array<string, mixed>
*/ */
public function normalize($object, string $format = null, array $context = []) public function normalize($object, string $format = null, array $context = []): array
{ {
if (!$object instanceof AbstractStructuralDBElement) { if (!$object instanceof AbstractStructuralDBElement) {
throw new \InvalidArgumentException('This normalizer only supports AbstractStructural objects!'); throw new \InvalidArgumentException('This normalizer only supports AbstractStructural objects!');

View file

@ -448,7 +448,7 @@ class AttachmentSubmitHandler
'g' => 1000 * 1000 * 1000, 'g' => 1000 * 1000 * 1000,
'gi' => 1 << 30, 'gi' => 1 << 30,
]; ];
if (ctype_digit((string) $maxSize)) { if (ctype_digit($maxSize)) {
return (int) $maxSize; return (int) $maxSize;
} }

View file

@ -119,6 +119,9 @@ class FileTypeFilterTools
//Convert jpg to .jpg //Convert jpg to .jpg
$element = '.'.$element; $element = '.'.$element;
} }
//Prevent weird side effects
unset($element);
} }
$elements = array_unique($elements); $elements = array_unique($elements);

View file

@ -133,7 +133,7 @@ class ElementTypeNameGenerator
{ {
$type = $this->getLocalizedTypeLabel($entity); $type = $this->getLocalizedTypeLabel($entity);
if ($use_html) { if ($use_html) {
return '<i>'.$type.':</i> '.htmlspecialchars((string) $entity->getName()); return '<i>'.$type.':</i> '.htmlspecialchars($entity->getName());
} }
return $type.': '.$entity->getName(); return $type.': '.$entity->getName();

View file

@ -42,7 +42,7 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use function \count; use function count;
/** /**
* This service is used to import the datastructures (categories, manufacturers, etc.) from a PartKeepr export. * This service is used to import the datastructures (categories, manufacturers, etc.) from a PartKeepr export.

View file

@ -222,7 +222,7 @@ trait PKImportHelperTrait
* @return void * @return void
* @throws \Exception * @throws \Exception
*/ */
protected function setCreationDate(TimeStampableInterface $entity, ?string $datetime_str) protected function setCreationDate(TimeStampableInterface $entity, ?string $datetime_str): void
{ {
if ($datetime_str !== null && $datetime_str !== '' && $datetime_str !== '0000-00-00 00:00:00') { if ($datetime_str !== null && $datetime_str !== '' && $datetime_str !== '0000-00-00 00:00:00') {
$date = new \DateTime($datetime_str); $date = new \DateTime($datetime_str);
@ -252,7 +252,7 @@ trait PKImportHelperTrait
$prefixes = $data['siprefix']; $prefixes = $data['siprefix'];
foreach ($prefixes as $prefix) { foreach ($prefixes as $prefix) {
if ((int) $prefix['id'] === $prefix_id) { if ((int) $prefix['id'] === $prefix_id) {
return pow((int) $prefix['base'], (int) $prefix['exponent']); return (int)$prefix['base'] ** (int)$prefix['exponent'];
} }
} }

View file

@ -45,7 +45,7 @@ class FileDTO
//Find all occurrences of non URL safe characters and replace them with their URL encoded version. //Find all occurrences of non URL safe characters and replace them with their URL encoded version.
//We only want to replace characters which can not have a valid meaning in a URL (what would break the URL). //We only want to replace characters which can not have a valid meaning in a URL (what would break the URL).
//Digikey provided some wrong URLs with a ^ in them, which is not a valid URL character. (https://github.com/Part-DB/Part-DB-server/issues/521) //Digikey provided some wrong URLs with a ^ in them, which is not a valid URL character. (https://github.com/Part-DB/Part-DB-server/issues/521)
$this->url = preg_replace_callback('/[^a-zA-Z0-9_\-.$+!*();\/?:@=&#%]/', fn($matches) => rawurlencode($matches[0]), $url); $this->url = preg_replace_callback('/[^a-zA-Z0-9_\-.$+!*();\/?:@=&#%]/', static fn($matches) => rawurlencode($matches[0]), $url);
} }

View file

@ -125,7 +125,7 @@ class Element14Provider implements InfoProviderInterface
} }
/** /**
* @param mixed[]|null $datasheets * @param array|null $datasheets
* @return FileDTO[]|null Array of FileDTOs * @return FileDTO[]|null Array of FileDTOs
*/ */
private function parseDataSheets(?array $datasheets): ?array private function parseDataSheets(?array $datasheets): ?array

View file

@ -40,7 +40,7 @@ class LCSCProvider implements InfoProviderInterface
public const DISTRIBUTOR_NAME = 'LCSC'; public const DISTRIBUTOR_NAME = 'LCSC';
public function __construct(private readonly HttpClientInterface $lcscClient, private string $currency, private bool $enabled = true) public function __construct(private readonly HttpClientInterface $lcscClient, private readonly string $currency, private readonly bool $enabled = true)
{ {
} }
@ -152,10 +152,7 @@ class LCSCProvider implements InfoProviderInterface
} }
//Build category by concatenating the catalogName and parentCatalogName //Build category by concatenating the catalogName and parentCatalogName
$category = null; $category = $product['parentCatalogName'] ?? null;
if (isset($product['parentCatalogName'])) {
$category = $product['parentCatalogName'];
}
if (isset($product['catalogName'])) { if (isset($product['catalogName'])) {
$category = ($category ?? '') . ' -> ' . $product['catalogName']; $category = ($category ?? '') . ' -> ' . $product['catalogName'];

View file

@ -210,7 +210,7 @@ class OctopartProvider implements InfoProviderInterface
$item = $this->partInfoCache->getItem($key); $item = $this->partInfoCache->getItem($key);
$item->set($part); $item->set($part);
$item->expiresAfter(3600 * 24 * 1); //Cache for 1 day $item->expiresAfter(3600 * 24); //Cache for 1 day
$this->partInfoCache->save($item); $this->partInfoCache->save($item);
} }
@ -295,7 +295,7 @@ class OctopartProvider implements InfoProviderInterface
//Built the category full path //Built the category full path
$category = null; $category = null;
if (!empty($part['category']['name'])) { if (!empty($part['category']['name'])) {
$category = implode(' -> ', array_map(fn($c) => $c['name'], $part['category']['ancestors'] ?? [])); $category = implode(' -> ', array_map(static fn($c) => $c['name'], $part['category']['ancestors'] ?? []));
if (!empty($category)) { if (!empty($category)) {
$category .= ' -> '; $category .= ' -> ';
} }

View file

@ -49,11 +49,7 @@ class TMEClient
public function isUsable(): bool public function isUsable(): bool
{ {
if ($this->token === '' || $this->secret === '') { return !($this->token === '' || $this->secret === '');
return false;
}
return true;
} }

View file

@ -27,7 +27,7 @@ use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
#[AsDecorator(decorates: DompdfFactoryInterface::class)] #[AsDecorator(decorates: DompdfFactoryInterface::class)]
class DompdfFactory implements DompdfFactoryInterface class DompdfFactory implements DompdfFactoryInterface
{ {
public function __construct(private string $fontDirectory, private string $tmpDirectory) public function __construct(private readonly string $fontDirectory, private readonly string $tmpDirectory)
{ {
//Create folder if it does not exist //Create folder if it does not exist
$this->createDirectoryIfNotExisting($this->fontDirectory); $this->createDirectoryIfNotExisting($this->fontDirectory);

View file

@ -119,7 +119,7 @@ final class PartProvider implements PlaceholderProviderInterface
} }
if ('[[DESCRIPTION_T]]' === $placeholder) { if ('[[DESCRIPTION_T]]' === $placeholder) {
return strip_tags((string) $parsedown->line($part->getDescription())); return strip_tags($parsedown->line($part->getDescription()));
} }
if ('[[COMMENT]]' === $placeholder) { if ('[[COMMENT]]' === $placeholder) {
@ -127,7 +127,7 @@ final class PartProvider implements PlaceholderProviderInterface
} }
if ('[[COMMENT_T]]' === $placeholder) { if ('[[COMMENT_T]]' === $placeholder) {
return strip_tags((string) $parsedown->line($part->getComment())); return strip_tags($parsedown->line($part->getComment()));
} }
return null; return null;

View file

@ -41,7 +41,7 @@ class LogDataFormatter
public function formatData(mixed $data, AbstractLogEntry $logEntry, string $fieldName): string public function formatData(mixed $data, AbstractLogEntry $logEntry, string $fieldName): string
{ {
if (is_string($data)) { if (is_string($data)) {
$tmp = '<span class="text-muted user-select-none">"</span>' . mb_strimwidth(htmlspecialchars($data), 0, self::STRING_MAX_LENGTH, ) . '<span class="text-muted user-select-none">"</span>'; $tmp = '<span class="text-muted user-select-none">"</span>' . mb_strimwidth(htmlspecialchars($data), 0, self::STRING_MAX_LENGTH) . '<span class="text-muted user-select-none">"</span>';
//Show special characters and line breaks //Show special characters and line breaks
$tmp = preg_replace('/\n/', '<span class="text-muted user-select-none">\\n</span><br>', $tmp); $tmp = preg_replace('/\n/', '<span class="text-muted user-select-none">\\n</span><br>', $tmp);
@ -87,7 +87,7 @@ class LogDataFormatter
private function formatJSON(array $data): string private function formatJSON(array $data): string
{ {
$json = htmlspecialchars(json_encode($data, JSON_PRETTY_PRINT), ENT_QUOTES | ENT_SUBSTITUTE); $json = htmlspecialchars(json_encode($data, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT), ENT_QUOTES | ENT_SUBSTITUTE);
return sprintf( return sprintf(
'<div data-controller="elements--json-formatter" data-json="%s"></div>', '<div data-controller="elements--json-formatter" data-json="%s"></div>',

View file

@ -105,7 +105,7 @@ class DBInfoHelper
*/ */
public function getDatabaseName(): ?string public function getDatabaseName(): ?string
{ {
return $this->connection->getDatabase() ?? null; return $this->connection->getDatabase();
} }
/** /**

View file

@ -98,7 +98,7 @@ final class OAuthTokenManager
$token = $this->getToken($app_name); $token = $this->getToken($app_name);
if (!$token) { if (!$token) {
throw new \Exception('No token was saved yet for '.$app_name); throw new \RuntimeException('No token was saved yet for '.$app_name);
} }
$client = $this->clientRegistry->getClient($app_name); $client = $this->clientRegistry->getClient($app_name);

View file

@ -17,7 +17,7 @@ use Doctrine\ORM\EntityManagerInterface;
final class PartLotWithdrawAddHelper final class PartLotWithdrawAddHelper
{ {
public function __construct(private readonly EventLogger $eventLogger, public function __construct(private readonly EventLogger $eventLogger,
private readonly EventCommentHelper $eventCommentHelper, private EntityManagerInterface $entityManager) private readonly EventCommentHelper $eventCommentHelper, private readonly EntityManagerInterface $entityManager)
{ {
} }

View file

@ -99,7 +99,7 @@ class NodesListBuilder
/** @var StructuralDBElementRepository $repo */ /** @var StructuralDBElementRepository $repo */
$repo = $this->em->getRepository($class_name); $repo = $this->em->getRepository($class_name);
return array_map(fn(AbstractDBElement $element) => $element->getID(), $repo->getFlatList($parent)); return array_map(static fn(AbstractDBElement $element) => $element->getID(), $repo->getFlatList($parent));
}); });
} }

View file

@ -59,7 +59,7 @@ class TreeViewGenerator
protected ElementCacheTagGenerator $tagGenerator, protected ElementCacheTagGenerator $tagGenerator,
protected UserCacheKeyGenerator $keyGenerator, protected UserCacheKeyGenerator $keyGenerator,
protected TranslatorInterface $translator, protected TranslatorInterface $translator,
private UrlGeneratorInterface $router, private readonly UrlGeneratorInterface $router,
protected bool $rootNodeExpandedByDefault, protected bool $rootNodeExpandedByDefault,
protected bool $rootNodeEnabled, protected bool $rootNodeEnabled,

View file

@ -33,8 +33,8 @@ class DecoratedGoogleAuthenticator implements GoogleAuthenticatorInterface
public function __construct( public function __construct(
#[AutowireDecorated] #[AutowireDecorated]
private GoogleAuthenticatorInterface $inner, private readonly GoogleAuthenticatorInterface $inner,
private RequestStack $requestStack) private readonly RequestStack $requestStack)
{ {
} }

View file

@ -15,7 +15,7 @@ class PartDBInfoProvider implements ProviderInterface
public function __construct(private readonly VersionManagerInterface $versionManager, public function __construct(private readonly VersionManagerInterface $versionManager,
private readonly GitVersionInfo $gitVersionInfo, private readonly GitVersionInfo $gitVersionInfo,
private readonly string $partdb_title, private readonly string $partdb_title,
private string $base_currency, private readonly string $base_currency,
private readonly BannerHelper $bannerHelper, private readonly BannerHelper $bannerHelper,
private readonly string $default_uri, private readonly string $default_uri,
private readonly string $global_timezone, private readonly string $global_timezone,

View file

@ -86,11 +86,7 @@ class UniqueObjectCollectionValidator extends ConstraintValidator
private function getNormalizer(UniqueObjectCollection $unique): callable private function getNormalizer(UniqueObjectCollection $unique): callable
{ {
if (null === $unique->normalizer) { return $unique->normalizer ?? static fn($value) => $value;
return static fn ($value) => $value;
}
return $unique->normalizer;
} }
private function reduceElementKeys(array $fields, array $element, UniqueObjectCollection $constraint): array private function reduceElementKeys(array $fields, array $element, UniqueObjectCollection $constraint): array

View file

@ -38,7 +38,7 @@ use function strlen;
class ValidGoogleAuthCodeValidator extends ConstraintValidator class ValidGoogleAuthCodeValidator extends ConstraintValidator
{ {
public function __construct(private GoogleAuthenticatorInterface $googleAuthenticator, private Security $security) public function __construct(private readonly GoogleAuthenticatorInterface $googleAuthenticator, private readonly Security $security)
{ {
} }