/** * Computes node positions and updates the sort field in memory and in the db * * @param SortableAdapter $ea * @param array $config * @param ClassMetadata $meta * @param object $object */ private function processUpdate(SortableAdapter $ea, array $config, $meta, $object) { $em = $ea->getObjectManager(); $uow = $em->getUnitOfWork(); $changed = false; $groupHasChanged = false; $changeSet = $ea->getObjectChangeSet($uow, $object); // Get groups $groups = $this->getGroups($meta, $config, $object); // handle old groups $oldGroups = $groups; foreach (array_keys($groups) as $group) { if (array_key_exists($group, $changeSet)) { $changed = true; $oldGroups[$group] = $changeSet[$group][0]; } } if ($changed) { $oldHash = $this->getHash($oldGroups, $config); $this->maxPositions[$oldHash] = $this->getMaxPosition($ea, $meta, $config, $object, $oldGroups); if (array_key_exists($config['position'], $changeSet)) { $oldPosition = $changeSet[$config['position']][0]; } else { $oldPosition = $meta->getReflectionProperty($config['position'])->getValue($object); } $this->addRelocation($oldHash, $config['useObjectClass'], $oldGroups, $oldPosition + 1, $this->maxPositions[$oldHash] + 1, -1); $groupHasChanged = true; } if (array_key_exists($config['position'], $changeSet)) { // position was manually updated $oldPosition = $changeSet[$config['position']][0]; $newPosition = $changeSet[$config['position']][1]; $changed = $changed || $oldPosition != $newPosition; } elseif ($changed) { // group has changed, so position has to be recalculated $oldPosition = -1; $newPosition = -1; // specific case } if ($groupHasChanged) { $oldPosition = -1; } if (!$changed) { return; } // Get hash $hash = $this->getHash($groups, $config); // Get max position if (!isset($this->maxPositions[$hash])) { $this->maxPositions[$hash] = $this->getMaxPosition($ea, $meta, $config, $object); } // Compute position if it is negative if ($newPosition < 0) { if ($oldPosition === -1) { $newPosition += $this->maxPositions[$hash] + 2; // position == -1 => append at end of list } else { $newPosition += $this->maxPositions[$hash] + 1; // position == -1 => append at end of list } if ($newPosition < 0) { $newPosition = 0; } } else { $newPosition = min(array($this->maxPositions[$hash], $newPosition)); } // Compute relocations /* CASE 1: shift backwards |----0----|----1----|----2----|----3----|----4----| |--node1--|--node2--|--node3--|--node4--|--node5--| Update node4: setPosition(1) --> Update position + 1 where position in [1,3) |--node1--|--node4--|--node2--|--node3--|--node5--| CASE 2: shift forward |----0----|----1----|----2----|----3----|----4----| |--node1--|--node2--|--node3--|--node4--|--node5--| Update node2: setPosition(3) --> Update position - 1 where position in (1,3] |--node1--|--node3--|--node4--|--node2--|--node5--| */ $relocation = null; if ($oldPosition === -1) { // special case when group changes $relocation = array($hash, $config['useObjectClass'], $groups, $newPosition, -1, +1); } elseif ($newPosition < $oldPosition) { $relocation = array($hash, $config['useObjectClass'], $groups, $newPosition, $oldPosition, +1); } elseif ($newPosition > $oldPosition) { $relocation = array($hash, $config['useObjectClass'], $groups, $oldPosition + 1, $newPosition + 1, -1); } // Apply existing relocations $applyDelta = 0; if (isset($this->relocations[$hash])) { foreach ($this->relocations[$hash]['deltas'] as $delta) { if ($delta['start'] <= $newPosition && ($delta['stop'] > $newPosition || $delta['stop'] < 0)) { $applyDelta += $delta['delta']; } } } $newPosition += $applyDelta; if ($relocation) { // Add relocation call_user_func_array(array($this, 'addRelocation'), $relocation); } // Set new position $meta->getReflectionProperty($config['position'])->setValue($object, $newPosition); $ea->recomputeSingleObjectChangeSet($uow, $meta, $object); }
/** * @param SortableAdapter $ea */ private function processRelocations(SortableAdapter $ea) { $em = $ea->getObjectManager(); foreach ($this->relocations as $hash => $relocation) { $config = $this->getConfiguration($em, $relocation['name']); foreach ($relocation['deltas'] as $delta) { if ($delta['start'] > $this->maxPositions[$hash] || $delta['delta'] == 0) { continue; } $ea->updatePositions($relocation, $delta, $config); $meta = $em->getClassMetadata($relocation['name']); // now walk through the unit of work in memory objects and sync those $uow = $em->getUnitOfWork(); foreach ($uow->getIdentityMap() as $className => $objects) { // for inheritance mapped classes, only root is always in the identity map if ($className !== $ea->getRootObjectClass($meta) || !$this->getConfiguration($em, $className)) { continue; } foreach ($objects as $object) { if ($object instanceof Proxy && !$object->__isInitialized__) { continue; } // if the entity's position is already changed, stop now if (array_key_exists($config['position'], $ea->getObjectChangeSet($uow, $object))) { continue; } $oid = spl_object_hash($object); $pos = $meta->getReflectionProperty($config['position'])->getValue($object); $matches = $pos >= $delta['start']; $matches = $matches && ($delta['stop'] <= 0 || $pos < $delta['stop']); $value = reset($relocation['groups']); while ($matches && ($group = key($relocation['groups']))) { $gr = $meta->getReflectionProperty($group)->getValue($object); if (null === $value) { $matches = $gr === null; } else { $matches = $gr === $value; } $value = next($relocation['groups']); } if ($matches) { $meta->getReflectionProperty($config['position'])->setValue($object, $pos + $delta['delta']); $ea->setOriginalObjectProperty($uow, $oid, $config['position'], $pos + $delta['delta']); } } } } } // Clear relocations $this->relocations = array(); $this->maxPositions = array(); }