using System; using System.Collections.Generic; using System.Linq; using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { public class RelationService : IRelationService { private readonly IDatabaseUnitOfWorkProvider _uowProvider; private readonly RepositoryFactory _repositoryFactory; private readonly IEntityService _entityService; public RelationService(IDatabaseUnitOfWorkProvider uowProvider, RepositoryFactory repositoryFactory, IEntityService entityService) { _uowProvider = uowProvider; _repositoryFactory = repositoryFactory; _entityService = entityService; } /// /// Gets a by its Id /// /// Id of the /// A object public IRelation GetById(int id) { using (var repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork())) { return repository.Get(id); } } /// /// Gets a by its Id /// /// Id of the /// A object public IRelationType GetRelationTypeById(int id) { using (var repository = _repositoryFactory.CreateRelationTypeRepository(_uowProvider.GetUnitOfWork())) { return repository.Get(id); } } /// /// Gets a by its Alias /// /// Alias of the /// A object public IRelationType GetRelationTypeByAlias(string alias) { using (var repository = _repositoryFactory.CreateRelationTypeRepository(_uowProvider.GetUnitOfWork())) { var query = new Query().Where(x => x.Alias == alias); return repository.GetByQuery(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 repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork())) { return repository.GetAll(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 repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork())) { var query = new Query().Where(x => x.RelationTypeId == relationTypeId); return repository.GetByQuery(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 repository = _repositoryFactory.CreateRelationTypeRepository(_uowProvider.GetUnitOfWork())) { return repository.GetAll(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 repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork())) { var query = new Query().Where(x => x.ParentId == id); return repository.GetByQuery(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 repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork())) { var query = new Query().Where(x => x.ChildId == id); return repository.GetByQuery(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 repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork())) { var query = new Query().Where(x => x.ChildId == id || x.ParentId == id); return repository.GetByQuery(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 = null; using (var repository = _repositoryFactory.CreateRelationTypeRepository(_uowProvider.GetUnitOfWork())) { var query = new Query().Where(x => x.Name == relationTypeName); var relationTypes = repository.GetByQuery(query); if (relationTypes.Any()) { relationTypeIds = relationTypes.Select(x => x.Id).ToList(); } } if (relationTypeIds == null) return Enumerable.Empty(); return 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 = null; using (var repository = _repositoryFactory.CreateRelationTypeRepository(_uowProvider.GetUnitOfWork())) { var query = new Query().Where(x => x.Alias == relationTypeAlias); var relationTypes = repository.GetByQuery(query); if (relationTypes.Any()) { relationTypeIds = relationTypes.Select(x => x.Id).ToList(); } } if (relationTypeIds == null) return Enumerable.Empty(); return 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 repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork())) { var query = new Query().Where(x => x.RelationTypeId == relationTypeId); return repository.GetByQuery(query); } } /// /// Gets the Child object from a Relation as an /// /// Relation to retrieve child object from /// Optional bool to load the complete object graph when set to False /// An public IUmbracoEntity GetChildEntityFromRelation(IRelation relation, bool loadBaseType = false) { var objectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ChildObjectType); return _entityService.Get(relation.ChildId, objectType, loadBaseType); } /// /// Gets the Parent object from a Relation as an /// /// Relation to retrieve parent object from /// Optional bool to load the complete object graph when set to False /// An public IUmbracoEntity GetParentEntityFromRelation(IRelation relation, bool loadBaseType = false) { var objectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ParentObjectType); return _entityService.Get(relation.ParentId, objectType, loadBaseType); } /// /// Gets the Parent and Child objects from a Relation as a "/> with . /// /// Relation to retrieve parent and child object from /// Optional bool to load the complete object graph when set to False /// Returns a Tuple with Parent (item1) and Child (item2) public Tuple GetEntitiesFromRelation(IRelation relation, bool loadBaseType = false) { var childObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ChildObjectType); var parentObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ParentObjectType); var child = _entityService.Get(relation.ChildId, childObjectType, loadBaseType); var parent = _entityService.Get(relation.ParentId, parentObjectType, loadBaseType); 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 /// Optional bool to load the complete object graph when set to False /// An enumerable list of public IEnumerable GetChildEntitiesFromRelations(IEnumerable relations, bool loadBaseType = false) { foreach (var relation in relations) { var objectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ChildObjectType); yield return _entityService.Get(relation.ChildId, objectType, loadBaseType); } } /// /// Gets the Parent objects from a list of Relations as a list of objects. /// /// List of relations to retrieve parent objects from /// Optional bool to load the complete object graph when set to False /// An enumerable list of public IEnumerable GetParentEntitiesFromRelations(IEnumerable relations, bool loadBaseType = false) { foreach (var relation in relations) { var objectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ParentObjectType); yield return _entityService.Get(relation.ParentId, objectType, loadBaseType); } } /// /// 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 /// Optional bool to load the complete object graph when set to False /// An enumerable list of with public IEnumerable> GetEntitiesFromRelations( IEnumerable relations, bool loadBaseType = false) { foreach (var relation in relations) { var childObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ChildObjectType); var parentObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(relation.RelationType.ParentObjectType); var child = _entityService.Get(relation.ChildId, childObjectType, loadBaseType); var parent = _entityService.Get(relation.ParentId, parentObjectType, loadBaseType); yield return new Tuple(parent, child); } } /// /// 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) { //Ensure that the RelationType has an indentity before using it to relate two entities if (relationType.HasIdentity == false) Save(relationType); var relation = new Relation(parent.Id, child.Id, relationType); if (SavingRelation.IsRaisedEventCancelled(new SaveEventArgs(relation), this)) return relation; var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateRelationRepository(uow)) { repository.AddOrUpdate(relation); uow.Commit(); } SavedRelation.RaiseEvent(new SaveEventArgs(relation, false), this); return relation; } /// /// 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)); var relation = new Relation(parent.Id, child.Id, relationType); if (SavingRelation.IsRaisedEventCancelled(new SaveEventArgs(relation), this)) return relation; var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateRelationRepository(uow)) { repository.AddOrUpdate(relation); uow.Commit(); } SavedRelation.RaiseEvent(new SaveEventArgs(relation, false), this); return relation; } /// /// 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 repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork())) { var query = new Query().Where(x => x.RelationTypeId == relationType.Id); return repository.GetByQuery(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 repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork())) { var query = new Query().Where(x => x.ParentId == id || x.ChildId == id); return repository.GetByQuery(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 repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork())) { var query = new Query().Where(x => x.ParentId == parentId && x.ChildId == childId); return repository.GetByQuery(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 repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork())) { var query = new Query().Where(x => x.ParentId == parentId && x.ChildId == childId && x.RelationTypeId == relationType.Id); return repository.GetByQuery(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) { if (SavingRelation.IsRaisedEventCancelled(new SaveEventArgs(relation), this)) return; var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateRelationRepository(uow)) { repository.AddOrUpdate(relation); uow.Commit(); } SavedRelation.RaiseEvent(new SaveEventArgs(relation, false), this); } /// /// Saves a /// /// RelationType to Save public void Save(IRelationType relationType) { if (SavingRelationType.IsRaisedEventCancelled(new SaveEventArgs(relationType), this)) return; var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateRelationTypeRepository(uow)) { repository.AddOrUpdate(relationType); uow.Commit(); } SavedRelationType.RaiseEvent(new SaveEventArgs(relationType, false), this); } /// /// Deletes a /// /// Relation to Delete public void Delete(IRelation relation) { if (DeletingRelation.IsRaisedEventCancelled(new DeleteEventArgs(relation), this)) return; var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateRelationRepository(uow)) { repository.Delete(relation); uow.Commit(); } DeletedRelation.RaiseEvent(new DeleteEventArgs(relation, false), this); } /// /// Deletes a /// /// RelationType to Delete public void Delete(IRelationType relationType) { if (DeletingRelationType.IsRaisedEventCancelled(new DeleteEventArgs(relationType), this)) return; var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateRelationTypeRepository(uow)) { repository.Delete(relationType); uow.Commit(); } DeletedRelationType.RaiseEvent(new DeleteEventArgs(relationType, false), this); } /// /// Deletes all objects based on the passed in /// /// to Delete Relations for public void DeleteRelationsOfType(IRelationType relationType) { var relations = new List(); var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateRelationRepository(uow)) { var query = new Query().Where(x => x.RelationTypeId == relationType.Id); relations.AddRange(repository.GetByQuery(query).ToList()); foreach (var relation in relations) { repository.Delete(relation); } uow.Commit(); } DeletedRelation.RaiseEvent(new DeleteEventArgs(relations, false), this); } #region Private Methods private IEnumerable GetRelationsByListOfTypeIds(IEnumerable relationTypeIds) { var relations = new List(); using (var repository = _repositoryFactory.CreateRelationRepository(_uowProvider.GetUnitOfWork())) { foreach (var relationTypeId in relationTypeIds) { int id = relationTypeId; var query = new Query().Where(x => x.RelationTypeId == id); relations.AddRange(repository.GetByQuery(query).ToList()); } } 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 } }