I have a WholesaleRuleset entity (https://pastebin.com/PYjHmGi1) With a M2M relationship to WholesaleRuleQuantityStep (https://pastebin.com/JLrQfQV7).
This relationship is properly set up. It works perfectly in WholesaleRulesetType (https://pastebin.com/VGCdABb9).This is the "Edit" page of the submitted ruleset with its quantity step rule collection.
Here's where my requirements get tricky. As you can see, the admin can add quantitySteps at the scopes Taxonomy, Product, and Taxonomy using different tabs.
I am trying to have multiple, separate collections submit to the same entity.:
WholesaleRulesetType would end up looking like this (even added the necessary collection methods in this trait https://pastebin.com/F5B97xgW). Desired form type:
->add(
'quantityStepRulesByTaxon',
CollectionType::class,
[
'entry_type' => WholesaleRuleQuantityStepByTaxonType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
]
)
->add(
'quantityStepRulesByProduct',
CollectionType::class,
[
'entry_type' => WholesaleRuleQuantityStepByProductType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
]
)
->add(
'quantityStepRulesByProductVariant',
CollectionType::class,
[
'entry_type' => WholesaleRuleQuantityStepByProductVariantType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
]
);
And in the request data they all combine to submit to WholesaleRuleQuantityStep. It must be separate collections, because you cannot use the same form field more than once simultaneously. I have been stuck on this for a month. No responses from the Sylius or Symfony slacks. Please. Help.
(Okay, I hope I do get this right now, for brevity (and lazyness) I have shortened all class names and field names, because your MWE is quite extensive and unnecessarily verbose. So, you have to map my entity RuleSet to your WholesaleRuleset and my Rule to your WholesaleRuleQuantityStep.)
I believe you have two entities RuleSet and Rule, connected via many-to-many. The rules have essentially three types product, variant and taxon.
Now you want to display those rules in different form fields, which you try to mitigate by essentially creating new properties on your object via your trait, but as far as I can tell, those are not connected at all to the many-to-many field.
To be able to edit your entity later as well, I propose the following: Make your "fields" fully virtual. For Symfony forms a "field" is actually something that a property accessor can access for reading and writing (getting and setting, or getting and adding and removing in the case of collections).
I have done this a couple of times and I love the concept, for each of your types of rules do this:
// in class RuleSet
protected $rules; // <-- collection containing *all* rules.
public function getTaxonRules(): array {
// extract the rules of type taxon
return array_filter(function($rule) {
return $rule->getType() == 'taxon';
}, $this->rules->toArray());
}
public function addTaxonRule(Rule $rule) {
// you might even set the type here ;o)
if (!in_array($rule, $this->getTaxonRules()) && $rule->getType() == 'taxon') {
$this->rules->add($rule);
}
}
public function removeTaxonRule(Rule $rule) {
if (in_array($rule, $this->getTaxonRules()) && $rule->getType() == 'taxon') {
$this->rules->removeElement($rule);
}
}
// add these functions:
// public function getVariantRules() ...
// public function addVariantRule(Rule $rule) ...
// public function removeVariantRule(Rule $rule) ...
// public function getProductRules() ...
// public function addProductRule(Rule $rule) ...
// public function removeProductRule(Rule $rule) ...
This way, the entity acts as if it has a field taxonRules
, because what really is a field if not its interface to the outside world (which is getters and setters). The outside doesn't care about the implementation details inside.
Note: the tests for $rule->getType() == ...
really shouldn't be necessary. Also, no form events or other fancy stuff should be required, you can now just go ahead and have three form fields.
Side-Note: I really don't like exposing Collection
s to the outside of my entity. Any changes to the collection is supposed to be happening via setters/adders/removers. returning an array will also make byReference => false
obsolete. In my opinion having collections isn't worth the trouble in this case. You might be able to make it work, but the amount of code will be bigger (with nothing gained, imho).
(Side-Burn: yoda conditions are out! Also: try adding a function to your Rule isTaxonRule(): bool
, don't use "magic" strings in your code, they don't support auto-completion, at least use constants on your Rule class, where these strings are stored and go like $rule->getType() === Rule::TAXON
, however, don't use strings directly. a typo can mess up your code and you might only notice in production ;o))