33namespace ShipMonk \DoctrineEntityPreloader ;
44
55use ArrayAccess ;
6+ use Doctrine \DBAL \ArrayParameterType ;
7+ use Doctrine \DBAL \ParameterType ;
8+ use Doctrine \DBAL \Types \Type ;
69use Doctrine \ORM \EntityManagerInterface ;
710use Doctrine \ORM \Mapping \ClassMetadata ;
811use Doctrine \ORM \PersistentCollection ;
@@ -141,7 +144,7 @@ private function loadProxies(
141144 }
142145
143146 foreach (array_chunk ($ uninitializedIds , $ batchSize ) as $ idsChunk ) {
144- $ this ->loadEntitiesBy ($ classMetadata , $ identifierName , $ idsChunk , $ maxFetchJoinSameFieldCount );
147+ $ this ->loadEntitiesBy ($ classMetadata , $ identifierName , $ classMetadata , $ idsChunk , $ maxFetchJoinSameFieldCount );
145148 }
146149
147150 return array_values ($ uniqueEntities );
@@ -270,6 +273,7 @@ private function preloadOneToManyInner(
270273 $ targetEntitiesList = $ this ->loadEntitiesBy (
271274 $ targetClassMetadata ,
272275 $ targetPropertyName ,
276+ $ sourceClassMetadata ,
273277 $ uninitializedSourceEntityIdsChunk ,
274278 $ maxFetchJoinSameFieldCount ,
275279 $ associationMapping ['orderBy ' ] ?? [],
@@ -318,12 +322,18 @@ private function preloadManyToManyInner(
318322 $ sourceIdentifierName = $ sourceClassMetadata ->getSingleIdentifierFieldName ();
319323 $ targetIdentifierName = $ targetClassMetadata ->getSingleIdentifierFieldName ();
320324
325+ $ sourceIdentifierType = $ this ->getIdentifierFieldType ($ sourceClassMetadata );
326+
321327 $ manyToManyRows = $ this ->entityManager ->createQueryBuilder ()
322328 ->select ("source. {$ sourceIdentifierName } AS sourceId " , "target. {$ targetIdentifierName } AS targetId " )
323329 ->from ($ sourceClassMetadata ->getName (), 'source ' )
324330 ->join ("source. {$ sourcePropertyName }" , 'target ' )
325331 ->andWhere ('source IN (:sourceEntityIds) ' )
326- ->setParameter ('sourceEntityIds ' , $ uninitializedSourceEntityIdsChunk )
332+ ->setParameter (
333+ 'sourceEntityIds ' ,
334+ $ this ->convertFieldValuesToDatabaseValues ($ sourceIdentifierType , $ uninitializedSourceEntityIdsChunk ),
335+ $ this ->deduceArrayParameterType ($ sourceIdentifierType ),
336+ )
327337 ->getQuery ()
328338 ->getResult ();
329339
@@ -345,7 +355,7 @@ private function preloadManyToManyInner(
345355 $ uninitializedTargetEntityIds [$ targetEntityKey ] = $ targetEntityId ;
346356 }
347357
348- foreach ($ this ->loadEntitiesBy ($ targetClassMetadata , $ targetIdentifierName , array_values ($ uninitializedTargetEntityIds ), $ maxFetchJoinSameFieldCount ) as $ targetEntity ) {
358+ foreach ($ this ->loadEntitiesBy ($ targetClassMetadata , $ targetIdentifierName , $ sourceClassMetadata , array_values ($ uninitializedTargetEntityIds ), $ maxFetchJoinSameFieldCount ) as $ targetEntity ) {
349359 $ targetEntityKey = (string ) $ targetIdentifierReflection ->getValue ($ targetEntity );
350360 $ targetEntities [$ targetEntityKey ] = $ targetEntity ;
351361 }
@@ -404,15 +414,18 @@ private function preloadToOne(
404414 /**
405415 * @param ClassMetadata<T> $targetClassMetadata
406416 * @param list<mixed> $fieldValues
417+ * @param ClassMetadata<R> $referencedClassMetadata
407418 * @param non-negative-int $maxFetchJoinSameFieldCount
408419 * @param array<string, 'asc'|'desc'> $orderBy
409420 * @return list<T>
410421 *
411422 * @template T of E
423+ * @template R of E
412424 */
413425 private function loadEntitiesBy (
414426 ClassMetadata $ targetClassMetadata ,
415427 string $ fieldName ,
428+ ClassMetadata $ referencedClassMetadata ,
416429 array $ fieldValues ,
417430 int $ maxFetchJoinSameFieldCount ,
418431 array $ orderBy = [],
@@ -422,13 +435,18 @@ private function loadEntitiesBy(
422435 return [];
423436 }
424437
438+ $ referencedType = $ this ->getIdentifierFieldType ($ referencedClassMetadata );
425439 $ rootLevelAlias = 'e ' ;
426440
427441 $ queryBuilder = $ this ->entityManager ->createQueryBuilder ()
428442 ->select ($ rootLevelAlias )
429443 ->from ($ targetClassMetadata ->getName (), $ rootLevelAlias )
430444 ->andWhere ("{$ rootLevelAlias }. {$ fieldName } IN (:fieldValues) " )
431- ->setParameter ('fieldValues ' , $ fieldValues );
445+ ->setParameter (
446+ 'fieldValues ' ,
447+ $ this ->convertFieldValuesToDatabaseValues ($ referencedType , $ fieldValues ),
448+ $ this ->deduceArrayParameterType ($ referencedType ),
449+ );
432450
433451 $ this ->addFetchJoinsToPreventFetchDuringHydration ($ rootLevelAlias , $ queryBuilder , $ targetClassMetadata , $ maxFetchJoinSameFieldCount );
434452
@@ -439,6 +457,54 @@ private function loadEntitiesBy(
439457 return $ queryBuilder ->getQuery ()->getResult ();
440458 }
441459
460+ private function deduceArrayParameterType (Type $ dbalType ): ?ArrayParameterType
461+ {
462+ return match ($ dbalType ->getBindingType ()) {
463+ ParameterType::INTEGER => ArrayParameterType::INTEGER ,
464+ ParameterType::STRING => ArrayParameterType::STRING ,
465+ ParameterType::ASCII => ArrayParameterType::ASCII ,
466+ ParameterType::BINARY => ArrayParameterType::BINARY ,
467+ default => null , // @phpstan-ignore shipmonk.defaultMatchArmWithEnum
468+ };
469+ }
470+
471+ /**
472+ * @param array<mixed> $fieldValues
473+ * @return list<mixed>
474+ */
475+ private function convertFieldValuesToDatabaseValues (
476+ Type $ dbalType ,
477+ array $ fieldValues ,
478+ ): array
479+ {
480+ $ connection = $ this ->entityManager ->getConnection ();
481+ $ platform = $ connection ->getDatabasePlatform ();
482+
483+ $ convertedValues = [];
484+ foreach ($ fieldValues as $ value ) {
485+ $ convertedValues [] = $ dbalType ->convertToDatabaseValue ($ value , $ platform );
486+ }
487+
488+ return $ convertedValues ;
489+ }
490+
491+ /**
492+ * @param ClassMetadata<C> $classMetadata
493+ *
494+ * @template C of E
495+ */
496+ private function getIdentifierFieldType (ClassMetadata $ classMetadata ): Type
497+ {
498+ $ identifierName = $ classMetadata ->getSingleIdentifierFieldName ();
499+ $ sourceIdTypeName = $ classMetadata ->getTypeOfField ($ identifierName );
500+
501+ if ($ sourceIdTypeName === null ) {
502+ throw new LogicException ("Identifier field ' {$ identifierName }' for class ' {$ classMetadata ->getName ()}' has unknown field type. " );
503+ }
504+
505+ return Type::getType ($ sourceIdTypeName );
506+ }
507+
442508 /**
443509 * @param ClassMetadata<S> $sourceClassMetadata
444510 * @param array<string, array<string, int>> $alreadyPreloadedJoins
0 commit comments