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
This commit is contained in:
Mole
2024-03-18 10:46:03 +01:00
committed by GitHub
parent cb02c51da7
commit 1408298adf
12 changed files with 103 additions and 71 deletions

View File

@@ -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<TContentType, TContentTypeService>
where TContentTypeService : IContentTypeBaseService<TContentType>
where TContentType : IContentTypeComposition
{
private readonly TContentTypeService _contentTypeService;
private readonly IUmbracoMapper _umbracoMapper;
protected ContentPresentationFactoryBase(TContentTypeService contentTypeService, IUmbracoMapper umbracoMapper)
{
_contentTypeService = contentTypeService;
_umbracoMapper = umbracoMapper;
}
protected TContentTypeReferenceResponseModel CreateContentTypeReferenceResponseModel<TContentTypeReferenceResponseModel>(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<TContentTypeReferenceResponseModel>(contentType)!
: new TContentTypeReferenceResponseModel();
}
}

View File

@@ -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<IContentType, IContentTypeService>, 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<DocumentTypeReferenceResponseModel>(contentType)!;
}
responseModel.DocumentType = _umbracoMapper.Map<DocumentTypeReferenceResponseModel>(entity)!;
responseModel.Variants = CreateVariantsItemResponseModels(entity);
@@ -118,7 +111,7 @@ internal sealed class DocumentPresentationFactory
}
public DocumentTypeReferenceResponseModel CreateDocumentTypeReferenceResponseModel(IDocumentEntitySlim entity)
=> CreateContentTypeReferenceResponseModel<DocumentTypeReferenceResponseModel>(entity);
=> _umbracoMapper.Map<DocumentTypeReferenceResponseModel>(entity)!;
public Attempt<CultureAndScheduleModel, ContentPublishingOperationStatus> CreateCultureAndScheduleModel(PublishDocumentRequestModel requestModel)
{

View File

@@ -22,5 +22,6 @@ public interface IDocumentPresentationFactory
IEnumerable<DocumentVariantItemResponseModel> CreateVariantsItemResponseModels(IDocumentEntitySlim entity);
DocumentTypeReferenceResponseModel CreateDocumentTypeReferenceResponseModel(IDocumentEntitySlim entity);
Attempt<CultureAndScheduleModel,ContentPublishingOperationStatus> CreateCultureAndScheduleModel(PublishDocumentRequestModel requestModel);
}

View File

@@ -14,28 +14,23 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Management.Factories;
internal sealed class MediaPresentationFactory
: ContentPresentationFactoryBase<IMediaType, IMediaTypeService>, 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> 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<MediaResponseModel> 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<MediaTypeReferenceResponseModel>(mediaType)!;
}
responseModel.MediaType = _umbracoMapper.Map<MediaTypeReferenceResponseModel>(entity)!;
responseModel.Variants = CreateVariantsItemResponseModels(entity);
@@ -85,5 +76,5 @@ internal sealed class MediaPresentationFactory
};
public MediaTypeReferenceResponseModel CreateMediaTypeReferenceResponseModel(IMediaEntitySlim entity)
=> CreateContentTypeReferenceResponseModel<MediaTypeReferenceResponseModel>(entity);
=> _umbracoMapper.Map<MediaTypeReferenceResponseModel>(entity)!;
}

View File

@@ -9,24 +9,19 @@ using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Api.Management.Factories;
internal sealed class MemberPresentationFactory
: ContentPresentationFactoryBase<IMemberType, IMemberTypeService>, 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<MemberTypeReferenceResponseModel>(memberType)!;
}
responseModel.MemberType = _umbracoMapper.Map<MemberTypeReferenceResponseModel>(entity)!;
responseModel.Variants = CreateVariantsItemResponseModels(entity);
@@ -80,5 +71,5 @@ internal sealed class MemberPresentationFactory
};
public MemberTypeReferenceResponseModel CreateMemberTypeReferenceResponseModel(IMemberEntitySlim entity)
=> CreateContentTypeReferenceResponseModel<MemberTypeReferenceResponseModel>(entity);
=> _umbracoMapper.Map<MemberTypeReferenceResponseModel>(entity)!;
}

View File

@@ -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<IContentType,
mapper.Define<ISimpleContentType, DocumentTypeReferenceResponseModel>((_, _) => new DocumentTypeReferenceResponseModel(), Map);
mapper.Define<IContentType, AllowedDocumentType>((_, _) => new AllowedDocumentType(), Map);
mapper.Define<ISimpleContentType, DocumentTypeCollectionReferenceResponseModel>((_, _) => new DocumentTypeCollectionReferenceResponseModel(), Map);
mapper.Define<IContentEntitySlim, DocumentTypeReferenceResponseModel>((_, _) => new DocumentTypeReferenceResponseModel(), Map);
mapper.Define<IDocumentEntitySlim, DocumentTypeReferenceResponseModel>((_, _) => new DocumentTypeReferenceResponseModel(), Map);
}
// Umbraco.Code.MapAll
@@ -71,6 +74,22 @@ public class DocumentTypeMapDefinition : ContentTypeMapDefinition<IContentType,
target.Collection = ReferenceByIdModel.ReferenceOrNull(source.ListView);
}
// Umbraco.Code.MapAll
private void Map(IContentEntitySlim source, DocumentTypeReferenceResponseModel target, MapperContext context)
{
target.Id = source.Key;
target.Icon = source.ContentTypeIcon ?? string.Empty;
target.Collection = ReferenceByIdModel.ReferenceOrNull(source.ListViewKey);
}
// Umbraco.Code.MapAll
private void Map(IDocumentEntitySlim source, DocumentTypeReferenceResponseModel target, MapperContext context)
{
target.Id = source.Key;
target.Icon = source.ContentTypeIcon ?? string.Empty;
target.Collection = ReferenceByIdModel.ReferenceOrNull(source.ListViewKey);
}
// Umbraco.Code.MapAll
private void Map(ISimpleContentType source, DocumentTypeReferenceResponseModel target, MapperContext context)
{

View File

@@ -3,6 +3,7 @@ using Umbraco.Cms.Api.Management.ViewModels;
using Umbraco.Cms.Api.Management.ViewModels.MediaType;
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.MediaType;
@@ -13,6 +14,8 @@ public class MediaTypeMapDefinition : ContentTypeMapDefinition<IMediaType, Media
{
mapper.Define<IMediaType, MediaTypeResponseModel>((_, _) => new MediaTypeResponseModel(), Map);
mapper.Define<IMediaType, MediaTypeReferenceResponseModel>((_, _) => new MediaTypeReferenceResponseModel(), Map);
mapper.Define<IMediaEntitySlim, MediaTypeReferenceResponseModel>((_, _) => new MediaTypeReferenceResponseModel(), Map);
mapper.Define<IContentEntitySlim, MediaTypeReferenceResponseModel>((_, _) => new MediaTypeReferenceResponseModel(), Map);
mapper.Define<ISimpleContentType, MediaTypeReferenceResponseModel>((_, _) => new MediaTypeReferenceResponseModel(), Map);
mapper.Define<IMediaType, AllowedMediaType>((_, _) => new AllowedMediaType(), Map);
mapper.Define<ISimpleContentType, MediaTypeCollectionReferenceResponseModel>((_, _) => new MediaTypeCollectionReferenceResponseModel(), Map);
@@ -55,6 +58,22 @@ public class MediaTypeMapDefinition : ContentTypeMapDefinition<IMediaType, Media
target.Collection = ReferenceByIdModel.ReferenceOrNull(source.ListView);
}
// Umbraco.Code.MapAll
private void Map(IMediaEntitySlim source, MediaTypeReferenceResponseModel target, MapperContext context)
{
target.Id = source.Key;
target.Icon = source.ContentTypeIcon ?? string.Empty;
target.Collection = ReferenceByIdModel.ReferenceOrNull(source.ListViewKey);
}
// Umbraco.Code.MapAll
private void Map(IContentEntitySlim source, MediaTypeReferenceResponseModel target, MapperContext context)
{
target.Id = source.Key;
target.Icon = source.ContentTypeIcon ?? string.Empty;
target.Collection = ReferenceByIdModel.ReferenceOrNull(source.ListViewKey);
}
// Umbraco.Code.MapAll
private void Map(ISimpleContentType source, MediaTypeReferenceResponseModel target, MapperContext context)
{

View File

@@ -2,6 +2,7 @@
using Umbraco.Cms.Api.Management.ViewModels.MemberType;
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.MemberType;
@@ -12,6 +13,8 @@ public class MemberTypeMapDefinition : ContentTypeMapDefinition<IMemberType, Mem
{
mapper.Define<IMemberType, MemberTypeResponseModel>((_, _) => new MemberTypeResponseModel(), Map);
mapper.Define<IMemberType, MemberTypeReferenceResponseModel>((_, _) => new MemberTypeReferenceResponseModel(), Map);
mapper.Define<IMemberEntitySlim, MemberTypeReferenceResponseModel>((_, _) => new MemberTypeReferenceResponseModel(), Map);
mapper.Define<IContentEntitySlim, MemberTypeReferenceResponseModel>((_, _) => new MemberTypeReferenceResponseModel(), Map);
mapper.Define<ISimpleContentType, MemberTypeReferenceResponseModel>((_, _) => new MemberTypeReferenceResponseModel(), Map);
}
@@ -46,6 +49,18 @@ public class MemberTypeMapDefinition : ContentTypeMapDefinition<IMemberType, Mem
target.Icon = source.Icon ?? string.Empty;
}
private void Map(IMemberEntitySlim source, MemberTypeReferenceResponseModel target, MapperContext context)
{
target.Id = source.ContentTypeKey;
target.Icon = source.ContentTypeIcon ?? string.Empty;
}
private void Map(IContentEntitySlim source, MemberTypeReferenceResponseModel target, MapperContext context)
{
target.Id = source.ContentTypeKey;
target.Icon = source.ContentTypeIcon ?? string.Empty;
}
// Umbraco.Code.MapAll -Collection
private void Map(ISimpleContentType source, MemberTypeReferenceResponseModel target, MapperContext context)
{

View File

@@ -8,6 +8,11 @@ public class ContentEntitySlim : EntitySlim, IContentEntitySlim
/// <inheritdoc />
public string ContentTypeAlias { get; set; } = string.Empty;
/// <inheritdoc />
public Guid ContentTypeKey { get; set; }
public Guid? ListViewKey { get; set; }
/// <inheritdoc />
public string? ContentTypeIcon { get; set; }

View File

@@ -10,6 +10,16 @@ public interface IContentEntitySlim : IEntitySlim
/// </summary>
string ContentTypeAlias { get; }
/// <summary>
/// Gets the content type key.
/// </summary>
Guid ContentTypeKey { get; }
/// <summary>
/// Gets the listview key.
/// </summary>
Guid? ListViewKey { get; }
/// <summary>
/// Gets the content type icon.
/// </summary>

View File

@@ -373,6 +373,17 @@ namespace Umbraco.Extensions
? sql.GetColumns<TDto>(withAlias: false)
: fields.Select(x => sqlSyntax.GetFieldName(x)).ToArray();
return sql.Append(", " + string.Join(", ", columns));
}
public static Sql<ISqlContext> AndBy<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.Append(", " + string.Join(", ", columns));
}
/// <summary>

View File

@@ -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<NodeDto>("ContentTypeNode", x => Alias(x.UniqueId, "ContentTypeKey"));
}
if (isContent)
@@ -498,7 +499,9 @@ internal class EntityRepository : RepositoryBase, IEntityRepositoryExtended
.On<NodeDto, ContentVersionDto>((left, right) => left.NodeId == right.NodeId && right.Current)
.LeftJoin<ContentDto>().On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId)
.LeftJoin<ContentTypeDto>()
.On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId);
.On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId)
.LeftJoin<NodeDto>("ContentTypeNode")
.On<NodeDto, ContentTypeDto>((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<NodeDto>("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)