[FIXED] bug xPDO::_getRelatedObjectsByFK() (getMany())

Нашел сегодня еще одну очень неприятную багу. На самом деле она очень серьезная, и в отдельных случаях может не только доставить неудобства, но и к серьезным ошибкам привести. Когда мы выполняем modx>getCollection(modx->getCollection(class), мы получаем массив объектов. Но это не простой нумерованный массив (типа array(0 => object, 1 => object) и т.д.). В этом массиве у нас каждый ключ — это id объекта, то есть ключи часто идут не по порядку (3,7,9,57 и т.д.). Те, кто использует ->getMany(), знают — этот метод возвращает связанные объекты. По сути этот метод внутри себя выполняет тот же самый getCollection(). Но проблема кроется всего в одной строчке: this>relatedObjects[this->_relatedObjects[alias]= array_merge(this>relatedObjects[this->_relatedObjects[alias], collection);Тоестьвсесвязанныеобъектыпопадаютвобщиймассивcollection); То есть все связанные объекты попадают в общий массив this->_relatedObjects. Уточняю: getMany() не просто делает выборку getCollection(), но и набивает эти объекты в общий пулл связанных объектов. И с этим массивом связанных объектов this>relatedObjectsможно(аиногдаинужно)работать.Таквот,проблемавтом,чтоarraymergeнесохраняетключиidобъектов,анабиваетвсевновыйнумерованныймассив.Фокусхорошодемонстрируетвотэтотпример:<?phpthis->_relatedObjects можно (а иногда и нужно) работать. Так вот, проблема в том, что array_merge не сохраняет ключи-id объектов, а набивает все в новый нумерованный массив. Фокус хорошо демонстрирует вот этот пример: <?php a = array( 2=>'object'
);

b=arraymerge(b = array_merge(a, array( 5=>'object', 7 => 'object', ));

print_r(b);Навыходемыполучаем:Array([0]=>object[1]=>object[2]=>object)Последствия(отнеудобствдопроблем)Неудобствозаключаетсявотвчем:Допустим,мызнаем,чтоужеполучаливсезависимыеобъекты,итеперьпростохотимпроверитьналичиеобъектапоегоid.Унашихобъектовестьidшники7,9и12.Поидеемыимеемвозможностьпоidобратитьсякключумассива,тоестьb); На выходе мы получаем: Array ( [0] => object [1] => object [2] => object ) Последствия (от неудобств до проблем) Неудобство заключается вот в чем: Допустим, мы знаем, что уже получали все зависимые объекты, и теперь просто хотим проверить наличие объекта по его id. У наших объектов есть id-шники 7,9 и 12. По идее мы имеем возможность по id обратиться к ключу массива, то есть object->_relatedObjects[alias][7]долженнамвернутьнужныйнамобъект.Аннет.Иззаэтойбагиунастамтеперьмассивсключами0,1,2Тоестьобъектмыужепотеряли.Ачтопроизойдет,еслимыопятьвыполнимalias][7] должен нам вернуть нужный нам объект. Ан нет. Из-за этой баги у нас там теперь массив с ключами 0,1,2… То есть объект мы уже потеряли. А что произойдет, если мы опять выполним object->getMany()? По сути 3 объекта он уже в себе имеет. Но из-за этой фишки с array_merge, мы получим уже 6 объектов, так как в новом массиве объектов ключи 7,9 и 12, а в текущем массиве 0,1,2, и теперь станет еще и 3,4,5. А если у нас id-шники 3453,6757,90980 и мы выполним getMany() много раз? :-) Еще пример. if(!room=room = modx->getObject('modResource', 107)){return '';}

// Получаем все связанные объекты vars=vars = room->getMany('Variables');

print count($room->_relatedObjects['Variables']); // Считаем - 2 объекта

// Получаем один из имеющихся объектов var=current(var = current(room->_relatedObjects['Variables']);

// Добавляем его же room>addMany(room->addMany(var);

print count(room>relatedObjects[Variables]);//Уже3объектаПосутиэтотожесамое,простодругимпутем.Носутьвчем:вотоднастрочкаизэтогометодаroom->_relatedObjects['Variables']); // Уже 3 объекта По сути это тоже самое, просто другим путем. Но суть в чем: вот одна строчка из этого метода this->_relatedObjects[alias][alias][objpk]= $obj; То есть затирается элемент массива объектов с ключем id этого объекта. Но по нашей схеме в зависимости от того, какой id у объекта, он во-первых, может затереть вообще другой объект (а тот мог быть измененным, а значит при сохранении главного объекта этот объект уже не будет сохранен), а во-вторых, этот новый объект может встать или впереди, или позади своей же копии, но эти две копии могут отличаться (новый объект перед добавлением изменили), так вот в зависимости от их очередности, при сохранении главного объекта, эти объекты будут сохранены, и не факт, что в правильной исторической последовательности. Вот как-то так…