{"action":"created","issue":{"url":"https://api.github.com/repos/symfony/symfony/issues/15797","labels_url":"https://api.github.com/repos/symfony/symfony/issues/15797/labels{/name}","comments_url":"https://api.github.com/repos/symfony/symfony/issues/15797/comments","events_url":"https://api.github.com/repos/symfony/symfony/issues/15797/events","html_url":"https://github.com/symfony/symfony/issues/15797","id":106428035,"number":15797,"title":"Issue in collections with Symfony 2.6 (maybe in the other versions too) & Doctrine 2.5","user":{"login":"BboyKeen","id":1192309,"avatar_url":"https://avatars.githubusercontent.com/u/1192309?v=3","gravatar_id":"","url":"https://api.github.com/users/BboyKeen","html_url":"https://github.com/BboyKeen","followers_url":"https://api.github.com/users/BboyKeen/followers","following_url":"https://api.github.com/users/BboyKeen/following{/other_user}","gists_url":"https://api.github.com/users/BboyKeen/gists{/gist_id}","starred_url":"https://api.github.com/users/BboyKeen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/BboyKeen/subscriptions","organizations_url":"https://api.github.com/users/BboyKeen/orgs","repos_url":"https://api.github.com/users/BboyKeen/repos","events_url":"https://api.github.com/users/BboyKeen/events{/privacy}","received_events_url":"https://api.github.com/users/BboyKeen/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/symfony/symfony/labels/Doctrine","name":"Doctrine","color":"DDDDDD"},{"url":"https://api.github.com/repos/symfony/symfony/labels/Form","name":"Form","color":"DDDDDD"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2015-09-14T21:05:49Z","updated_at":"2015-10-21T12:42:35Z","closed_at":null,"body":"Hey Symfony guys,\r\n\r\nAfter upgrading Doctrine from 2.4 to 2.5, I've been facing a new problem with my collections which were not working well.\r\nThe entity association is simple : OneToMany; in my case one User linked to many Skills.\r\nAt first sight, this message appeared \" 'spl_object_hash() expects parameter 1 to be object, null given' \" with no reason.\r\n\r\nHere is the stack trace :\r\n```\r\n in vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php at line 2445  -\r\n\r\n         */\r\n        public function cancelOrphanRemoval($entity)\r\n        {\r\n            unset($this->orphanRemovals[spl_object_hash($entity)]);\r\n        }\r\n        /**\r\n\r\nat ErrorHandler ->handleError ('2', 'spl_object_hash() expects parameter 1 to be object, null given', '/home/kevin/Dev/Yogosha/Web/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php', '2445', array('entity' => null))\r\nat spl_object_hash (null)\r\nin vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php at line 2445  +\r\nat UnitOfWork ->cancelOrphanRemoval (null)\r\nin vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php at line 475  +\r\nat PersistentCollection ->set ('9', null)\r\nin vendor/doctrine/orm/lib/Doctrine/ORM/PersistentCollection.php at line 522  +\r\nat PersistentCollection ->offsetSet ('9', null)\r\nin vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php at line 226  +\r\nat PropertyAccessor ->readPropertiesUntil (object(PersistentCollection), object(PropertyPath), '1', true)\r\nin vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php at line 58  +\r\nat PropertyAccessor ->getValue (object(PersistentCollection), object(PropertyPath))\r\nin vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php at line 57  +\r\nat PropertyPathMapper ->mapDataToForms (object(PersistentCollection), object(RecursiveIteratorIterator))\r\nin vendor/symfony/symfony/src/Symfony/Component/Form/Form.php at line 921  +\r\nat Form ->add ('9', object(RatedSkillType), array('property_path' => '[9]', 'block_name' => 'entry'))\r\nin vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php at line 128  +\r\nat ResizeFormListener ->preSubmit (object(FormEvent), 'form.pre_bind', object(EventDispatcher))\r\nat call_user_func (array(object(ResizeFormListener), 'preSubmit'), object(FormEvent), 'form.pre_bind', object(EventDispatcher))\r\nin app/cache/dev/classes.php at line 1791  +\r\nat EventDispatcher ->doDispatch (array(array(object(BindRequestListener), 'preBind'), array(object(TrimListener), 'preSubmit'), array(object(CsrfValidationListener), 'preSubmit'), array(object(ResizeFormListener), 'preSubmit')), 'form.pre_bind', object(FormEvent))\r\nin app/cache/dev/classes.php at line 1724  +\r\nat EventDispatcher ->dispatch ('form.pre_bind', object(FormEvent))\r\nin vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php at line 43  +\r\nat ImmutableEventDispatcher ->dispatch ('form.pre_bind', object(FormEvent))\r\nin vendor/symfony/symfony/src/Symfony/Component/Form/Form.php at line 551  +\r\n```\r\n\r\nAfter looking at the Doctrine and Symfony code, I think I've spotted the problem.\r\n\r\nIf my understandings are good, when working with collections, Symfony has to prepare the field of the data_class object to receive potential new elements. This preparation is done at PRE_BIND state by the method \"readPropertiesUntil\" of PropertyAccessor class (line 191) by initializing new column with \"null\" value. \r\n\r\n```php\r\n// Create missing nested arrays on demand\r\n            if ($isIndex &&\r\n                (\r\n                    ($objectOrArray instanceof \\ArrayAccess && !isset($objectOrArray[$property])) ||\r\n                    (is_array($objectOrArray) && !array_key_exists($property, $objectOrArray))\r\n                )\r\n            ) {\r\n                if (!$ignoreInvalidIndices) {\r\n                    if (!is_array($objectOrArray)) {\r\n                        if (!$objectOrArray instanceof \\Traversable) {\r\n                            throw new NoSuchIndexException(sprintf(\r\n                                'Cannot read index \"%s\" while trying to traverse path \"%s\".',\r\n                                $property,\r\n                                (string) $propertyPath\r\n                            ));\r\n                        }\r\n\r\n                        $objectOrArray = iterator_to_array($objectOrArray);\r\n                    }\r\n\r\n                    throw new NoSuchIndexException(sprintf(\r\n                        'Cannot read index \"%s\" while trying to traverse path \"%s\". Available indices are \"%s\".',\r\n                        $property,\r\n                        (string) $propertyPath,\r\n                        print_r(array_keys($objectOrArray), true)\r\n                    ));\r\n                }\r\n                $objectOrArray[$property] = $i + 1 < $propertyPath->getLength() ? array() : null;\r\n            }\r\n```\r\nAs we can see, the missing columns in the array are added thanks to this line : \r\n` $objectOrArray[$property] = $i + 1 < $propertyPath->getLength() ? array() : null;`\r\n\r\nAs Doctrine uses ArrayAccess implementation for the PersistentCollection, the use of `$objectOrArray[$property]` is equivalent to a call to the `offsetSet` method located at line 522 in the PersistentCollection class.\r\n\r\n``` php\r\npublic function offsetSet($offset, $value)\r\n    {\r\n        if ( ! isset($offset)) {\r\n            return $this->add($value);\r\n        }\r\n\r\n        return $this->set($offset, $value);\r\n    }\r\n```\r\n\r\nThen a call to the `set` method is issued :\r\n```php\r\npublic function set($key, $value)\r\n    {\r\n        parent::set($key, $value);\r\n\r\n        $this->changed();\r\n\r\n        if ($this->em && $value != null) {\r\n            $this->em->getUnitOfWork()->cancelOrphanRemoval($value);\r\n        }\r\n    }\r\n```\r\n\r\nAnd in my opinion, the problem comes here. \r\n\r\nIn Doctrine 2.4, the `set` method was :\r\n ```php\r\npublic function set($key, $value)\r\n    {\r\n        $this->initialize();\r\n\r\n        $this->coll->set($key, $value);\r\n\r\n        $this->changed();\r\n    }\r\n```\r\n\r\nBetween these two versions, we can see the introduction of this piece of code :\r\n\r\n```php\r\nif ($this->em) {\r\n            $this->em->getUnitOfWork()->cancelOrphanRemoval($value);\r\n        }\r\n```\r\n\r\nAnd `cancelOrphanRemoval` results in :\r\n```php\r\npublic function cancelOrphanRemoval($entity)\r\n    {\r\n        unset($this->orphanRemovals[spl_object_hash($entity)]);\r\n    }\r\n```\r\n\r\nSo, when the PropertyAccessor initializes the object to prepare it to receive the new value which will be inserted during the write phase, it issues `unset($this->orphanRemovals[spl_object_hash(null)])` indirectly through the cancelOrphanRemoval method. That's the reason why the exception is produced. This call was not issued in Doctrine 2.4 which explains why there was no problem.\r\n\r\nAs a quick workaround, I've used this \r\n```php\r\nif ($this->em && $value != null) {\r\n            $this->em->getUnitOfWork()->cancelOrphanRemoval($value);\r\n        }\r\n```\r\nbecause I don't know if it's the role of Doctrine to restrict null values or the role of Symfony to initialize the PersistentCollection in another way.\r\n\r\nSorry for this very long bugreport,\r\n\r\nKeen"},"comment":{"url":"https://api.github.com/repos/symfony/symfony/issues/comments/149882992","html_url":"https://github.com/symfony/symfony/issues/15797#issuecomment-149882992","issue_url":"https://api.github.com/repos/symfony/symfony/issues/15797","id":149882992,"user":{"login":"ZCJ","id":1475845,"avatar_url":"https://avatars.githubusercontent.com/u/1475845?v=3","gravatar_id":"","url":"https://api.github.com/users/ZCJ","html_url":"https://github.com/ZCJ","followers_url":"https://api.github.com/users/ZCJ/followers","following_url":"https://api.github.com/users/ZCJ/following{/other_user}","gists_url":"https://api.github.com/users/ZCJ/gists{/gist_id}","starred_url":"https://api.github.com/users/ZCJ/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ZCJ/subscriptions","organizations_url":"https://api.github.com/users/ZCJ/orgs","repos_url":"https://api.github.com/users/ZCJ/repos","events_url":"https://api.github.com/users/ZCJ/events{/privacy}","received_events_url":"https://api.github.com/users/ZCJ/received_events","type":"User","site_admin":false},"created_at":"2015-10-21T12:42:34Z","updated_at":"2015-10-21T12:42:34Z","body":"@BboyKeen @h3llr4iser - I am facing the same issue in the 'unscheduleOrphanRemoval' and applied a similar workaround. Is this issue resolved ?"}}