First of all, I use Gedmo's softdeletable to manage deleted entities. Also I have my own EntityField
:
namespace App\Form\Fields;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
class EntityField extends AbstractType
{
public function configureOptions(OptionsResolver $resolver): void
{
parent::configureOptions($resolver);
// some additional configuration
$resolver->setDefaults([
'query_builder' => function (EntityRepository $er): QueryBuilder {
return $er->createQueryBuilder('entity')
->andWhere('entity.deletedAt IS NULL');
},
]);
}
public function getParent(): string
{
return EntityType::class;
}
}
As you can see, I don't use softdeleteable
filter in Doctrine, but instead I use custom query_builder
in my EntityField
.
It works fine as long as I've softdeleted entity thas has been already used.
E.g. I have User
, File
and FileType
entities, User
has collection of File
s and File
has FileType
. I've softdeleted some FileType
and for me it means I don't want to create new File
s of this FileType
, but all existing File
s of this FileType
are still legal and have to be properly displayed in forms. "Properly" means, in this case, that if I render File
form and this exact file has deleted FileType
, the select must have this FileType
. But now I have no selected value in this field and it loks like "there is no selected file type for this file" that is not.
I tried to modify query_builder
in FormEvents::PRE_SET_DATA
event and add the value from the database, but it looks like list of choises is generated before this event, because I can see updated query_builder
in the form, but the real query corresponds to unmodified query_buider
.
There are buildView
and finishView
methods, but I don't think it's a good idea to use them, because in this case I need to manually modify form attributes.
So, is there a "Symfony way" to do that? Maybe I forgot some form options?
First of all, I use Gedmo's softdeletable to manage deleted entities. Also I have my own EntityField
:
namespace App\Form\Fields;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
class EntityField extends AbstractType
{
public function configureOptions(OptionsResolver $resolver): void
{
parent::configureOptions($resolver);
// some additional configuration
$resolver->setDefaults([
'query_builder' => function (EntityRepository $er): QueryBuilder {
return $er->createQueryBuilder('entity')
->andWhere('entity.deletedAt IS NULL');
},
]);
}
public function getParent(): string
{
return EntityType::class;
}
}
As you can see, I don't use softdeleteable
filter in Doctrine, but instead I use custom query_builder
in my EntityField
.
It works fine as long as I've softdeleted entity thas has been already used.
E.g. I have User
, File
and FileType
entities, User
has collection of File
s and File
has FileType
. I've softdeleted some FileType
and for me it means I don't want to create new File
s of this FileType
, but all existing File
s of this FileType
are still legal and have to be properly displayed in forms. "Properly" means, in this case, that if I render File
form and this exact file has deleted FileType
, the select must have this FileType
. But now I have no selected value in this field and it loks like "there is no selected file type for this file" that is not.
I tried to modify query_builder
in FormEvents::PRE_SET_DATA
event and add the value from the database, but it looks like list of choises is generated before this event, because I can see updated query_builder
in the form, but the real query corresponds to unmodified query_buider
.
There are buildView
and finishView
methods, but I don't think it's a good idea to use them, because in this case I need to manually modify form attributes.
So, is there a "Symfony way" to do that? Maybe I forgot some form options?
Well, I did it.
As I assumed from the very beginning, the only way to solve this problem is using buildView method:
public function buildView(FormView $view, FormInterface $form, array $options): void
{
if ($options['show_selected_choice']) {
$choices = $view->vars['choices'];
if ($data = $view->vars['data']) {
$selectedValue = $data->getId();
if (!array_key_exists($selectedValue, $choices)) {
$choiceView = new ChoiceView(
$data,
$this->getChoiceParam($data, $options['choice_value']),
$this->getChoiceParam($data, $options['choice_label']),
[
...$choices[array_key_first($choices)]->attr,
'disabled' => true,
]
);
$view->vars['choices'][$selectedValue] = $choiceView;
if ($options['expanded']) {
$this->addSubForm($form, $selectedValue, $choiceView, $options);
}
}
}
}
}
private function getChoiceParam(object $entity, mixed $param): string
{
return match (true) {
$param instanceof AbstractStaticOption => $param->getOption()($entity),
$param instanceof PropertyPath => $this->propertyAccessor->getValue($entity, $param),
is_callable($param) => $param($entity),
is_string($param) => $this->propertyAccessor->getValue($entity, new PropertyPath($param)),
default => (string)$param,
};
}
Note the $this->addSubForm
: you need to add child form for expanded fields like checkboxes or radio. And you can just copy-paste private function addSubForm
from ChoiceType
.
Ah, and don't forget to inject PropertyAccessorInterface $propertyAccessor
to your form class.
Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
? – craigh Commented Feb 1 at 13:14