2626
2727use Doctrine \Common \Collections \Criteria ;
2828use Doctrine \ORM \Utility \PersisterHelper ;
29+ use function array_combine ;
30+ use function array_reverse ;
2931
3032/**
3133 * The joined subclass persister maps a single entity instance to several tables in the
@@ -271,35 +273,42 @@ public function update($entity)
271273 public function delete ($ entity )
272274 {
273275 $ identifier = $ this ->em ->getUnitOfWork ()->getEntityIdentifier ($ entity );
274- $ id = array_combine ($ this ->class ->getIdentifierColumnNames (), $ identifier );
275276
276277 $ this ->deleteJoinTableRecords ($ identifier );
277278
278- // If the database platform supports FKs, just
279- // delete the row from the root table. Cascades do the rest.
280- if ($ this ->platform ->supportsForeignKeyConstraints ()) {
281- $ rootClass = $ this ->em ->getClassMetadata ($ this ->class ->rootEntityName );
282- $ rootTable = $ this ->quoteStrategy ->getTableName ($ rootClass , $ this ->platform );
283- $ rootTypes = $ this ->getClassIdentifiersTypes ($ rootClass );
284-
285- return (bool ) $ this ->conn ->delete ($ rootTable , $ id , $ rootTypes );
286- }
287-
288- // Delete from all tables individually, starting from this class' table up to the root table.
289- $ rootTable = $ this ->quoteStrategy ->getTableName ($ this ->class , $ this ->platform );
290- $ rootTypes = $ this ->getClassIdentifiersTypes ($ this ->class );
291-
292- $ affectedRows = $ this ->conn ->delete ($ rootTable , $ id , $ rootTypes );
293-
294- foreach ($ this ->class ->parentClasses as $ parentClass ) {
279+ // Delete parent entries in reverse order (root first, until the direct parent of the current class).
280+ //
281+ // If foreign key are supported (and set as well as active), the first deletion will cascade and the
282+ // following queries won't delete anything, but it's better not to rely on a possible foreign key
283+ // functionality which cannot be checked here (foreign keys can be missing or disabled although
284+ // supported in general).
285+ //
286+ // On the other hand the supportsForeignKeyConstraints() method of the platform is also not reliable
287+ // when it returns false, because it only means that Doctrine\DBAL doesn't support foreign keys for
288+ // this platform, but they may be set in an existing database structure or added manually, so the
289+ // previous order of deletion (current class to root) would have failed - this could i.e. happen with
290+ // SQLite where foreign keys are theoretically possible but not supported by DBAL.
291+ $ deleted = false ;
292+ foreach (array_reverse ($ this ->class ->parentClasses ) as $ parentClass ) {
295293 $ parentMetadata = $ this ->em ->getClassMetadata ($ parentClass );
296294 $ parentTable = $ this ->quoteStrategy ->getTableName ($ parentMetadata , $ this ->platform );
297295 $ parentTypes = $ this ->getClassIdentifiersTypes ($ parentMetadata );
296+ $ parentId = array_combine ($ parentMetadata ->getIdentifierColumnNames (), $ identifier );
298297
299- $ this ->conn ->delete ($ parentTable , $ id , $ parentTypes );
298+ $ affectedRows = $ this ->conn ->delete ($ parentTable , $ parentId , $ parentTypes );
299+ $ deleted = $ deleted ?: ($ affectedRows > 0 );
300300 }
301301
302- return (bool ) $ affectedRows ;
302+ // Now delete the entries from the current class (if not deleted automatically
303+ // because of a foreign key).
304+ $ currentTable = $ this ->quoteStrategy ->getTableName ($ this ->class , $ this ->platform );
305+ $ currentTypes = $ this ->getClassIdentifiersTypes ($ this ->class );
306+ $ currentId = array_combine ($ this ->class ->getIdentifierColumnNames (), $ identifier );
307+
308+ $ affectedRows = $ this ->conn ->delete ($ currentTable , $ currentId , $ currentTypes );
309+ $ deleted = $ deleted ?: ($ affectedRows > 0 );
310+
311+ return $ deleted ;
303312 }
304313
305314 /**
@@ -412,8 +421,11 @@ protected function getLockTablesSql($lockMode)
412421 $ parentClass = $ this ->em ->getClassMetadata ($ parentClassName );
413422 $ joinSql .= ' INNER JOIN ' . $ this ->quoteStrategy ->getTableName ($ parentClass , $ this ->platform ) . ' ' . $ tableAlias . ' ON ' ;
414423
415- foreach ($ identifierColumns as $ idColumn ) {
416- $ conditions [] = $ baseTableAlias . '. ' . $ idColumn . ' = ' . $ tableAlias . '. ' . $ idColumn ;
424+ // Fix for bug GH-8229 (id column from parent class renamed in child class):
425+ // Use the correct name for the id column as named in the parent class.
426+ $ parentIdentifierColumns = $ parentClass ->getIdentifierColumnNames ();
427+ foreach ($ identifierColumns as $ index => $ idColumn ) {
428+ $ conditions [] = $ baseTableAlias . '. ' . $ idColumn . ' = ' . $ tableAlias . '. ' . $ parentIdentifierColumns [$ index ];
417429 }
418430
419431 $ joinSql .= implode (' AND ' , $ conditions );
@@ -589,9 +601,11 @@ private function getJoinSql($baseTableAlias)
589601 $ tableAlias = $ this ->getSQLTableAlias ($ parentClassName );
590602 $ joinSql .= ' INNER JOIN ' . $ this ->quoteStrategy ->getTableName ($ parentClass , $ this ->platform ) . ' ' . $ tableAlias . ' ON ' ;
591603
592-
593- foreach ($ identifierColumn as $ idColumn ) {
594- $ conditions [] = $ baseTableAlias . '. ' . $ idColumn . ' = ' . $ tableAlias . '. ' . $ idColumn ;
604+ // Fix for bug GH-8229 (id column from parent class renamed in child class):
605+ // Use the correct name for the id column as named in the parent class.
606+ $ parentIdentifierColumn = $ parentClass ->getIdentifierColumnNames ();
607+ foreach ($ identifierColumn as $ index => $ idColumn ) {
608+ $ conditions [] = $ baseTableAlias . '. ' . $ idColumn . ' = ' . $ tableAlias . '. ' . $ parentIdentifierColumn [$ index ];
595609 }
596610
597611 $ joinSql .= implode (' AND ' , $ conditions );
@@ -604,8 +618,11 @@ private function getJoinSql($baseTableAlias)
604618 $ tableAlias = $ this ->getSQLTableAlias ($ subClassName );
605619 $ joinSql .= ' LEFT JOIN ' . $ this ->quoteStrategy ->getTableName ($ subClass , $ this ->platform ) . ' ' . $ tableAlias . ' ON ' ;
606620
607- foreach ($ identifierColumn as $ idColumn ) {
608- $ conditions [] = $ baseTableAlias . '. ' . $ idColumn . ' = ' . $ tableAlias . '. ' . $ idColumn ;
621+ // Fix for bug GH-8229 (id column from parent class renamed in child class):
622+ // Use the correct name for the id column as named in the parent class.
623+ $ subClassIdentifierColumn = $ subClass ->getIdentifierColumnNames ();
624+ foreach ($ identifierColumn as $ index => $ idColumn ) {
625+ $ conditions [] = $ baseTableAlias . '. ' . $ idColumn . ' = ' . $ tableAlias . '. ' . $ subClassIdentifierColumn [$ index ];
609626 }
610627
611628 $ joinSql .= implode (' AND ' , $ conditions );
0 commit comments