Migrations: Create missing tabs on content types when referenced by both composition and content type groups (closes #20058) (#20303)

* Add migration to create missing tabs

In v13, if a tab had groups in both a composition and the content type, the tab might not exist on the content type itself.
Newer versions require such tabs to also exist directly on the content type. This migration ensures those tabs are created.

Also fixes an issue in LeftJoin where nested sql arguments were being discarded.

* Small fixes

* WIP: Integration test.

* Added asserts to show the current issue with the integration test.

* Adjusted the integration test

* Added logging of result. Minor re-order and extraction refactoring in integration test.

---------

Co-authored-by: Andy Butland <abutland73@gmail.com>
This commit is contained in:
Laura Neto
2025-10-01 09:39:56 +02:00
committed by GitHub
parent 1082bf17b2
commit a84d67eff8
5 changed files with 401 additions and 2 deletions

View File

@@ -393,6 +393,26 @@ namespace Umbraco.Extensions
return sql.GroupBy(columns);
}
/// <summary>
/// Appends a GROUP BY clause to the Sql statement.
/// </summary>
/// <typeparam name="TDto">The type of the Dto.</typeparam>
/// <param name="sql">The Sql statement.</param>
/// <param name="tableAlias">A table alias.</param>
/// <param name="fields">Expression specifying the fields.</param>
/// <returns>The Sql statement.</returns>
public static Sql<ISqlContext> GroupBy<TDto>(
this Sql<ISqlContext> sql,
string tableAlias,
params Expression<Func<TDto, object?>>[] fields)
{
ISqlSyntaxProvider sqlSyntax = sql.SqlContext.SqlSyntax;
var columns = fields.Length == 0
? sql.GetColumns<TDto>(withAlias: false)
: fields.Select(x => sqlSyntax.GetFieldName(x, tableAlias)).ToArray();
return sql.GroupBy(columns);
}
/// <summary>
/// Appends more ORDER BY or GROUP BY fields to the Sql statement.
/// </summary>
@@ -548,7 +568,8 @@ namespace Umbraco.Extensions
join += " " + sql.SqlContext.SqlSyntax.GetQuotedTableName(alias);
}
return sql.LeftJoin(join);
sql.Append("LEFT JOIN " + join, nestedSelect.Arguments);
return new Sql<ISqlContext>.SqlJoinClause<ISqlContext>(sql);
}
/// <summary>
@@ -801,6 +822,29 @@ namespace Umbraco.Extensions
return sql;
}
/// <summary>
/// Creates a SELECT DISTINCT Sql statement.
/// </summary>
/// <typeparam name="TDto">The type of the DTO to select.</typeparam>
/// <param name="sql">The origin sql.</param>
/// <param name="tableAlias">A table alias.</param>
/// <param name="fields">Expressions indicating the columns to select.</param>
/// <returns>The Sql statement.</returns>
/// <remarks>
/// <para>If <paramref name="fields"/> is empty, all columns are selected.</para>
/// </remarks>
public static Sql<ISqlContext> SelectDistinct<TDto>(this Sql<ISqlContext> sql, string tableAlias, params Expression<Func<TDto, object?>>[] fields)
{
if (sql == null)
{
throw new ArgumentNullException(nameof(sql));
}
var columns = sql.GetColumns(tableAlias: tableAlias, columnExpressions: fields);
sql.Append("SELECT DISTINCT " + string.Join(", ", columns));
return sql;
}
public static Sql<ISqlContext> SelectDistinct(this Sql<ISqlContext> sql, params object[] columns)
{
sql.Append("SELECT DISTINCT " + string.Join(", ", columns));