using System; using System.Collections.Generic; using System.Linq; using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Scoping; namespace Umbraco.Core.Services.Implement { public class RelationService : ScopeRepositoryService, IRelationService { private readonly IEntityService _entityService; private readonly IRelationRepository _relationRepository; private readonly IRelationTypeRepository _relationTypeRepository; public RelationService(IScopeProvider uowProvider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IEntityService entityService, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository) : base(uowProvider, logger, eventMessagesFactory) { _relationRepository = relationRepository; _relationTypeRepository = relationTypeRepository; _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); } /// /// Gets a by its Id /// /// Id of the /// A object public IRelation GetById(int id) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { return _relationRepository.Get(id); } } /// /// Gets a by its Id /// /// Id of the /// A object public IRelationType GetRelationTypeById(int id) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { return _relationTypeRepository.Get(id); } } /// /// Gets a by its Id /// /// Id of the /// A object public IRelationType GetRelationTypeById(Guid id) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { return _relationTypeRepository.Get(id); } } /// /// Gets a by its Alias /// /// Alias of the /// A object public IRelationType GetRelationTypeByAlias(string alias) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.Alias == alias); return _relationTypeRepository.Get(query).FirstOrDefault(); } } /// /// Gets all objects /// /// Optional array of integer ids to return relations for /// An enumerable list of objects public IEnumerable GetAllRelations(params int[] ids) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { return _relationRepository.GetMany(ids); } } /// /// Gets all objects by their /// /// to retrieve Relations for /// An enumerable list of objects public IEnumerable GetAllRelationsByRelationType(RelationType relationType) { return GetAllRelationsByRelationType(relationType.Id); } /// /// Gets all objects by their 's Id /// /// Id of the to retrieve Relations for /// An enumerable list of objects public IEnumerable GetAllRelationsByRelationType(int relationTypeId) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.RelationTypeId == relationTypeId); return _relationRepository.Get(query); } } /// /// Gets all objects /// /// Optional array of integer ids to return relationtypes for /// An enumerable list of objects public IEnumerable GetAllRelationTypes(params int[] ids) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { return _relationTypeRepository.GetMany(ids); } } /// /// Gets a list of objects by their parent Id /// /// Id of the parent to retrieve relations for /// An enumerable list of objects public IEnumerable GetByParentId(int id) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.ParentId == id); return _relationRepository.Get(query); } } /// /// Gets a list of objects by their parent entity /// /// Parent Entity to retrieve relations for /// An enumerable list of objects public IEnumerable GetByParent(IUmbracoEntity parent) { return GetByParentId(parent.Id); } /// /// Gets a list of objects by their parent entity /// /// Parent Entity to retrieve relations for /// Alias of the type of relation to retrieve /// An enumerable list of objects public IEnumerable GetByParent(IUmbracoEntity parent, string relationTypeAlias) { return GetByParent(parent).Where(relation => relation.RelationType.Alias == relationTypeAlias); } /// /// Gets a list of objects by their child Id /// /// Id of the child to retrieve relations for /// An enumerable list of objects public IEnumerable GetByChildId(int id) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.ChildId == id); return _relationRepository.Get(query); } } /// /// Gets a list of objects by their child Entity /// /// Child Entity to retrieve relations for /// An enumerable list of objects public IEnumerable GetByChild(IUmbracoEntity child) { return GetByChildId(child.Id); } /// /// Gets a list of objects by their child Entity /// /// Child Entity to retrieve relations for /// Alias of the type of relation to retrieve /// An enumerable list of objects public IEnumerable GetByChild(IUmbracoEntity child, string relationTypeAlias) { return GetByChild(child).Where(relation => relation.RelationType.Alias == relationTypeAlias); } /// /// Gets a list of objects by their child or parent Id. /// Using this method will get you all relations regards of it being a child or parent relation. /// /// Id of the child or parent to retrieve relations for /// An enumerable list of objects public IEnumerable GetByParentOrChildId(int id) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.ChildId == id || x.ParentId == id); return _relationRepository.Get(query); } } public IEnumerable GetByParentOrChildId(int id, string relationTypeAlias) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var rtQuery = Query().Where(x => x.Alias == relationTypeAlias); var relationType = _relationTypeRepository.Get(rtQuery).FirstOrDefault(); if (relationType == null) return Enumerable.Empty(); var query = Query().Where(x => (x.ChildId == id || x.ParentId == id) && x.RelationTypeId == relationType.Id); return _relationRepository.Get(query); } } /// /// Gets a list of objects by the Name of the /// /// Name of the to retrieve Relations for /// An enumerable list of objects public IEnumerable GetByRelationTypeName(string relationTypeName) { List relationTypeIds; using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.Name == relationTypeName); var relationTypes = _relationTypeRepository.Get(query); relationTypeIds = relationTypes.Select(x => x.Id).ToList(); } return relationTypeIds.Count == 0 ? Enumerable.Empty() : GetRelationsByListOfTypeIds(relationTypeIds); } /// /// Gets a list of objects by the Alias of the /// /// Alias of the to retrieve Relations for /// An enumerable list of objects public IEnumerable GetByRelationTypeAlias(string relationTypeAlias) { List relationTypeIds; using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.Alias == relationTypeAlias); var relationTypes = _relationTypeRepository.Get(query); relationTypeIds = relationTypes.Select(x => x.Id).ToList(); } return relationTypeIds.Count == 0 ? Enumerable.Empty() : GetRelationsByListOfTypeIds(relationTypeIds); } /// /// Gets a list of objects by the Id of the /// /// Id of the to retrieve Relations for /// An enumerable list of objects public IEnumerable GetByRelationTypeId(int relationTypeId) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.RelationTypeId == relationTypeId); return _relationRepository.Get(query); } } /// /// Gets the Child object from a Relation as an /// /// Relation to retrieve child object from /// An public IUmbracoEntity GetChildEntityFromRelation(IRelation relation) { var objectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ChildObjectType); return _entityService.Get(relation.ChildId, objectType); } /// /// Gets the Parent object from a Relation as an /// /// Relation to retrieve parent object from /// An public IUmbracoEntity GetParentEntityFromRelation(IRelation relation) { var objectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ParentObjectType); return _entityService.Get(relation.ParentId, objectType); } /// /// Gets the Parent and Child objects from a Relation as a "/> with . /// /// Relation to retrieve parent and child object from /// 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 child = _entityService.Get(relation.ChildId, childObjectType); var parent = _entityService.Get(relation.ParentId, parentObjectType); return new Tuple(parent, child); } /// /// Gets the Child objects from a list of Relations as a list of objects. /// /// List of relations to retrieve child objects from /// An enumerable list of public IEnumerable GetChildEntitiesFromRelations(IEnumerable relations) { foreach (var relation in relations) { var objectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ChildObjectType); yield return _entityService.Get(relation.ChildId, objectType); } } /// /// Gets the Parent objects from a list of Relations as a list of objects. /// /// List of relations to retrieve parent objects from /// An enumerable list of public IEnumerable GetParentEntitiesFromRelations(IEnumerable relations) { foreach (var relation in relations) { var objectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ParentObjectType); yield return _entityService.Get(relation.ParentId, objectType); } } /// /// Gets the Parent and Child objects from a list of Relations as a list of objects. /// /// List of relations to retrieve parent and child objects from /// An enumerable list of with public IEnumerable> GetEntitiesFromRelations(IEnumerable relations) { foreach (var relation in relations) { var childObjectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ChildObjectType); var parentObjectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ParentObjectType); var child = _entityService.Get(relation.ChildId, childObjectType); var parent = _entityService.Get(relation.ParentId, parentObjectType); yield return new Tuple(parent, child); } } /// /// Relates two objects by their entity Ids. /// /// Id of the parent /// Id of the child /// The type of relation to create /// The created public IRelation Relate(int parentId, int childId, IRelationType relationType) { // Ensure that the RelationType has an identity before using it to relate two entities if (relationType.HasIdentity == false) Save(relationType); var relation = new Relation(parentId, childId, relationType); using (var scope = ScopeProvider.CreateScope()) { var saveEventArgs = new SaveEventArgs(relation); if (scope.Events.DispatchCancelable(SavingRelation, this, saveEventArgs)) { scope.Complete(); return relation; // TODO: returning sth that does not exist here?! } _relationRepository.Save(relation); saveEventArgs.CanCancel = false; scope.Events.Dispatch(SavedRelation, this, saveEventArgs); scope.Complete(); return relation; } } /// /// Relates two objects that are based on the interface. /// /// Parent entity /// Child entity /// The type of relation to create /// The created public IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, IRelationType relationType) { return Relate(parent.Id, child.Id, relationType); } /// /// Relates two objects by their entity Ids. /// /// Id of the parent /// Id of the child /// Alias of the type of relation to create /// The created public IRelation Relate(int parentId, int childId, string relationTypeAlias) { var relationType = GetRelationTypeByAlias(relationTypeAlias); if (relationType == null || string.IsNullOrEmpty(relationType.Alias)) throw new ArgumentNullException(string.Format("No RelationType with Alias '{0}' exists.", relationTypeAlias)); return Relate(parentId, childId, relationType); } /// /// Relates two objects that are based on the interface. /// /// Parent entity /// Child entity /// Alias of the type of relation to create /// The created public IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, string relationTypeAlias) { var relationType = GetRelationTypeByAlias(relationTypeAlias); if (relationType == null || string.IsNullOrEmpty(relationType.Alias)) throw new ArgumentNullException(string.Format("No RelationType with Alias '{0}' exists.", relationTypeAlias)); return Relate(parent.Id, child.Id, relationType); } /// /// Checks whether any relations exists for the passed in . /// /// to check for relations /// Returns True if any relations exists for the given , otherwise False public bool HasRelations(IRelationType relationType) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.RelationTypeId == relationType.Id); return _relationRepository.Get(query).Any(); } } /// /// Checks whether any relations exists for the passed in Id. /// /// Id of an object to check relations for /// Returns True if any relations exists with the given Id, otherwise False public bool IsRelated(int id) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.ParentId == id || x.ChildId == id); return _relationRepository.Get(query).Any(); } } /// /// Checks whether two items are related /// /// Id of the Parent relation /// Id of the Child relation /// Returns True if any relations exists with the given Ids, otherwise False public bool AreRelated(int parentId, int childId) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.ParentId == parentId && x.ChildId == childId); return _relationRepository.Get(query).Any(); } } /// /// Checks whether two items are related with a given relation type alias /// /// Id of the Parent relation /// Id of the Child relation /// Alias of the relation type /// Returns True if any relations exists with the given Ids and relation type, otherwise False public bool AreRelated(int parentId, int childId, string relationTypeAlias) { var relType = GetRelationTypeByAlias(relationTypeAlias); if (relType == null) return false; return AreRelated(parentId, childId, relType); } /// /// Checks whether two items are related with a given relation type /// /// Id of the Parent relation /// Id of the Child relation /// Type of relation /// Returns True if any relations exists with the given Ids and relation type, otherwise False public bool AreRelated(int parentId, int childId, IRelationType relationType) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { var query = Query().Where(x => x.ParentId == parentId && x.ChildId == childId && x.RelationTypeId == relationType.Id); return _relationRepository.Get(query).Any(); } } /// /// Checks whether two items are related /// /// Parent entity /// Child entity /// Returns True if any relations exist between the entities, otherwise False public bool AreRelated(IUmbracoEntity parent, IUmbracoEntity child) { return AreRelated(parent.Id, child.Id); } /// /// Checks whether two items are related /// /// Parent entity /// Child entity /// Alias of the type of relation to create /// Returns True if any relations exist between the entities, otherwise False public bool AreRelated(IUmbracoEntity parent, IUmbracoEntity child, string relationTypeAlias) { return AreRelated(parent.Id, child.Id, relationTypeAlias); } /// /// Saves a /// /// Relation to save public void Save(IRelation relation) { using (var scope = ScopeProvider.CreateScope()) { var saveEventArgs = new SaveEventArgs(relation); if (scope.Events.DispatchCancelable(SavingRelation, this, saveEventArgs)) { scope.Complete(); return; } _relationRepository.Save(relation); scope.Complete(); saveEventArgs.CanCancel = false; scope.Events.Dispatch(SavedRelation, this, saveEventArgs); } } /// /// Saves a /// /// RelationType to Save public void Save(IRelationType relationType) { using (var scope = ScopeProvider.CreateScope()) { var saveEventArgs = new SaveEventArgs(relationType); if (scope.Events.DispatchCancelable(SavingRelationType, this, saveEventArgs)) { scope.Complete(); return; } _relationTypeRepository.Save(relationType); scope.Complete(); saveEventArgs.CanCancel = false; scope.Events.Dispatch(SavedRelationType, this, saveEventArgs); } } /// /// Deletes a /// /// Relation to Delete public void Delete(IRelation relation) { using (var scope = ScopeProvider.CreateScope()) { var deleteEventArgs = new DeleteEventArgs(relation); if (scope.Events.DispatchCancelable(DeletingRelation, this, deleteEventArgs)) { scope.Complete(); return; } _relationRepository.Delete(relation); scope.Complete(); deleteEventArgs.CanCancel = false; scope.Events.Dispatch(DeletedRelation, this, deleteEventArgs); } } /// /// Deletes a /// /// RelationType to Delete public void Delete(IRelationType relationType) { using (var scope = ScopeProvider.CreateScope()) { var deleteEventArgs = new DeleteEventArgs(relationType); if (scope.Events.DispatchCancelable(DeletingRelationType, this, deleteEventArgs)) { scope.Complete(); return; } _relationTypeRepository.Delete(relationType); scope.Complete(); deleteEventArgs.CanCancel = false; scope.Events.Dispatch(DeletedRelationType, this, deleteEventArgs); } } /// /// Deletes all objects based on the passed in /// /// to Delete Relations for public void DeleteRelationsOfType(IRelationType relationType) { var relations = new List(); using (var scope = ScopeProvider.CreateScope()) { var query = Query().Where(x => x.RelationTypeId == relationType.Id); relations.AddRange(_relationRepository.Get(query).ToList()); foreach (var relation in relations) _relationRepository.Delete(relation); scope.Complete(); scope.Events.Dispatch(DeletedRelation, this, new DeleteEventArgs(relations, false)); } } #region Private Methods private IEnumerable GetRelationsByListOfTypeIds(IEnumerable relationTypeIds) { var relations = new List(); using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { foreach (var relationTypeId in relationTypeIds) { var id = relationTypeId; var query = Query().Where(x => x.RelationTypeId == id); relations.AddRange(_relationRepository.Get(query)); } } return relations; } #endregion #region Events Handlers /// /// Occurs before Deleting a Relation /// public static event TypedEventHandler> DeletingRelation; /// /// Occurs after a Relation is Deleted /// public static event TypedEventHandler> DeletedRelation; /// /// Occurs before Saving a Relation /// public static event TypedEventHandler> SavingRelation; /// /// Occurs after a Relation is Saved /// public static event TypedEventHandler> SavedRelation; /// /// Occurs before Deleting a RelationType /// public static event TypedEventHandler> DeletingRelationType; /// /// Occurs after a RelationType is Deleted /// public static event TypedEventHandler> DeletedRelationType; /// /// Occurs before Saving a RelationType /// public static event TypedEventHandler> SavingRelationType; /// /// Occurs after a RelationType is Saved /// public static event TypedEventHandler> SavedRelationType; #endregion } }