mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-01-13 05:39:33 +00:00
Baugruppen Stückliste um referenzierte Baugruppe erweitern
This commit is contained in:
parent
14b0665daa
commit
c7aa730bc3
22 changed files with 332 additions and 116 deletions
124
src/Form/Type/AssemblySelectType.php
Normal file
124
src/Form/Type/AssemblySelectType.php
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Entity\AssemblySystem\Assembly;
|
||||
use App\Entity\Attachments\Attachment;
|
||||
use App\Services\Attachments\AssemblyPreviewGenerator;
|
||||
use App\Services\Attachments\AttachmentURLGenerator;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\ChoiceList\ChoiceList;
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
use Symfony\Component\Form\Event\PreSetDataEvent;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
class AssemblySelectType extends AbstractType implements DataMapperInterface
|
||||
{
|
||||
public function __construct(private readonly UrlGeneratorInterface $urlGenerator, private readonly EntityManagerInterface $em, private readonly AssemblyPreviewGenerator $previewGenerator, private readonly AttachmentURLGenerator $attachmentURLGenerator)
|
||||
{
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
//At initialization, we have to fill the form element with our selected data, so the user can see it
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) {
|
||||
$form = $event->getForm();
|
||||
$config = $form->getConfig()->getOptions();
|
||||
$data = $event->getData() ?? [];
|
||||
|
||||
$config['compound'] = false;
|
||||
$config['choices'] = is_iterable($data) ? $data : [$data];
|
||||
$config['error_bubbling'] = true;
|
||||
|
||||
$form->add('autocomplete', EntityType::class, $config);
|
||||
});
|
||||
|
||||
//After form submit, we have to add the selected element as choice, otherwise the form will not accept this element
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) {
|
||||
$data = $event->getData();
|
||||
$form = $event->getForm();
|
||||
$options = $form->get('autocomplete')->getConfig()->getOptions();
|
||||
|
||||
|
||||
if (!isset($data['autocomplete']) || '' === $data['autocomplete'] || empty($data['autocomplete'])) {
|
||||
$options['choices'] = [];
|
||||
} else {
|
||||
//Extract the ID from the submitted data
|
||||
$id = $data['autocomplete'];
|
||||
//Find the element in the database
|
||||
$element = $this->em->find($options['class'], $id);
|
||||
|
||||
//Add the element as choice
|
||||
$options['choices'] = [$element];
|
||||
$options['error_bubbling'] = true;
|
||||
$form->add('autocomplete', EntityType::class, $options);
|
||||
}
|
||||
});
|
||||
|
||||
$builder->setDataMapper($this);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'class' => Assembly::class,
|
||||
'choice_label' => 'name',
|
||||
'compound' => true,
|
||||
'error_bubbling' => false,
|
||||
]);
|
||||
|
||||
error_log($this->urlGenerator->generate('typeahead_assemblies', ['query' => '__QUERY__']));
|
||||
|
||||
$resolver->setDefaults([
|
||||
'attr' => [
|
||||
'data-controller' => 'elements--assembly-select',
|
||||
'data-autocomplete' => $this->urlGenerator->generate('typeahead_assemblies', ['query' => '__QUERY__']),
|
||||
'autocomplete' => 'off',
|
||||
],
|
||||
]);
|
||||
|
||||
$resolver->setDefaults([
|
||||
//Prefill the selected choice with the needed data, so the user can see it without an additional Ajax request
|
||||
'choice_attr' => ChoiceList::attr($this, function (?Assembly $assembly) {
|
||||
if($assembly instanceof Assembly) {
|
||||
//Determine the picture to show:
|
||||
$preview_attachment = $this->previewGenerator->getTablePreviewAttachment($assembly);
|
||||
if ($preview_attachment instanceof Attachment) {
|
||||
$preview_url = $this->attachmentURLGenerator->getThumbnailURL($preview_attachment,
|
||||
'thumbnail_sm');
|
||||
} else {
|
||||
$preview_url = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $assembly instanceof Assembly ? [
|
||||
'data-description' => $assembly->getDescription() ? mb_strimwidth($assembly->getDescription(), 0, 127, '...') : '',
|
||||
'data-category' => '',
|
||||
'data-footprint' => '',
|
||||
'data-image' => $preview_url,
|
||||
] : [];
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
public function mapDataToForms($data, \Traversable $forms): void
|
||||
{
|
||||
$form = current(iterator_to_array($forms, false));
|
||||
$form->setData($data);
|
||||
}
|
||||
|
||||
public function mapFormsToData(\Traversable $forms, &$data): void
|
||||
{
|
||||
$form = current(iterator_to_array($forms, false));
|
||||
$data = $form->getData();
|
||||
}
|
||||
|
||||
}
|
||||
69
src/Repository/AssemblyRepository.php
Normal file
69
src/Repository/AssemblyRepository.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\AssemblySystem\Assembly;
|
||||
|
||||
/**
|
||||
* @template TEntityClass of Assembly
|
||||
* @extends DBElementRepository<TEntityClass>
|
||||
*/
|
||||
class AssemblyRepository extends StructuralDBElementRepository
|
||||
{
|
||||
/**
|
||||
* @return Assembly[]
|
||||
*/
|
||||
public function autocompleteSearch(string $query, int $max_limits = 50): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('assembly');
|
||||
$qb->select('assembly')
|
||||
->where('ILIKE(assembly.name, :query) = TRUE')
|
||||
->orWhere('ILIKE(assembly.description, :query) = TRUE');
|
||||
|
||||
$qb->setParameter('query', '%'.$query.'%');
|
||||
|
||||
$qb->setMaxResults($max_limits);
|
||||
$qb->orderBy('NATSORT(assembly.name)', 'ASC');
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
}
|
||||
93
src/Services/Attachments/AssemblyPreviewGenerator.php
Normal file
93
src/Services/Attachments/AssemblyPreviewGenerator.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Attachments;
|
||||
|
||||
use App\Entity\AssemblySystem\Assembly;
|
||||
use App\Entity\Attachments\Attachment;
|
||||
|
||||
class AssemblyPreviewGenerator
|
||||
{
|
||||
public function __construct(protected AttachmentManager $attachmentHelper)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of attachments that can be used for previewing the assembly ordered by priority.
|
||||
*
|
||||
* @param Assembly $assembly the assembly for which the attachments should be determined
|
||||
*
|
||||
* @return (Attachment|null)[]
|
||||
*
|
||||
* @psalm-return list<Attachment|null>
|
||||
*/
|
||||
public function getPreviewAttachments(Assembly $assembly): array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
//Master attachment has top priority
|
||||
$attachment = $assembly->getMasterPictureAttachment();
|
||||
if ($this->isAttachmentValidPicture($attachment)) {
|
||||
$list[] = $attachment;
|
||||
}
|
||||
|
||||
//Then comes the other images of the assembly
|
||||
foreach ($assembly->getAttachments() as $attachment) {
|
||||
//Dont show the master attachment twice
|
||||
if ($this->isAttachmentValidPicture($attachment) && $attachment !== $assembly->getMasterPictureAttachment()) {
|
||||
$list[] = $attachment;
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines what attachment should be used for previewing a assembly (especially in assembly table).
|
||||
* The returned attachment is guaranteed to be existing and be a picture.
|
||||
*
|
||||
* @param Assembly $assembly The assembly for which the attachment should be determined
|
||||
*/
|
||||
public function getTablePreviewAttachment(Assembly $assembly): ?Attachment
|
||||
{
|
||||
$attachment = $assembly->getMasterPictureAttachment();
|
||||
if ($this->isAttachmentValidPicture($attachment)) {
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a attachment is exising and a valid picture.
|
||||
*
|
||||
* @param Attachment|null $attachment the attachment that should be checked
|
||||
*
|
||||
* @return bool true if the attachment is valid
|
||||
*/
|
||||
protected function isAttachmentValidPicture(?Attachment $attachment): bool
|
||||
{
|
||||
return $attachment instanceof Attachment
|
||||
&& $attachment->isPicture()
|
||||
&& $this->attachmentHelper->isFileExisting($attachment);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue