diff --git a/src/Umbraco.Core/Compose/RelateOnCopyComponent.cs b/src/Umbraco.Core/Compose/RelateOnCopyComponent.cs index 63a7e170da..b56ff8b87e 100644 --- a/src/Umbraco.Core/Compose/RelateOnCopyComponent.cs +++ b/src/Umbraco.Core/Compose/RelateOnCopyComponent.cs @@ -26,10 +26,11 @@ namespace Umbraco.Core.Compose if (relationType == null) { - relationType = new RelationType(Constants.ObjectTypes.Document, + relationType = new RelationType(Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, + Constants.Conventions.RelationTypes.RelateDocumentOnCopyName, + true, Constants.ObjectTypes.Document, - Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, - Constants.Conventions.RelationTypes.RelateDocumentOnCopyName) { IsBidirectional = true }; + Constants.ObjectTypes.Document); relationService.Save(relationType); } diff --git a/src/Umbraco.Core/Compose/RelateOnTrashComponent.cs b/src/Umbraco.Core/Compose/RelateOnTrashComponent.cs index 8371f9b279..4e01c50fc6 100644 --- a/src/Umbraco.Core/Compose/RelateOnTrashComponent.cs +++ b/src/Umbraco.Core/Compose/RelateOnTrashComponent.cs @@ -63,7 +63,7 @@ namespace Umbraco.Core.Compose var documentObjectType = Constants.ObjectTypes.Document; const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName; - relationType = new RelationType(documentObjectType, documentObjectType, relationTypeAlias, relationTypeName); + relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType); relationService.Save(relationType); } @@ -106,7 +106,7 @@ namespace Umbraco.Core.Compose { var documentObjectType = Constants.ObjectTypes.Document; const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName; - relationType = new RelationType(documentObjectType, documentObjectType, relationTypeAlias, relationTypeName); + relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType); relationService.Save(relationType); } foreach (var item in e.MoveInfoCollection) diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs index 1027840995..78e22ef28e 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs @@ -325,7 +325,7 @@ namespace Umbraco.Core.Migrations.Install relationType.UniqueId = (relationType.Alias + "____" + relationType.Name).ToGuid(); _database.Insert(Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType); - relationType = new RelationTypeDto { Id = 4, Alias = Constants.Conventions.RelationTypes.RelatedDocumentAlias, ChildObjectType = Constants.ObjectTypes.Document, ParentObjectType = Constants.ObjectTypes.Document, Dual = false, Name = Constants.Conventions.RelationTypes.RelatedDocumentName }; + relationType = new RelationTypeDto { Id = 5, Alias = Constants.Conventions.RelationTypes.RelatedDocumentAlias, ChildObjectType = Constants.ObjectTypes.Document, ParentObjectType = Constants.ObjectTypes.Document, Dual = false, Name = Constants.Conventions.RelationTypes.RelatedDocumentName }; relationType.UniqueId = (relationType.Alias + "____" + relationType.Name).ToGuid(); _database.Insert(Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType); } diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index e8fd3414ec..e5e335d309 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -6,6 +6,7 @@ using Umbraco.Core.Migrations.Upgrade.Common; using Umbraco.Core.Migrations.Upgrade.V_8_0_0; using Umbraco.Core.Migrations.Upgrade.V_8_0_1; using Umbraco.Core.Migrations.Upgrade.V_8_1_0; +using Umbraco.Core.Migrations.Upgrade.V_8_5_0; namespace Umbraco.Core.Migrations.Upgrade { @@ -182,6 +183,9 @@ namespace Umbraco.Core.Migrations.Upgrade To("{0372A42B-DECF-498D-B4D1-6379E907EB94}"); To("{5B1E0D93-F5A3-449B-84BA-65366B84E2D4}"); + // to 8.5.0... + To("{4759A294-9860-46BC-99F9-B4C975CAE580}"); + //FINAL } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_5_0/UpdateRelationTypeTable.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_5_0/UpdateRelationTypeTable.cs new file mode 100644 index 0000000000..ed21935488 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_5_0/UpdateRelationTypeTable.cs @@ -0,0 +1,21 @@ +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_5_0 +{ + public class UpdateRelationTypeTable : MigrationBase + { + public UpdateRelationTypeTable(IMigrationContext context) + : base(context) + { } + + public override void Migrate() + { + + Alter.Table(Constants.DatabaseSchema.Tables.RelationType).AlterColumn("parentObjectType").AsGuid().Nullable(); + Alter.Table(Constants.DatabaseSchema.Tables.RelationType).AlterColumn("childObjectType").AsGuid().Nullable(); + + //TODO: We have to update this field to ensure it's not null, we can just copy across the name since that is not nullable + Alter.Table(Constants.DatabaseSchema.Tables.RelationType).AlterColumn("alias").AsString(100).NotNullable(); + } + } +} diff --git a/src/Umbraco.Core/Models/IRelation.cs b/src/Umbraco.Core/Models/IRelation.cs index 745216fba1..6bd348d72f 100644 --- a/src/Umbraco.Core/Models/IRelation.cs +++ b/src/Umbraco.Core/Models/IRelation.cs @@ -1,4 +1,5 @@ -using System.Runtime.Serialization; +using System; +using System.Runtime.Serialization; using Umbraco.Core.Models.Entities; namespace Umbraco.Core.Models @@ -11,12 +12,18 @@ namespace Umbraco.Core.Models [DataMember] int ParentId { get; set; } + [DataMember] + Guid ParentObjectType { get; set; } + /// /// Gets or sets the Child Id of the Relation (Destination) /// [DataMember] int ChildId { get; set; } + [DataMember] + Guid ChildObjectType { get; set; } + /// /// Gets or sets the for the Relation /// diff --git a/src/Umbraco.Core/Models/IRelationType.cs b/src/Umbraco.Core/Models/IRelationType.cs index 8bbe657427..9253fae8ab 100644 --- a/src/Umbraco.Core/Models/IRelationType.cs +++ b/src/Umbraco.Core/Models/IRelationType.cs @@ -29,13 +29,13 @@ namespace Umbraco.Core.Models /// /// Corresponds to the NodeObjectType in the umbracoNode table [DataMember] - Guid ParentObjectType { get; set; } + Guid? ParentObjectType { get; set; } /// /// Gets or sets the Childs object type id /// /// Corresponds to the NodeObjectType in the umbracoNode table [DataMember] - Guid ChildObjectType { get; set; } + Guid? ChildObjectType { get; set; } } } diff --git a/src/Umbraco.Core/Models/Relation.cs b/src/Umbraco.Core/Models/Relation.cs index f5d13c70c4..8e3a073a96 100644 --- a/src/Umbraco.Core/Models/Relation.cs +++ b/src/Umbraco.Core/Models/Relation.cs @@ -17,12 +17,37 @@ namespace Umbraco.Core.Models private IRelationType _relationType; private string _comment; + /// + /// Constructor for constructing the entity to be created + /// + /// + /// + /// + /// + /// public Relation(int parentId, int childId, IRelationType relationType) { _parentId = parentId; _childId = childId; _relationType = relationType; } + + /// + /// Constructor for reconstructing the entity from the data source + /// + /// + /// + /// + /// + /// + public Relation(int parentId, int childId, Guid parentObjectType, Guid childObjectType, IRelationType relationType) + { + _parentId = parentId; + _childId = childId; + _relationType = relationType; + ParentObjectType = parentObjectType; + ChildObjectType = childObjectType; + } /// @@ -35,6 +60,9 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _parentId, nameof(ParentId)); } + [DataMember] + public Guid ParentObjectType { get; set; } + /// /// Gets or sets the Child Id of the Relation (Destination) /// @@ -45,6 +73,9 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _childId, nameof(ChildId)); } + [DataMember] + public Guid ChildObjectType { get; set; } + /// /// Gets or sets the for the Relation /// diff --git a/src/Umbraco.Core/Models/RelationType.cs b/src/Umbraco.Core/Models/RelationType.cs index 259b7bc4ef..ea62cecab7 100644 --- a/src/Umbraco.Core/Models/RelationType.cs +++ b/src/Umbraco.Core/Models/RelationType.cs @@ -15,24 +15,26 @@ namespace Umbraco.Core.Models private string _name; private string _alias; private bool _isBidrectional; - private Guid _parentObjectType; - private Guid _childObjectType; + private Guid? _parentObjectType; + private Guid? _childObjectType; - public RelationType(Guid childObjectType, Guid parentObjectType, string alias) + //TODO: Should we put back the broken ctors with obsolete attributes? + + public RelationType(string alias, string name) + : this(name, alias, false, null, null) { - if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentNullOrEmptyException(nameof(alias)); - _childObjectType = childObjectType; - _parentObjectType = parentObjectType; + } + + public RelationType(string name, string alias, bool isBidrectional, Guid? parentObjectType, Guid? childObjectType) + { + _name = name; _alias = alias; - Name = _alias; + _isBidrectional = isBidrectional; + _parentObjectType = parentObjectType; + _childObjectType = childObjectType; } - public RelationType(Guid childObjectType, Guid parentObjectType, string alias, string name) - : this(childObjectType, parentObjectType, alias) - { - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name)); - Name = name; - } + /// /// Gets or sets the Name of the RelationType @@ -69,7 +71,7 @@ namespace Umbraco.Core.Models /// /// Corresponds to the NodeObjectType in the umbracoNode table [DataMember] - public Guid ParentObjectType + public Guid? ParentObjectType { get => _parentObjectType; set => SetPropertyValueAndDetectChanges(value, ref _parentObjectType, nameof(ParentObjectType)); @@ -80,7 +82,7 @@ namespace Umbraco.Core.Models /// /// Corresponds to the NodeObjectType in the umbracoNode table [DataMember] - public Guid ChildObjectType + public Guid? ChildObjectType { get => _childObjectType; set => SetPropertyValueAndDetectChanges(value, ref _childObjectType, nameof(ChildObjectType)); diff --git a/src/Umbraco.Core/Persistence/Dtos/RelationDto.cs b/src/Umbraco.Core/Persistence/Dtos/RelationDto.cs index f1fd3007d7..b21866eb8b 100644 --- a/src/Umbraco.Core/Persistence/Dtos/RelationDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/RelationDto.cs @@ -34,5 +34,13 @@ namespace Umbraco.Core.Persistence.Dtos [Column("comment")] [Length(1000)] public string Comment { get; set; } + + [ResultColumn] + [Column("parentObjectType")] + public Guid ParentObjectType { get; set; } + + [ResultColumn] + [Column("childObjectType")] + public Guid ChildObjectType { get; set; } } } diff --git a/src/Umbraco.Core/Persistence/Dtos/RelationTypeDto.cs b/src/Umbraco.Core/Persistence/Dtos/RelationTypeDto.cs index e972192844..d3e107d23f 100644 --- a/src/Umbraco.Core/Persistence/Dtos/RelationTypeDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/RelationTypeDto.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Dtos [ExplicitColumns] internal class RelationTypeDto { - public const int NodeIdSeed = 4; + public const int NodeIdSeed = 10; [Column("id")] [PrimaryKeyColumn(IdentitySeed = NodeIdSeed)] @@ -23,17 +23,20 @@ namespace Umbraco.Core.Persistence.Dtos public bool Dual { get; set; } [Column("parentObjectType")] - public Guid ParentObjectType { get; set; } + [NullSetting(NullSetting = NullSettings.Null)] + public Guid? ParentObjectType { get; set; } [Column("childObjectType")] - public Guid ChildObjectType { get; set; } + [NullSetting(NullSetting = NullSettings.Null)] + public Guid? ChildObjectType { get; set; } [Column("name")] + [NullSetting(NullSetting = NullSettings.NotNull)] [Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelationType_name")] public string Name { get; set; } [Column("alias")] - [NullSetting(NullSetting = NullSettings.Null)] + [NullSetting(NullSetting = NullSettings.NotNull)] [Length(100)] [Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelationType_alias")] public string Alias { get; set; } diff --git a/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs b/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs index 6abb858e94..d8f100cdbe 100644 --- a/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/RelationFactory.cs @@ -3,20 +3,11 @@ using Umbraco.Core.Persistence.Dtos; namespace Umbraco.Core.Persistence.Factories { - internal class RelationFactory + internal static class RelationFactory { - private readonly IRelationType _relationType; - - public RelationFactory(IRelationType relationType) + public static IRelation BuildEntity(RelationDto dto, IRelationType relationType) { - _relationType = relationType; - } - - #region Implementation of IEntityFactory - - public IRelation BuildEntity(RelationDto dto) - { - var entity = new Relation(dto.ParentId, dto.ChildId, _relationType); + var entity = new Relation(dto.ParentId, dto.ChildId, dto.ParentObjectType, dto.ChildObjectType, relationType); try { @@ -37,7 +28,7 @@ namespace Umbraco.Core.Persistence.Factories } } - public RelationDto BuildDto(IRelation entity) + public static RelationDto BuildDto(IRelation entity) { var dto = new RelationDto { @@ -54,6 +45,5 @@ namespace Umbraco.Core.Persistence.Factories return dto; } - #endregion } } diff --git a/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs index ca6928a0a1..edd87fec68 100644 --- a/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Factories public static IRelationType BuildEntity(RelationTypeDto dto) { - var entity = new RelationType(dto.ChildObjectType, dto.ParentObjectType, dto.Alias); + var entity = new RelationType(dto.Name, dto.Alias, dto.Dual, dto.ChildObjectType, dto.ParentObjectType); try { @@ -17,8 +17,6 @@ namespace Umbraco.Core.Persistence.Factories entity.Id = dto.Id; entity.Key = dto.UniqueId; - entity.IsBidirectional = dto.Dual; - entity.Name = dto.Name; // reset dirty initial properties (U4-1946) entity.ResetDirtyProperties(false); diff --git a/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs index 00ab158d83..9a2e42568e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Models; +using System.Collections.Generic; +using Umbraco.Core.Models; namespace Umbraco.Core.Persistence.Repositories { diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/RelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/RelationRepository.cs index 88e28e6ab8..ab6b02afb4 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/RelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/RelationRepository.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Scoping; +using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics; namespace Umbraco.Core.Persistence.Repositories.Implement { @@ -40,10 +41,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var relationType = _relationTypeRepository.Get(dto.RelationType); if (relationType == null) - throw new Exception(string.Format("RelationType with Id: {0} doesn't exist", dto.RelationType)); + throw new InvalidOperationException(string.Format("RelationType with Id: {0} doesn't exist", dto.RelationType)); - var factory = new RelationFactory(relationType); - return DtoToEntity(dto, factory); + return DtoToEntity(dto, relationType); } protected override IEnumerable PerformGetAll(params int[] ids) @@ -68,26 +68,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private IEnumerable DtosToEntities(IEnumerable dtos) { - // in most cases, the relation type will be the same for all of them, - // plus we've ordered the relations by type, so try to allocate as few - // factories as possible - bearing in mind that relation types are cached - RelationFactory factory = null; - var relationTypeId = -1; + //NOTE: This is N+1, BUT ALL relation types are cached so shouldn't matter - return dtos.Select(x => - { - if (relationTypeId != x.RelationType) - factory = new RelationFactory(_relationTypeRepository.Get(relationTypeId = x.RelationType)); - return DtoToEntity(x, factory); - }).ToList(); + return dtos.Select(x => DtoToEntity(x, _relationTypeRepository.Get(x.RelationType))).ToList(); } - private static IRelation DtoToEntity(RelationDto dto, RelationFactory factory) + private static IRelation DtoToEntity(RelationDto dto, IRelationType relationType) { - var entity = factory.BuildEntity(dto); + var entity = RelationFactory.BuildEntity(dto, relationType); // reset dirty initial properties (U4-1946) - ((BeingDirtyBase)entity).ResetDirtyProperties(false); + entity.ResetDirtyProperties(false); return entity; } @@ -98,14 +89,18 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override Sql GetBaseQuery(bool isCount) { - var sql = Sql(); + if (isCount) + { + return Sql().SelectCount().From(); + } - sql = isCount - ? sql.SelectCount() - : sql.Select(); + var sql = Sql().Select() + .AndSelect("uchild", x => Alias(x.NodeObjectType, "childObjectType")) + .AndSelect("uparent", x => Alias(x.NodeObjectType, "parentObjectType")) + .From() + .InnerJoin("uchild").On((rel, node) => rel.ChildId == node.NodeId, aliasRight: "uchild") + .InnerJoin("uparent").On((rel, node) => rel.ParentId == node.NodeId, aliasRight: "uparent"); - sql - .From(); return sql; } @@ -137,11 +132,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { entity.AddingEntity(); - var factory = new RelationFactory(entity.RelationType); - var dto = factory.BuildDto(entity); + var dto = RelationFactory.BuildDto(entity); var id = Convert.ToInt32(Database.Insert(dto)); + entity.Id = id; + PopulateObjectTypes(entity); entity.ResetDirtyProperties(); } @@ -150,10 +146,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { entity.UpdatingEntity(); - var factory = new RelationFactory(entity.RelationType); - var dto = factory.BuildDto(entity); + var dto = RelationFactory.BuildDto(entity); Database.Update(dto); + PopulateObjectTypes(entity); + entity.ResetDirtyProperties(); } @@ -173,5 +170,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Execute(Sql().Delete().WhereIn(x => x.Id, subQuery)); } + + private void PopulateObjectTypes(IRelation entity) + { + var nodes = Database.Fetch(Sql().Select().From().Where(x => x.NodeId == entity.ChildId || x.NodeId == entity.ParentId)) + .ToDictionary(x => x.NodeId, x => x.NodeObjectType); + entity.ParentObjectType = nodes[entity.ParentId].Value; + entity.ChildObjectType = nodes[entity.ChildId].Value; + } } } diff --git a/src/Umbraco.Core/Services/Implement/RelationService.cs b/src/Umbraco.Core/Services/Implement/RelationService.cs index 9de3492e09..cc0a3e23bc 100644 --- a/src/Umbraco.Core/Services/Implement/RelationService.cs +++ b/src/Umbraco.Core/Services/Implement/RelationService.cs @@ -288,7 +288,7 @@ namespace Umbraco.Core.Services.Implement /// An public IUmbracoEntity GetChildEntityFromRelation(IRelation relation) { - var objectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ChildObjectType); + var objectType = ObjectTypes.GetUmbracoObjectType(relation.ChildObjectType); return _entityService.Get(relation.ChildId, objectType); } @@ -299,7 +299,7 @@ namespace Umbraco.Core.Services.Implement /// An public IUmbracoEntity GetParentEntityFromRelation(IRelation relation) { - var objectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ParentObjectType); + var objectType = ObjectTypes.GetUmbracoObjectType(relation.ParentObjectType); return _entityService.Get(relation.ParentId, objectType); } @@ -310,8 +310,8 @@ namespace Umbraco.Core.Services.Implement /// Returns a Tuple with Parent (item1) and Child (item2) public Tuple GetEntitiesFromRelation(IRelation relation) { - var childObjectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ChildObjectType); - var parentObjectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ParentObjectType); + var childObjectType = ObjectTypes.GetUmbracoObjectType(relation.ChildObjectType); + var parentObjectType = ObjectTypes.GetUmbracoObjectType(relation.ParentObjectType); var child = _entityService.Get(relation.ChildId, childObjectType); var parent = _entityService.Get(relation.ParentId, parentObjectType); @@ -328,7 +328,7 @@ namespace Umbraco.Core.Services.Implement { foreach (var relation in relations) { - var objectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ChildObjectType); + var objectType = ObjectTypes.GetUmbracoObjectType(relation.ChildObjectType); yield return _entityService.Get(relation.ChildId, objectType); } } @@ -342,7 +342,7 @@ namespace Umbraco.Core.Services.Implement { foreach (var relation in relations) { - var objectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ParentObjectType); + var objectType = ObjectTypes.GetUmbracoObjectType(relation.ParentObjectType); yield return _entityService.Get(relation.ParentId, objectType); } } @@ -356,8 +356,8 @@ namespace Umbraco.Core.Services.Implement { foreach (var relation in relations) { - var childObjectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ChildObjectType); - var parentObjectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ParentObjectType); + var childObjectType = ObjectTypes.GetUmbracoObjectType(relation.ChildObjectType); + var parentObjectType = ObjectTypes.GetUmbracoObjectType(relation.ParentObjectType); var child = _entityService.Get(relation.ChildId, childObjectType); var parent = _entityService.Get(relation.ParentId, parentObjectType); @@ -379,6 +379,8 @@ namespace Umbraco.Core.Services.Implement if (relationType.HasIdentity == false) Save(relationType); + //TODO: We don't check if this exists first, it will throw some sort of data integrity exception if it already exists, is that ok? + var relation = new Relation(parentId, childId, relationType); using (var scope = ScopeProvider.CreateScope()) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 65118a877a..1f83bd2807 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -245,6 +245,7 @@ + diff --git a/src/Umbraco.Tests/Models/RelationTests.cs b/src/Umbraco.Tests/Models/RelationTests.cs index c62dcdc6eb..91560abbb3 100644 --- a/src/Umbraco.Tests/Models/RelationTests.cs +++ b/src/Umbraco.Tests/Models/RelationTests.cs @@ -12,7 +12,7 @@ namespace Umbraco.Tests.Models [Test] public void Can_Deep_Clone() { - var item = new Relation(9, 8, new RelationType(Guid.NewGuid(), Guid.NewGuid(), "test") + var item = new Relation(9, 8, new RelationType("test", "test", false, Guid.NewGuid(), Guid.NewGuid()) { Id = 66 }) @@ -52,7 +52,7 @@ namespace Umbraco.Tests.Models { var ss = new SerializationService(new JsonNetSerializer()); - var item = new Relation(9, 8, new RelationType(Guid.NewGuid(), Guid.NewGuid(), "test") + var item = new Relation(9, 8, new RelationType("test", "test", false, Guid.NewGuid(), Guid.NewGuid()) { Id = 66 }) diff --git a/src/Umbraco.Tests/Models/RelationTypeTests.cs b/src/Umbraco.Tests/Models/RelationTypeTests.cs index 9d8fdcdf25..4555b6366f 100644 --- a/src/Umbraco.Tests/Models/RelationTypeTests.cs +++ b/src/Umbraco.Tests/Models/RelationTypeTests.cs @@ -12,7 +12,7 @@ namespace Umbraco.Tests.Models [Test] public void Can_Deep_Clone() { - var item = new RelationType(Guid.NewGuid(), Guid.NewGuid(), "test") + var item = new RelationType("test", "test", false, Guid.NewGuid(), Guid.NewGuid()) { Id = 66, CreateDate = DateTime.Now, @@ -48,7 +48,7 @@ namespace Umbraco.Tests.Models { var ss = new SerializationService(new JsonNetSerializer()); - var item = new RelationType(Guid.NewGuid(), Guid.NewGuid(), "test") + var item = new RelationType("test", "test", false, Guid.NewGuid(), Guid.NewGuid()) { Id = 66, CreateDate = DateTime.Now, diff --git a/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs index cea7f44b71..8d9f82a776 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs @@ -260,8 +260,16 @@ namespace Umbraco.Tests.Persistence.Repositories public void CreateTestData() { - var relateContent = new RelationType(Constants.ObjectTypes.Document, new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"), "relateContentOnCopy") { IsBidirectional = true, Name = "Relate Content on Copy" }; - var relateContentType = new RelationType(Constants.ObjectTypes.DocumentType, new Guid("A2CB7800-F571-4787-9638-BC48539A0EFB"), "relateContentTypeOnCopy") { IsBidirectional = true, Name = "Relate ContentType on Copy" }; + var relateContent = new RelationType( + "Relate Content on Copy", "relateContentOnCopy", true, + Constants.ObjectTypes.Document, + new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972")); + + var relateContentType = new RelationType("Relate ContentType on Copy", + "relateContentTypeOnCopy", + true, + Constants.ObjectTypes.DocumentType, + new Guid("A2CB7800-F571-4787-9638-BC48539A0EFB")); var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) diff --git a/src/Umbraco.Tests/Persistence/Repositories/RelationTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/RelationTypeRepositoryTest.cs index e52e2dfcdf..881ea23dc8 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/RelationTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/RelationTypeRepositoryTest.cs @@ -42,9 +42,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository(provider); // Act - var relateMemberToContent = new RelationType(Constants.ObjectTypes.Member, - Constants.ObjectTypes.Document, - "relateMemberToContent") { IsBidirectional = true, Name = "Relate Member to Content" }; + var relateMemberToContent = new RelationType("Relate Member to Content", "relateMemberToContent", true, Constants.ObjectTypes.Member, Constants.ObjectTypes.Document); repository.Save(relateMemberToContent); @@ -226,8 +224,8 @@ namespace Umbraco.Tests.Persistence.Repositories public void CreateTestData() { - var relateContent = new RelationType(Constants.ObjectTypes.Document, new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"), "relateContentOnCopy") { IsBidirectional = true, Name = "Relate Content on Copy" }; - var relateContentType = new RelationType(Constants.ObjectTypes.DocumentType, new Guid("A2CB7800-F571-4787-9638-BC48539A0EFB"), "relateContentTypeOnCopy") { IsBidirectional = true, Name = "Relate ContentType on Copy" }; + var relateContent = new RelationType("Relate Content on Copy", "relateContentOnCopy", true, Constants.ObjectTypes.Document, new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972")); + var relateContentType = new RelationType("Relate ContentType on Copy", "relateContentTypeOnCopy", true, Constants.ObjectTypes.DocumentType, new Guid("A2CB7800-F571-4787-9638-BC48539A0EFB")); var provider = TestObjects.GetScopeProvider(Logger); using (var scope = ScopeProvider.CreateScope()) diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 1f53767dbb..7d65513cc0 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1778,7 +1778,7 @@ namespace Umbraco.Tests.Services admin.StartContentIds = new[] {content1.Id}; ServiceContext.UserService.Save(admin); - ServiceContext.RelationService.Save(new RelationType(Constants.ObjectTypes.Document, Constants.ObjectTypes.Document, "test")); + ServiceContext.RelationService.Save(new RelationType("test", "test", false, Constants.ObjectTypes.Document, Constants.ObjectTypes.Document)); Assert.IsNotNull(ServiceContext.RelationService.Relate(content1, content2, "test")); ServiceContext.PublicAccessService.Save(new PublicAccessEntry(content1, content2, content2, new List diff --git a/src/Umbraco.Tests/Services/RelationServiceTests.cs b/src/Umbraco.Tests/Services/RelationServiceTests.cs index ad24d2345a..a39a9c838b 100644 --- a/src/Umbraco.Tests/Services/RelationServiceTests.cs +++ b/src/Umbraco.Tests/Services/RelationServiceTests.cs @@ -4,24 +4,91 @@ using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; namespace Umbraco.Tests.Services { [TestFixture] [Apartment(ApartmentState.STA)] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] public class RelationServiceTests : TestWithSomeContentBase { [Test] public void Can_Create_RelationType_Without_Name() { var rs = ServiceContext.RelationService; - var rt = new RelationType(Constants.ObjectTypes.Document, Constants.ObjectTypes.Document, "repeatedEventOccurence"); + IRelationType rt = new RelationType("Test", "repeatedEventOccurence", false, Constants.ObjectTypes.Document, Constants.ObjectTypes.Media); Assert.DoesNotThrow(() => rs.Save(rt)); - Assert.AreEqual(rt.Name, "repeatedEventOccurence"); + //re-get + rt = ServiceContext.RelationService.GetRelationTypeById(rt.Id); + + Assert.AreEqual("Test", rt.Name); + Assert.AreEqual("repeatedEventOccurence", rt.Alias); + Assert.AreEqual(false, rt.IsBidirectional); + Assert.AreEqual(Constants.ObjectTypes.Document, rt.ChildObjectType.Value); + Assert.AreEqual(Constants.ObjectTypes.Media, rt.ParentObjectType.Value); + } + + [Test] + public void Create_Relation_Type_Without_Object_Types() + { + var rs = ServiceContext.RelationService; + IRelationType rt = new RelationType("repeatedEventOccurence", "repeatedEventOccurence", false, null, null); + + Assert.DoesNotThrow(() => rs.Save(rt)); + + //re-get + rt = ServiceContext.RelationService.GetRelationTypeById(rt.Id); + + Assert.IsNull(rt.ChildObjectType); + Assert.IsNull(rt.ParentObjectType); + } + + [Test] + public void Relation_Returns_Parent_Child_Object_Types_When_Creating() + { + var r = CreateNewRelation("Test", "test"); + + Assert.AreEqual(Constants.ObjectTypes.Document, r.ParentObjectType); + Assert.AreEqual(Constants.ObjectTypes.Media, r.ChildObjectType); + } + + [Test] + public void Relation_Returns_Parent_Child_Object_Types_When_Getting() + { + var r = CreateNewRelation("Test", "test"); + + // re-get + r = ServiceContext.RelationService.GetById(r.Id); + + Assert.AreEqual(Constants.ObjectTypes.Document, r.ParentObjectType); + Assert.AreEqual(Constants.ObjectTypes.Media, r.ChildObjectType); + } + + private IRelation CreateNewRelation(string name, string alias) + { + var rs = ServiceContext.RelationService; + var rt = new RelationType(name, alias, false, null, null); + rs.Save(rt); + + var ct = MockedContentTypes.CreateBasicContentType(); + ServiceContext.ContentTypeService.Save(ct); + + var mt = MockedContentTypes.CreateImageMediaType("img"); + ServiceContext.MediaTypeService.Save(mt); + + var c1 = MockedContent.CreateBasicContent(ct); + var c2 = MockedMedia.CreateMediaImage(mt, -1); + ServiceContext.ContentService.Save(c1); + ServiceContext.MediaService.Save(c2); + + var r = new Relation(c1.Id, c2.Id, rt); + ServiceContext.RelationService.Save(r); + + return r; } //TODO: Create a relation for entities of the wrong Entity Type (GUID) based on the Relation Type's defined parent/child object types diff --git a/src/Umbraco.Web/Editors/RelationTypeController.cs b/src/Umbraco.Web/Editors/RelationTypeController.cs index faafbb79f1..6fb9108d74 100644 --- a/src/Umbraco.Web/Editors/RelationTypeController.cs +++ b/src/Umbraco.Web/Editors/RelationTypeController.cs @@ -84,11 +84,7 @@ namespace Umbraco.Web.Editors /// A containing the persisted relation type's ID. public HttpResponseMessage PostCreate(RelationTypeSave relationType) { - var relationTypePersisted = new RelationType(relationType.ChildObjectType, relationType.ParentObjectType, relationType.Name.ToSafeAlias(true)) - { - Name = relationType.Name, - IsBidirectional = relationType.IsBidirectional - }; + var relationTypePersisted = new RelationType(relationType.Name, relationType.Name.ToSafeAlias(true), relationType.IsBidirectional, relationType.ChildObjectType, relationType.ParentObjectType); try { diff --git a/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs index 55e140d6f3..8a92d085eb 100644 --- a/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/RelationTypeDisplay.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.Models.ContentEditing /// /// Corresponds to the NodeObjectType in the umbracoNode table [DataMember(Name = "parentObjectType", IsRequired = true)] - public Guid ParentObjectType { get; set; } + public Guid? ParentObjectType { get; set; } /// /// Gets or sets the Parent's object type name. @@ -38,7 +38,7 @@ namespace Umbraco.Web.Models.ContentEditing /// /// Corresponds to the NodeObjectType in the umbracoNode table [DataMember(Name = "childObjectType", IsRequired = true)] - public Guid ChildObjectType { get; set; } + public Guid? ChildObjectType { get; set; } /// /// Gets or sets the Child's object type name. diff --git a/src/Umbraco.Web/Models/Mapping/RelationMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/RelationMapDefinition.cs index 7787750e54..7f89c78f8a 100644 --- a/src/Umbraco.Web/Models/Mapping/RelationMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/RelationMapDefinition.cs @@ -29,8 +29,8 @@ namespace Umbraco.Web.Models.Mapping target.Path = "-1," + source.Id; // Set the "friendly" names for the parent and child object types - target.ParentObjectTypeName = ObjectTypes.GetUmbracoObjectType(source.ParentObjectType).GetFriendlyName(); - target.ChildObjectTypeName = ObjectTypes.GetUmbracoObjectType(source.ChildObjectType).GetFriendlyName(); + target.ParentObjectTypeName = source.ParentObjectType.HasValue ? ObjectTypes.GetUmbracoObjectType(source.ParentObjectType.Value).GetFriendlyName() : string.Empty; + target.ChildObjectTypeName = source.ChildObjectType.HasValue ? ObjectTypes.GetUmbracoObjectType(source.ChildObjectType.Value).GetFriendlyName() : string.Empty; } // Umbraco.Code.MapAll -ParentName -ChildName