You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Apologies in advance if this is hard to follow. This is with the latest version, dev-master 011aaa7.
My schema is using 3 primary keys on an isCrossRef table. In this case, 1 or more performers appearing at an event, with a flag to say who is a headliner. Schema is like this:
I am using an ObjectCombinationCollection to collect the perfomer & headliner flag, and calling the generated setPerformerHeadliner() function like this:
$performers = new \Propel\Runtime\Collection\ObjectCombinationCollection();
foreach ($headliners as $performerJson) {
$performer = Performer::CreateFromJSON($performerJson);
$performers->push($performer, true); // is headliner
}
foreach ($supportingActs as $performerJson) {
$performer = Performer::CreateFromJSON($performerJson);
$performers->push($performer, false); // is NOT headliner
}
$event->setPerformerIsHeadliners($performers);
The first pass works correctly, rows are inserted as expected.
On subsequent passes, however, the event_performers cross-reference rows are deleted and re-created on every pass.
Digging in to the generated setPerformerIsHeadliners() function, the existing records are fetched from the db, diff() is called, and then any records scheduled for deletion are removed:
public function setPerformerIsHeadliners(Collection $PerformerIsHeadliners, ?ConnectionInterface $con = null)
{
$this->clearPerformerIsHeadliners();
$currentPerformerIsHeadliners = $this->getPerformerIsHeadliners();
$combinationCollPerformerIsHeadlinersScheduledForDeletion = $currentPerformerIsHeadliners->diff($PerformerIsHeadliners);
foreach ($combinationCollPerformerIsHeadlinersScheduledForDeletion as $toDelete) {
$this->removePerformerIsHeadliner(...$toDelete);
}
foreach ($PerformerIsHeadliners as $PerformerIsHeadliner) {
if (!$currentPerformerIsHeadliners->contains(...$PerformerIsHeadliner)) {
$this->doAddPerformerIsHeadliner(...$PerformerIsHeadliner);
}
}
If I print $combinationCollPerformerIsHeadlinersScheduledForDeletion, on every subsequent pass it lists all of my cross-references. So it appears diff() is not working as expected and all of my relationships are deleted.
Notice that if (!$currentPerformerIsHeadliners->contains(...$PerformerIsHeadliner)) { is using the spread operator.
$currentPerformerIsHeadliners returns an object of type ObjectCombinationCollection, but there is no overload for diff(). The version of diff() that is called is in the Collection class, which doesn't use the spread operator on the contains() call:
class Collection implements ArrayAccess, IteratorAggregate, Countable, Serializable
{
public function diff(Collection $collection): self
{
$diff = clone $this;
$diff->clear();
foreach ($this as $object) {
if (!$collection->contains($object)) {
$diff[] = $object;
}
}
return $diff;
}
This results in the search() function of ObjectCombinationCollection being passed args that look like:
[ 0 => [ Performer_object, IsHeadliner_bool ] ]
You can see the foreach (func_get_args() as $pos => $obj) { loop is expecting to loop over the 2 args, checking if they are ActiveRecordInterface. But the double-array breaks that:
public function search($element)
{
$hashes = [];
$isActiveRecord = [];
foreach (func_get_args() as $pos => $obj) {
if ($obj instanceof ActiveRecordInterface) {
$hashes[$pos] = $obj->hashCode();
$isActiveRecord[$pos] = true;
} else {
$hashes[$pos] = $obj;
$isActiveRecord[$pos] = false;
}
}
If I edit ObjectCombinationCollection and overload the diff() function, such that it uses the spread operator like in setPerformerIsHeadliners():
class ObjectCombinationCollection extends ObjectCollection
{
public function diff(Collection $collection): self
{
$diff = clone $this;
$diff->clear();
foreach ($this as $object) {
if (!$collection->contains(...$object)) {
$diff[] = $object;
}
}
return $diff;
}
Then search() is passed args like [ Performer_object, IsHeadliner_bool ] which works correctly, and my generated setPerformerIsHeadliners() functions work properly. Rows aren't deleted erroneously on every pass.
I believe this is a bug.
The text was updated successfully, but these errors were encountered:
To get you started, it looks like there are a bunch of tests for ObjectCombinationCollection, but they are not at the expected place, but in the files:
So you could create the ObjectCombinationCollection.php file using CollectionTest.php as a template (or find another way of course). Let me know if you run into issues. Godspeed!
Apologies in advance if this is hard to follow. This is with the latest version,
dev-master 011aaa7
.My schema is using 3 primary keys on an
isCrossRef
table. In this case, 1 or more performers appearing at an event, with a flag to say who is a headliner. Schema is like this:I am using an
ObjectCombinationCollection
to collect the perfomer & headliner flag, and calling the generatedsetPerformerHeadliner()
function like this:The first pass works correctly, rows are inserted as expected.
On subsequent passes, however, the
event_performers
cross-reference rows are deleted and re-created on every pass.Digging in to the generated
setPerformerIsHeadliners()
function, the existing records are fetched from the db,diff()
is called, and then any records scheduled for deletion are removed:If I print
$combinationCollPerformerIsHeadlinersScheduledForDeletion
, on every subsequent pass it lists all of my cross-references. So it appearsdiff()
is not working as expected and all of my relationships are deleted.Notice that
if (!$currentPerformerIsHeadliners->contains(...$PerformerIsHeadliner)) {
is using the spread operator.$currentPerformerIsHeadliners
returns an object of typeObjectCombinationCollection
, but there is no overload fordiff()
. The version ofdiff()
that is called is in theCollection
class, which doesn't use the spread operator on thecontains()
call:This results in the
search()
function ofObjectCombinationCollection
being passed args that look like:You can see the
foreach (func_get_args() as $pos => $obj) {
loop is expecting to loop over the 2 args, checking if they areActiveRecordInterface
. But the double-array breaks that:If I edit
ObjectCombinationCollection
and overload thediff()
function, such that it uses the spread operator like insetPerformerIsHeadliners()
:Then
search()
is passed args like[ Performer_object, IsHeadliner_bool ]
which works correctly, and my generatedsetPerformerIsHeadliners()
functions work properly. Rows aren't deleted erroneously on every pass.I believe this is a bug.
The text was updated successfully, but these errors were encountered: