-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add adders and remover callbacks #66
Comments
Just to add that I made numerous attempts of implementing it. One of them was new form-type extension like this, with bigger or lesser priority: Note:I removed lots of code in this example but you get the idea. public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefault('adder', null);
$resolver->setAllowedTypes('adder', ['null', Closure::class]);
$resolver->setNormalizer('write_property_path', function (Options $options) {
return function ($data, array $values) {
// get original values via $option['read_property_path']
// compare and call $options['adder'] and $options['remover']
foreach($newValues as $newValue) {
$adder($data, $newValue);
}
foreach($removedValues as $removedValue) {
$remover($data, $removedValue);
}
}
//
}); Using The errors I got was either failure at this point: throw new InvalidConfigurationException(
'Cannot use "read_property_path" without "write_property_path".'
); or circular dependency error by OptionsResolver. So none of my attempts can work, it needs changes in resolver. |
Sorry for making a sale, but there is one more use-case I forgot but can be used in implementation. It is when when $data is not an entity with adder and remover methods, but when I can work only with array of entities. This happens when I have unidirectional Many2One relation. In this situation, each new entity must be manually Example: // ComboController.php
$data = [
'awards' => $awardRepository->findAll(),
// can have other entities here
]; and form, removed irrelevant parts: $builder
->add('awards', BootstrapCollectionType::class, [
// ... other options ...
'read_property_path' => function ($data) {
return $data['awards'];
},
'write_property_path' => function ($data, array $newValues) {
$oldValues = $data['awards'];
$this->collectionFormWriter->diff($data, $oldValues, $newValues,
// $data is not used in this example but will be in other cases
function ($data, Award $award) {$this->em->persist($award);},
function ($data, Award $award) {$this->em->remove($award);}
);
},
]); And this writer: class CollectionFormWriter
{
public function diff($data, array $oldValues, array $newValues, callable $adder, callable $remover): void
{
$addedValues = $this->getDiff($oldValues, $newValues);
$removedValues = $this->getDiff($newValues, $oldValues);
foreach ($addedValues as $addedValue) {
$adder($data, $addedValue);
}
foreach ($removedValues as $removedValue) {
$remover($data, $removedValue);
}
}
private function getDiff(array $oldValues, array $newValues): array
{
return array_filter($newValues, static function ($newValue) use ($oldValues) {
return !in_array($newValue, $oldValues, true);
});
} This is something I am using right now, looks like everything is working. My example deals only with arrays; iterables are easy to convert. |
When dealing with collections, using
write_property_path
can be problematic when dealing with non-direct relations.Simple example; please note I am using new arrow function for readibility, recent github changes for code blocks are problematic:
Lets say we have Category<->Product many2many but with association class. But our form don't need to display assoc class, it can be much simpler:
And Category entity:
But when there is only
setProducts(array $products)
code would be much more complex. User would need to manually compare existing vs new values, something that Symfony already does perfectly.
Usage
New options would be:
If
adder
andremover
are set, thenwrite_property_path
is not required.This has more usages when there is extra param needed, something I really need in my current app and the reason why I have to use DTO. If interested, I will put the problem here.
But in short; I have m2m relation between Client and CustomValue. CustomValue has discriminator string column
type
.Entire ClientType form is made of 11 pages (flow bundle) and most of fields are working with that m2m relation but depend on value
type
column.So for each value of
type
, I would need to write getter, adder and remover methods and make my entity totally unreadable.With this RFC, I could change that to simple:
This way, I would need only 3 methods.
The text was updated successfully, but these errors were encountered: