Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The best way to relate hierarchical structure #4494

Closed
dodyg opened this issue Oct 8, 2019 · 36 comments
Closed

The best way to relate hierarchical structure #4494

dodyg opened this issue Oct 8, 2019 · 36 comments

Comments

@dodyg
Copy link

dodyg commented Oct 8, 2019

For a structure such as

Sector > Program > Project > Albums, what is the best way to design the relation between them?

I think using the container/list will make the UI too complex because of the nested structure. Right now the way I think about it is to create a text field (with predefined input) for each children content to relate to their parents. The problem of course this list needs to be maintained manually.

text-input-list

Therefore

  • Program will have ProgramSector text field
  • Project will have ProjectProgram text field
  • Album will have AlbumProject text field

I wonder if there's a better way.

@dodyg
Copy link
Author

dodyg commented Oct 8, 2019

Ok it looks like the better way is simply to do

  • Sector -> ListPart of Program
  • Program -> ListPart of Project
  • Project -> ListPart of Album

This type of content editing experience can really use breadcrumbs (#3687)

@jptissot
Copy link
Member

jptissot commented Oct 9, 2019

You can also use the ContentPickerField.

@hishamco
Copy link
Member

What about Tags & Taxonomies?

@dodyg
Copy link
Author

dodyg commented Oct 10, 2019

ContentPickerField

Oh I didn't know this. Excellent.

Tags & Taxonomies

Sector > Program > Project > Albums. It's hard to accommodate this using taxonomies. The relations are direct.

@hishamco
Copy link
Member

Sector > Program > Project > Albums. It's hard to accommodate this using taxonomies. The relations are direct.

Ya if it represents an entity aka content type, so ContentPickerField is the a good option

@dodyg
Copy link
Author

dodyg commented Oct 10, 2019

Wouldn't using List a more natural? Is there any downside of using List that I am not aware of?

@jptissot
Copy link
Member

I don't think there are downsides. The only thing content picker field allows you to do is reuse a piece of content in many parents. For example, I am using the same form in many pages so I reference the form with a content picker.

@jptissot
Copy link
Member

There is also a taxonomy picker field I believe.

@dodyg
Copy link
Author

dodyg commented Oct 10, 2019

OK so we can use:

  • ListPart
  • ContentPickerField
  • Taxonomy

for relating contents. Do I miss anything else?

@hishamco
Copy link
Member

It would be nice to know when choose what, for instance Use Taxonomy for create tagging, ListPart for ... etc

@jptissot
Copy link
Member

@dodyg There is also a LocalizationSetContentPicker for multilingual content.

Use case:

Event that has LocalizationPart.
Team that does not have a LocalizationPart but relates to the event.
Team would have a LocalizationSetContentPickerField that points to the event to be able to get the localized version based on the current culture when rendering.

@JoshTango
Copy link

I have been greatly interested in this ability for a while now, its important to hammer this out.
One of the other issues is if I am on a child how do I discover my parent?

@dodyg
Copy link
Author

dodyg commented Oct 22, 2019

@JoshTango

parent-2

The highlighted part is the content id of the parent, then you can use

{% assign my_content = Content.ContentItemId[Model.ContentItem.Content.ContainedPart.ListContentItemId] %}

to obtain the parent info.

or better

{% assign my_content = Model.ContentItem | container %}

For more info, check here (https://orchardcore.readthedocs.io/en/dev/OrchardCore.Modules/OrchardCore.Liquid/#container)

@jptissot
Copy link
Member

@dodyg this is specific to Lists. For the content picker(s) the code is a bit different see:
https://orchardcore.readthedocs.io/en/dev/OrchardCore.Modules/OrchardCore.ContentFields/#contentpickerfield

@jptissot
Copy link
Member

jptissot commented Nov 6, 2019

@dodyg how did you end up implementing this one ?

@dodyg
Copy link
Author

dodyg commented Nov 7, 2019

I am using ListPart for now but I am not sure whether this will be the final decision. I am still exploring.

@jptissot
Copy link
Member

jptissot commented Nov 7, 2019

I am looking to do something similar as well. @sebastienros suggested I try with a taxonomy to define the hierarchy and just select the taxonomy term with a field in my types. This keeps all types flat but allows you to define a hierarchical structure of content without having lots of nesting.

@arkadiuszwojcik
Copy link
Contributor

@dodyg in your hierarchical Content Items with ListPart are you also have experiencing this #4749 (see second comment) ?

@dodyg
Copy link
Author

dodyg commented Nov 8, 2019

@jptissot that looks like an interesting approach actually. I wonder how it will look like in being able to query them though.

If my hierarchy is Sector > Program > Projects, I will need to show

"Sector : Social Empowerment"

  • Program A
  • Program B
  • Program C

@dodyg
Copy link
Author

dodyg commented Nov 8, 2019

@arkadiuszwojcik I haven't gone far in this. I will keep you updated.

@jptissot
Copy link
Member

jptissot commented Nov 8, 2019

I believe there is a way to get all ContentItems that are tagged with a Taxonomy term. We would need to investigate and try.

My use case was a Knowledge Base. So I had a Knowledge Base Article, which would be tagged with a Category.

@dodyg
Copy link
Author

dodyg commented Nov 14, 2019

This is how to load related content based on Taxonomy

anatomy-of-taxonomy

@dodyg
Copy link
Author

dodyg commented Nov 14, 2019

So far, for this type of structure Sector > Program > Project > Albums., ListPart is the most obvious way to structure them.

  • Sector has ListPart of Program
  • Program has ListPart of Project
  • Project has ListPart of Albums

The problem with this arrangement though, when you are in the child content editor UI, you don't know what the parent is. It's not displayed.

So if I am editing a 'Program', I have no idea what Sector it belongs to.

@dodyg
Copy link
Author

dodyg commented Nov 14, 2019

The problem of using Taxonomy for Sector > Program > Project > Albums. structure can be seen here.

taxonomy

  • The first level in the hierarchy is Sector
  • The second level is Program

This display is a taxonomy field in the "Project" content type. As you can see from the UI, the field shows the whole hierarchy. User can pick the taxonomy term that is meant for "Sector" because there is no way to limit that user can only select "Program" taxonomy term.

@sebastienros
Copy link
Member

there is no way to limit that user can only select "Program" taxonomy term.

Isn't there? At least in Orchard 1 we have an option to define which level of the taxonomy can be selected. Don't we have it here too? If not we could add it. I am pretty sure we can already limit to "leaves" (last level)

@dodyg
Copy link
Author

dodyg commented Nov 15, 2019

At least in Orchard 1 we have an option to define which level of the taxonomy can be selected.

This doesn't exist at the moment

I am pretty sure we can already limit to "leaves" (last level)

Yes this exists

@jptissot
Copy link
Member

jptissot commented Nov 15, 2019

I assume it would be useful for your use case if the Taxonomy picker field could also be limited to display a subset of terms

@dodyg
Copy link
Author

dodyg commented Nov 16, 2019

Yes. I can think of two ways:

  • Filter by one or more levels, e.g. show level 2, 3 and 4 here.
  • Filter by one or more of the properties of the Taxonomy Term properties, e.g. show me all the taxonomy terms that has {Type : "Project"} .

@dodyg
Copy link
Author

dodyg commented Nov 16, 2019

The larger issue here is finding the best way to relate one content to another.

Parent > Child (List Part)

Right now to associate a child content to a parent via ListPart is to create the child content through the List Items interface of the parent Content Item. This is very restrictive. There is no way current to create a child content and associate it to the parent item.

@JoshTango
Copy link

I'm going to throw in a different idea. In the entity framework system if I have 2 entities with a relationship to each other we actually have 3 objects. 2 for the entities and 1 for the relationship. I had thought about making a content definition type just for the relationship. This is actually best for many to many relationships.

I don't believe any of this relationship stuff was thought through properly when Orchard Core was designed. The issue here is that I bet orchard core attracts a lot of developers where as drupal and wordpress attract a lot of web designers. We are trying to be developers with orchard core which is good.

@dodyg
Copy link
Author

dodyg commented Nov 16, 2019

I am trying to use Content Picker at the moment for 'Sector > Program > Project > Albums'.

So

  • Sector has no Parent
  • Program has Parent Field Content Picker of Content Type Sector or Program.
  • Project has Parent Field Content Picker of Content Type Program.
  • Album has Parent Field Content Picker for Project.

So this solves the issue of editing child content and not knowing the parent (ListPart has this problem).

The problem here the parent doesn't know its children. So it's a reverse situation. It's not a deal break because you can always query this information easily at the display level although admin support would be amazing.

@dodyg
Copy link
Author

dodyg commented Nov 18, 2019

Summary of approaches to structure hierarchical content.

The structure required is Sector > Program > Project > Albums.

ListPart

This approach allows a Content Item to have children contents. Each child content is stored on its document.

To achieve our required structure, we need to do the following:

  • Sector has ListPart of Program.
  • Program has ListPart of Project.
  • Project has ListPart of Albums.

The Good Part

  • The Admin section automatically create the interface to list the children contents.
  • The Admin section automatically associate the parent content with the children content.
  • From the children shape you can refer to the parent ContentItemId via ContainedPart.ListContentItemId. This way when you are accessing a child content, you can easily obtain the information about the parent.
  • Since the child content is stored separately (in contrast of Bag), you can obtain and process them individually.

The Bad Part

  • When you are editing the child content, there is no information available at the UI about the parent content (contained type)
  • You can't switch parent content. If your program A belongs to Sector B, you cannot switch to Sector C from the editor.
  • You can only have one ListPart per parent Content Type. If you have a Content Type that multiple list like behaviors, this can be a problem --- you have to resort to Bag, which embeds the children contents with the parent content.

ContentPickerField

This feature allows you to associate a content with another piece of content filtered by their Content Types. This makes it very versatile.

You can use ContentPickerField to model a child parent relationship. Note that the parent content has no idea that it has child contents. The information about the relationship is only stored at the child content.

To achieve our required structure, we need to do the following:

  • Sector has no Parent
  • Program has Parent Field Content Picker of Content Type Sector or Program.
  • Project has Parent Field Content Picker of Content Type Program.
  • Album has Parent Field Content Picker for Project.

The Good Part

  • Since the child content is stored separately , you can obtain and process them individually.
  • Since the child content has information about the parent content, you can obtain the information about the parent easily.
  • When you are editing a child content, it is very clear which one is the parent content.
  • You can easily switch one parent content with another.

The Bad Part

  • There is no Admin support regarding listing of child content by their parent content unlike ListPart approach.

Taxonomy

Taxonomy allows you to associate one content with another loosely. The taxonomy will represent the hierarchy and you associate your content with the relevant term within the taxonomy.

taxonomy

The Good Part

  • You can organize your hierarchy in one place.
  • You can obtain the organization of your content from a single query. This facility is not available with any other approach.
  • From your child content you can easily access the taxonomy and the related term(s) it is associated with.

The Bad Part

  • It's awkward to build hierarchy between content this way. You have to duplicate a lot of things. To associate parent content A with child content B, you would have to associate content A with a term X in the taxonomy and content B with term Y and then organize term X and Y accordingly in the taxonomy tree.

@willnationsdev
Copy link

willnationsdev commented Feb 13, 2021

I really like the design of the ContentPickerField solution. How about this?

Edit: Simplified proposal using Menu.

  1. Add a ParentContentPart ContentPart.
    1. ContentPickerField "Menu". Configure for Menus.
    2. ContentPickerField "Parent'. Configure for whatever you want.
  2. Make a ParentContentService that has a method to take in 3 ContentItemIds: a Menu, a Parent, and a Current item. Fetch the Menu. Update the Menu's MenuItemList's ContentMenuItem for Parent to ensure it has a ContentMenuItem for Current as a child. Reparent/delete/create menu items as needed for this.
  3. Make a ParentContentPartDisplayDriver that updates the menu using the service whenever the part in the current item is updated.
  4. Make another ContentPartDisplayDriver<MenuPart> that, on update, gets its MenuItemsListPart and compares the content within for changes. Build a list of reparenting operations that must be performed. Then fetch the content items, update their parent parts accordingly, and save them.
  5. Optionally define indexes to more quickly find associations between parent-child ContentItems (not the menu items) or fetch all the ContentItemIds associated with a subtree of a menu's menu items.

Example uses:

  • Admin
    • Have alternative ContentItem management pages in the admin that render a tree structure (e.g. indents rows of tabular data based on relationships, etc.). Or just apply indents to the existing pages using data from the Menu.
  • Frontend
    • Easily use placements to append a shape to the bottom of ContentItems with a ParentContentPart that builds a "child pages" widget.
    • Generate breadcrumb menus dynamically using the path in the tree to the current page.
    • Generate a sidebar navigation menu, optionally locally scoped to the current page and its children.

Thoughts?

@ricocsharp
Copy link

Is there any examples of how to use this Content Picker Programatically in Headless recipe

@OrchardCMS OrchardCMS deleted a comment from ricocsharp Sep 17, 2022
@OrchardCMS OrchardCMS deleted a comment from ricocsharp Sep 17, 2022
@OrchardCMS OrchardCMS deleted a comment from ricocsharp Sep 17, 2022
@Skrypt
Copy link
Contributor

Skrypt commented Sep 17, 2022

I think that ultimately we would need to adjust the UI of the ListPart to allow moving content items from one list to another. Also, allow adding content items that are already created in a list.

@dodyg I like what you did there, it should surely be copied over to a discussion "Show and Tell" as a small guideline to make it more evident for everyone.

@Piedone
Copy link
Member

Piedone commented Apr 25, 2024

@Piedone Piedone closed this as not planned Won't fix, can't repro, duplicate, stale Apr 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants