From 1408298adfdafc873acc1d592f94dbda7ec2c835 Mon Sep 17 00:00:00 2001 From: Mole Date: Mon, 18 Mar 2024 10:46:03 +0100 Subject: [PATCH] V14: Extend IContentEntitySlim (#15890) * Extend EntitySlim with key * Add ListView to GenericContentEntityDto and ContentEntitySlim * Move ContentTypeKey and ListViewKey back to BaseDto * Remove extra DB call when mapping to DocumentTypeReferenceResponseModel * Remove extra DB call when mapping to MediaTypeReferenceResponseModel * Remove duplicate db call for members * Remove now redundant base class * Fix comment --- .../ContentPresentationFactoryBase.cs | 33 ------------------- .../Factories/DocumentPresentationFactory.cs | 13 ++------ .../Factories/IDocumentPresentationFactory.cs | 1 + .../Factories/MediaPresentationFactory.cs | 17 +++------- .../Factories/MemberPresentationFactory.cs | 15 ++------- .../DocumentType/DocumentTypeMapDefinition.cs | 19 +++++++++++ .../MediaType/MediaTypeMapDefinition.cs | 19 +++++++++++ .../MemberType/MemberTypeMapDefinition.cs | 15 +++++++++ .../Models/Entities/ContentEntitySlim.cs | 5 +++ .../Models/Entities/IContentEntitySlim.cs | 10 ++++++ .../Persistence/NPocoSqlExtensions.cs | 11 +++++++ .../Implement/EntityRepository.cs | 16 +++++++-- 12 files changed, 103 insertions(+), 71 deletions(-) delete mode 100644 src/Umbraco.Cms.Api.Management/Factories/ContentPresentationFactoryBase.cs diff --git a/src/Umbraco.Cms.Api.Management/Factories/ContentPresentationFactoryBase.cs b/src/Umbraco.Cms.Api.Management/Factories/ContentPresentationFactoryBase.cs deleted file mode 100644 index ba4885f2a3..0000000000 --- a/src/Umbraco.Cms.Api.Management/Factories/ContentPresentationFactoryBase.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Umbraco.Cms.Api.Management.ViewModels.ContentType; -using Umbraco.Cms.Core.Mapping; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.Entities; -using Umbraco.Cms.Core.Services; - -namespace Umbraco.Cms.Api.Management.Factories; - -internal abstract class ContentPresentationFactoryBase - where TContentTypeService : IContentTypeBaseService - where TContentType : IContentTypeComposition -{ - private readonly TContentTypeService _contentTypeService; - private readonly IUmbracoMapper _umbracoMapper; - - protected ContentPresentationFactoryBase(TContentTypeService contentTypeService, IUmbracoMapper umbracoMapper) - { - _contentTypeService = contentTypeService; - _umbracoMapper = umbracoMapper; - } - - protected TContentTypeReferenceResponseModel CreateContentTypeReferenceResponseModel(IContentEntitySlim entity) - where TContentTypeReferenceResponseModel : ContentTypeReferenceResponseModelBase, new() - { - // This sucks, since it'll cost an extra DB call. - // but currently there's no really good way to get the content type key from an IDocumentEntitySlim or IMediaEntitySlim. - // FIXME: to fix this, add content type key and "IsContainer" to IDocumentEntitySlim and IMediaEntitySlim, and use those here instead of fetching the entire content type. - TContentType? contentType = _contentTypeService.Get(entity.ContentTypeAlias); - return contentType is not null - ? _umbracoMapper.Map(contentType)! - : new TContentTypeReferenceResponseModel(); - } -} diff --git a/src/Umbraco.Cms.Api.Management/Factories/DocumentPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/DocumentPresentationFactory.cs index f6d8b7026e..1532a9227f 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/DocumentPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/DocumentPresentationFactory.cs @@ -1,6 +1,5 @@ using Umbraco.Cms.Api.Management.Mapping.Content; using Umbraco.Cms.Api.Management.ViewModels; -using Umbraco.Cms.Api.Management.ViewModels.Content; using Umbraco.Cms.Api.Management.ViewModels.Document; using Umbraco.Cms.Api.Management.ViewModels.Document.Item; using Umbraco.Cms.Api.Management.ViewModels.DocumentBlueprint.Item; @@ -16,8 +15,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Api.Management.Factories; -internal sealed class DocumentPresentationFactory - : ContentPresentationFactoryBase, IDocumentPresentationFactory +internal sealed class DocumentPresentationFactory : IDocumentPresentationFactory { private readonly IUmbracoMapper _umbracoMapper; private readonly IDocumentUrlFactory _documentUrlFactory; @@ -33,7 +31,6 @@ internal sealed class DocumentPresentationFactory IContentTypeService contentTypeService, IPublicAccessService publicAccessService, TimeProvider timeProvider) - : base(contentTypeService, umbracoMapper) { _umbracoMapper = umbracoMapper; _documentUrlFactory = documentUrlFactory; @@ -70,11 +67,7 @@ internal sealed class DocumentPresentationFactory responseModel.IsProtected = _publicAccessService.IsProtected(entity.Path); - IContentType? contentType = _contentTypeService.Get(entity.ContentTypeAlias); - if (contentType is not null) - { - responseModel.DocumentType = _umbracoMapper.Map(contentType)!; - } + responseModel.DocumentType = _umbracoMapper.Map(entity)!; responseModel.Variants = CreateVariantsItemResponseModels(entity); @@ -118,7 +111,7 @@ internal sealed class DocumentPresentationFactory } public DocumentTypeReferenceResponseModel CreateDocumentTypeReferenceResponseModel(IDocumentEntitySlim entity) - => CreateContentTypeReferenceResponseModel(entity); + => _umbracoMapper.Map(entity)!; public Attempt CreateCultureAndScheduleModel(PublishDocumentRequestModel requestModel) { diff --git a/src/Umbraco.Cms.Api.Management/Factories/IDocumentPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/IDocumentPresentationFactory.cs index 240f3a0da3..883a004ed8 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/IDocumentPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/IDocumentPresentationFactory.cs @@ -22,5 +22,6 @@ public interface IDocumentPresentationFactory IEnumerable CreateVariantsItemResponseModels(IDocumentEntitySlim entity); DocumentTypeReferenceResponseModel CreateDocumentTypeReferenceResponseModel(IDocumentEntitySlim entity); + Attempt CreateCultureAndScheduleModel(PublishDocumentRequestModel requestModel); } diff --git a/src/Umbraco.Cms.Api.Management/Factories/MediaPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/MediaPresentationFactory.cs index a195537942..e6155a19a7 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/MediaPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/MediaPresentationFactory.cs @@ -14,28 +14,23 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Api.Management.Factories; -internal sealed class MediaPresentationFactory - : ContentPresentationFactoryBase, IMediaPresentationFactory +internal sealed class MediaPresentationFactory : IMediaPresentationFactory { private readonly IUmbracoMapper _umbracoMapper; private readonly ContentSettings _contentSettings; private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly IAbsoluteUrlBuilder _absoluteUrlBuilder; - private readonly IMediaTypeService _mediaTypeService; public MediaPresentationFactory( IUmbracoMapper umbracoMapper, IOptions contentSettings, MediaUrlGeneratorCollection mediaUrlGenerators, - IAbsoluteUrlBuilder absoluteUrlBuilder, - IMediaTypeService mediaTypeService) - : base(mediaTypeService, umbracoMapper) + IAbsoluteUrlBuilder absoluteUrlBuilder) { _umbracoMapper = umbracoMapper; _contentSettings = contentSettings.Value; _mediaUrlGenerators = mediaUrlGenerators; _absoluteUrlBuilder = absoluteUrlBuilder; - _mediaTypeService = mediaTypeService; } public Task CreateResponseModelAsync(IMedia media) @@ -63,11 +58,7 @@ internal sealed class MediaPresentationFactory IsTrashed = entity.Trashed }; - IMediaType? mediaType = _mediaTypeService.Get(entity.ContentTypeAlias); - if (mediaType is not null) - { - responseModel.MediaType = _umbracoMapper.Map(mediaType)!; - } + responseModel.MediaType = _umbracoMapper.Map(entity)!; responseModel.Variants = CreateVariantsItemResponseModels(entity); @@ -85,5 +76,5 @@ internal sealed class MediaPresentationFactory }; public MediaTypeReferenceResponseModel CreateMediaTypeReferenceResponseModel(IMediaEntitySlim entity) - => CreateContentTypeReferenceResponseModel(entity); + => _umbracoMapper.Map(entity)!; } diff --git a/src/Umbraco.Cms.Api.Management/Factories/MemberPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/MemberPresentationFactory.cs index e0d83f333b..d0905e4e1a 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/MemberPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/MemberPresentationFactory.cs @@ -9,24 +9,19 @@ using Umbraco.Cms.Core.Services; namespace Umbraco.Cms.Api.Management.Factories; -internal sealed class MemberPresentationFactory - : ContentPresentationFactoryBase, IMemberPresentationFactory +internal sealed class MemberPresentationFactory : IMemberPresentationFactory { private readonly IUmbracoMapper _umbracoMapper; private readonly IMemberService _memberService; - private readonly IMemberTypeService _memberTypeService; private readonly ITwoFactorLoginService _twoFactorLoginService; public MemberPresentationFactory( IUmbracoMapper umbracoMapper, IMemberService memberService, - IMemberTypeService memberTypeService, ITwoFactorLoginService twoFactorLoginService) - : base(memberTypeService, umbracoMapper) { _umbracoMapper = umbracoMapper; _memberService = memberService; - _memberTypeService = memberTypeService; _twoFactorLoginService = twoFactorLoginService; } @@ -58,11 +53,7 @@ internal sealed class MemberPresentationFactory Id = entity.Key, }; - IMemberType? memberType = _memberTypeService.Get(entity.ContentTypeAlias); - if (memberType is not null) - { - responseModel.MemberType = _umbracoMapper.Map(memberType)!; - } + responseModel.MemberType = _umbracoMapper.Map(entity)!; responseModel.Variants = CreateVariantsItemResponseModels(entity); @@ -80,5 +71,5 @@ internal sealed class MemberPresentationFactory }; public MemberTypeReferenceResponseModel CreateMemberTypeReferenceResponseModel(IMemberEntitySlim entity) - => CreateContentTypeReferenceResponseModel(entity); + => _umbracoMapper.Map(entity)!; } diff --git a/src/Umbraco.Cms.Api.Management/Mapping/DocumentType/DocumentTypeMapDefinition.cs b/src/Umbraco.Cms.Api.Management/Mapping/DocumentType/DocumentTypeMapDefinition.cs index aa3f483819..e7836497b6 100644 --- a/src/Umbraco.Cms.Api.Management/Mapping/DocumentType/DocumentTypeMapDefinition.cs +++ b/src/Umbraco.Cms.Api.Management/Mapping/DocumentType/DocumentTypeMapDefinition.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Api.Management.ViewModels; using Umbraco.Cms.Api.Management.ViewModels.DocumentType; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Entities; using Umbraco.Extensions; namespace Umbraco.Cms.Api.Management.Mapping.DocumentType; @@ -16,6 +17,8 @@ public class DocumentTypeMapDefinition : ContentTypeMapDefinition((_, _) => new DocumentTypeReferenceResponseModel(), Map); mapper.Define((_, _) => new AllowedDocumentType(), Map); mapper.Define((_, _) => new DocumentTypeCollectionReferenceResponseModel(), Map); + mapper.Define((_, _) => new DocumentTypeReferenceResponseModel(), Map); + mapper.Define((_, _) => new DocumentTypeReferenceResponseModel(), Map); } // Umbraco.Code.MapAll @@ -71,6 +74,22 @@ public class DocumentTypeMapDefinition : ContentTypeMapDefinition((_, _) => new MediaTypeResponseModel(), Map); mapper.Define((_, _) => new MediaTypeReferenceResponseModel(), Map); + mapper.Define((_, _) => new MediaTypeReferenceResponseModel(), Map); + mapper.Define((_, _) => new MediaTypeReferenceResponseModel(), Map); mapper.Define((_, _) => new MediaTypeReferenceResponseModel(), Map); mapper.Define((_, _) => new AllowedMediaType(), Map); mapper.Define((_, _) => new MediaTypeCollectionReferenceResponseModel(), Map); @@ -55,6 +58,22 @@ public class MediaTypeMapDefinition : ContentTypeMapDefinition((_, _) => new MemberTypeResponseModel(), Map); mapper.Define((_, _) => new MemberTypeReferenceResponseModel(), Map); + mapper.Define((_, _) => new MemberTypeReferenceResponseModel(), Map); + mapper.Define((_, _) => new MemberTypeReferenceResponseModel(), Map); mapper.Define((_, _) => new MemberTypeReferenceResponseModel(), Map); } @@ -46,6 +49,18 @@ public class MemberTypeMapDefinition : ContentTypeMapDefinition public string ContentTypeAlias { get; set; } = string.Empty; + /// + public Guid ContentTypeKey { get; set; } + + public Guid? ListViewKey { get; set; } + /// public string? ContentTypeIcon { get; set; } diff --git a/src/Umbraco.Core/Models/Entities/IContentEntitySlim.cs b/src/Umbraco.Core/Models/Entities/IContentEntitySlim.cs index 78ddf9bd82..a030d0c3a8 100644 --- a/src/Umbraco.Core/Models/Entities/IContentEntitySlim.cs +++ b/src/Umbraco.Core/Models/Entities/IContentEntitySlim.cs @@ -10,6 +10,16 @@ public interface IContentEntitySlim : IEntitySlim /// string ContentTypeAlias { get; } + /// + /// Gets the content type key. + /// + Guid ContentTypeKey { get; } + + /// + /// Gets the listview key. + /// + Guid? ListViewKey { get; } + /// /// Gets the content type icon. /// diff --git a/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs index 1eefc14097..645557cfe0 100644 --- a/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs +++ b/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs @@ -373,6 +373,17 @@ namespace Umbraco.Extensions ? sql.GetColumns(withAlias: false) : fields.Select(x => sqlSyntax.GetFieldName(x)).ToArray(); return sql.Append(", " + string.Join(", ", columns)); + + } + + public static Sql AndBy(this Sql sql, string tableAlias, + params Expression>[] fields) + { + ISqlSyntaxProvider sqlSyntax = sql.SqlContext.SqlSyntax; + var columns = fields.Length == 0 + ? sql.GetColumns(withAlias: false) + : fields.Select(x => sqlSyntax.GetFieldName(x, tableAlias)).ToArray(); + return sql.Append(", " + string.Join(", ", columns)); } /// diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs index 36a5dd9063..96b455d208 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs @@ -472,7 +472,8 @@ internal class EntityRepository : RepositoryBase, IEntityRepositoryExtended x => x.Icon, x => x.Thumbnail, x => x.ListView, - x => x.Variations); + x => x.Variations) + .AndSelect("ContentTypeNode", x => Alias(x.UniqueId, "ContentTypeKey")); } if (isContent) @@ -498,7 +499,9 @@ internal class EntityRepository : RepositoryBase, IEntityRepositoryExtended .On((left, right) => left.NodeId == right.NodeId && right.Current) .LeftJoin().On((left, right) => left.NodeId == right.NodeId) .LeftJoin() - .On((left, right) => left.ContentTypeId == right.NodeId); + .On((left, right) => left.ContentTypeId == right.NodeId) + .LeftJoin("ContentTypeNode") + .On((left, right) => left.NodeId == right.NodeId, aliasLeft: "ContentTypeNode"); } if (isContent) @@ -605,7 +608,8 @@ internal class EntityRepository : RepositoryBase, IEntityRepositoryExtended x => x.Icon, x => x.Thumbnail, x => x.ListView, - x => x.Variations); + x => x.Variations) + .AndBy("ContentTypeNode", x => x.UniqueId); } if (defaultSort) @@ -730,6 +734,10 @@ internal class EntityRepository : RepositoryBase, IEntityRepositoryExtended public string? Thumbnail { get; set; } public bool IsContainer { get; set; } + public Guid ContentTypeKey { get; set; } + + public Guid? ListView { get; set; } + // ReSharper restore UnusedAutoPropertyAccessor.Local // ReSharper restore UnusedMember.Local } @@ -786,6 +794,8 @@ internal class EntityRepository : RepositoryBase, IEntityRepositoryExtended entity.ContentTypeAlias = dto.Alias; entity.ContentTypeIcon = dto.Icon; entity.ContentTypeThumbnail = dto.Thumbnail; + entity.ContentTypeKey = dto.ContentTypeKey; + entity.ListViewKey = dto.ListView; } private MediaEntitySlim BuildMediaEntity(BaseDto dto)