Hierarchical multi select checkboxes for taxonomy?

I would do it the other way around as described in my previous post:

So you don’t set the other related pages on the page that represents the whole subcategory. Instead, you add the subcategory on all pages that belong to the subcategory. Basically you take a row of the table (a product page) and add every subcategory that contains an x in the table to the tags field.

Another idea that is probably more flexible in the future:

  1. Create a page /categories.
  2. Create a page for each category.
  3. Create a page for each subcategory as child of the category.
  4. Add two fields to it: A select field to select the destination page for the overview and a tags field, this time with the pages that belong to the subcategory.

Advantage: A product can be the destination for more than one subcategory and you can easily add new (sub)categories.
Disadvantage: Same problem with the tags field: You need to enter the URI of the page.

can i hide /categories from editors?

Unfortunately no. You can hide all subpages, but not a specific one.

But if the categories don’t need to be flexible and easy to change, this solution doesn’t make much sense anyway. My other suggestion doesn’t use extra pages. But: Because a page can be the destination of multiple subcategories, you will need to use two tags fields (one for the subcategories this page is destination of and one for the other subcategories) instead of one select and one tags field.

Sad times… to bad that checkboxes don’t support hierarchies :smile:, like in wordpress.

My client just said that it would also be an option for him to just have something like a tags field for all related pages…

Can i create a tag cloud from the products? Or maybe just restrict options to the product pages to make it more user-friendly?

The checkboxes are simply not meant to be used for that complex and large selection structures.

Another idea came to my mind: Instead of the two tags fields, you could also use one structure field that allows 0 to n items each containing a select field to select a subcategory and a toggle to select whether this page is the destination for this category:

fields:
  categories:
    label: Categories
    type: structure
    entry: >
      {{subcategory}}<br />
      Destination page: {{destination}}
    fields:
      subcategory:
        label: Subcategory
        type: select
        options:
          ...
      destination:
        label: Should this page be the destination page for this subcategory?
        type: toggle
        text: yes/no

This would be quite user-friendly and at the same time allow to make one page the destination for multiple subcategories.

1 Like

the related pages would still be different for every landing page :confused:

some products are landing page for two categories with different related pages… :sob:

Well, yes, but why select the related pages manually instead of filtering by category and subcategory?

i guess it’s either adding the categories and landing pages for every product page
Or the related pages for every page… since the “loesungen” already have a structure…

Forget about the “related pages”. You can get them in your template by filtering all product pages by the categories structure field. If this field contains the same category as the current page, it’s related.

It’s like with relational database systems. You can store the categories with the pages or the pages with the categories. You have to decide for one, but in this case the first makes more sense.

Exactly, because if you add related pages and you add a new product, you will have to touch every product and change the related pages.

1 Like

Hello everyone, so i got stuck with this again:

I integrated the structure field as proposed by @lukasbestle and attached some products to their categories and subcategories. The category options for the subcategory select field are named after the following scheme: Category/Subcategory.
Now i want to edit the front-end template and display the categories and their subcategories. The subcategories must be listed unter the categories and only appear once. Furthermore each subcategory must be a link to the regarding product page where destination = true.

Since the documentation on the structured field is a little limited i figured sth like this:

 foreach ($solutions->children()->children()->index() as $solution){
    // necessary information from all product pages in an array
    $content = yaml($solution->categories());
    ...
 }

i have a controller that searches the solution page, and return it as collection $solutions . But $solutions also include the substructure therefore i query children()->children() .

Thanks everyone! :slight_smile:

Edit: my Solution so far:

    <?php 

// All solution pages..
$solutions;
$blocks = array();
// The sorted Categories with url and such...
foreach ($solutions->children()->children()->index() as $solution){
    
// Page content definded by user
$content = $solution->categories()->yaml();
// This solutions URL
$url = $solution->url();

    // loop through content
    foreach($content as $value){
      if ($value['destination'] == 'true' ){
              $categories = explode("/", $value['subcategory']);
              $category = $categories[0];
              $subcategory = $categories[1];
              $blocks[$category][$subcategory] = $url;
      }
    }
    
  }
  
  //Print the whole thing....
  foreach ($blocks as $block=>$subcategory){
    echo "<div class='sm-col sm-col-12 md-col-6 lg-col-3 p2'>";
    echo "<div class='p2 bg-olive'>";
    echo "<h1>$block</h1>";
    echo "<ul>";
    foreach ($subcategory as $category => $link){
      echo "<li><a href='$link'>$category</a></li>";
    }
    echo "</ul>";
    echo "</div>";
    echo "</div>";
  }
?>

maybe someone has some suggestions how to do this more efficient / better? : )

Your code looks like a quite good start.

You can however also filter by template instead of limiting to a specific parent page:

foreach ($solutions->index()->filterBy('template', 'solution') as $solution) {
...

This will get all pages (even sub-sub-sub-pages of $solutions), but only those that use the template solution.php.

In the loop you then need to iterate through the items from the structure field and build an index of the categories. Here’s some untested example code:

Controller code

$categories = array();
foreach ($solutions->index()->filterBy('template', 'solution') as $solution) {
	$solutionCategories = $solution->categories()->toStructure();
	
	foreach($solutionCategories as $category) {
		list($mainCategory, $subCategory) = explode('/', $category->subcategory());
		
		// Add sub category to array
		if(!isset($categories[$mainCategory][$subCategory])) {
			$categories[$mainCategory][$subCategory] = false;
		}
		
		// Set page if set to be destination
		// Check whether a destination has already been set (makes sure the first match is being used)
		if($category->destination() === 'true' && $categories[$mainCategory][$subCategory] === false) {
			$categories[$mainCategory][$subCategory] = $solution;
		}
	}
}

// Sort columns alphabetically
ksort($categories);

Template code

// Print columns and rows
foreach($categories as $mainCategory => $subCategories) {
	// Pseudo markup
	echo '<column>' . $mainCategory;
	
	// Sort rows alphabetically
	ksort($subCategories);

	foreach($subCategories as $subCategory => $destination) {
		echo '<row><a href="' . $destination->url() . '">' . $subCategory . '</a></row>';
	}
	
	echo '</column>';
}

Edit: Oh, you were faster. I hope my example helps you anyway. :smiley:

1 Like

You will also need some code to fetch the related pages with the same subcategories. Here’s an example for that:

// Get a list of the categories of this page
$categories = $page->categories()->toStructure()->pluck('subcategories');

// Filter all solutions by the current categories
$related = $pages->index()->filterBy('template', 'solution')->filter(function($solution) use($categories) {
	$solutionCategories = $solution->categories()->toStructure();
	
	foreach($solutionCategories as $category) {
		// Check if this subcategory is shared with the current page
		// If yes, the pages are related
		if(in_array((string)$category->subcategory(), $categories)) return true;
	}
	
	// No match, no relation
	return false;
});

:joy:

Thanks for the effort though! :slight_smile: I thought it would be sad not to try it until i get an answer ;). Your solution looks a little cleaner though!

Now i will crosscheck and see where i can improve my solution!

and thanks for the snippet for the related pages, i will see how i can incorporate it into my solution :wink:

Thanks :smiley:

You are welcome. :smile:

Perfect, so i got everything working as it was supposed to,
i got one last question though:

I need to have an english translation for the Categories/SubCategories, where could i add those? :slight_smile:
Inside the language folder? What would be the key? Would it be the value saved in the blueprint?

Btw. here is the code i used for the related pages: maybe some tips, where i could be more efficient?

 // Get a list of the categories of this page
 $filter = urldecode(param('filter'));
 $related = array();
 
 if (!is_null($filter)){
   $solutions = $pages->index()->filterBy('template', 'solutions');

   foreach ($solutions as $solution){
   		$Categories = $solution->categories()->toStructure()->pluck('subcategory');

		foreach ($Categories as $category){
                        // this still looks ugly xD
			list($mainCategory, $subCategory) = explode("/", $category);
			if ($filter == $subCategory){
				$related[] = $solution;
			}
		}
   }
}