Ksenia Posted October 27, 2021 Share Posted October 27, 2021 Hello! I am a new user of PW and I really need some guidence. :---) I use PW to design a front-end for the researchers I intern with. They logged all their database with PW (but before only used admin backend). I use Page Reference field mostly to create filters. I have followed this tutorial (very grateful for it!) and it does work as you can see at the screen recording of the test-website. But I cannot figure out how to change the inner workings of it to combine the tags user presses like Russia&&esoterism to see only the items that have both of those tags. What is even more complex: I will have multiple filters working in the same manner (page reference field) and I need all of them to also be using && logic. With possibility of clearing the history and showing all documents again. My tree structure is sth like this: -Documents -document -document -document -Tags -tag -tag -tag -Types -type -type -type etc This is my code right now. <div class="row"> <ul class="nav"> <h3> Filter by tag</h3> <li class="nav-item"> <a class="nav-link" href="/Documents/">Show all</a> </li> <?php $docTags = $pages->get("/Tags/")->children; foreach ($docTags as $docTag): ?> <li class="nav-item"> <a class="nav-link" href='<?php echo "/documents/{$docTag->name}/"; ?>' ?> <?php echo $docTag->title; ?> </a> </li> <?php endforeach; ?> </ul> </div> <div class="row py-5"> <?php // only 1 URL segment should be allowed if ($input->urlSegment2) { throw new Wire404Exception(); } // create a string that will be our selector $selector = "template=Document"; // get URL segment 1 $segment1 = $input->urlSegment1; // if there is a URL segment 1 if ($segment1) { // get cat type page $catType = $pages->findOne("parent=/Tags/, name=$segment1"); // if cat type page exists if ($catType->id) { // add this to the selector $selector .= ", tags=$segment1"; } else { // invalid URL segment 1 throw new Wire404Exception(); } } // find the pages based on our selector $docPages = $pages->find($selector); foreach ( $docPages as $docPage): ?> <div class="col-md-4 pb-3"> <div class="card"> <?php // if the page object has a featured image if ( $docPage->featuredImage): // https://processwire.com/api/fieldtypes/images/ // set some default image options $options = array('quality' => 80, 'cropping' => 'center'); // create a new image on the fly 800px wide $img = $docPage->featuredImage->width(400, $options); // get the url to the image $imgUrl = $img->url; // get the description field $imgDesc = $img->description; ?> <a href="<?php echo $docPage->url; ?>"> <img src="<?php echo $imgUrl; ?>" alt="<?php echo $imgDesc; ?>" class="img-fluid card-img-top" /> </a> <?php endif; ?> <div class="card-body"> <h4 class="card-title"> <a href="<?php echo $docPage->url; ?>"><?php echo $docPage->title; ?></a> </h4> </div> </div> </div> <?php endforeach; ?> </div> I would really appreciate any links to resources/tutorials or even general explanation about which direction to take! Thanks in advance! This forum has already helped me a lot! Best, Ksenia 1 Link to comment Share on other sites More sharing options...
Robin S Posted October 27, 2021 Share Posted October 27, 2021 Welcome to the PW forums! A URL segment is suitable for a single tag but not for multiple tags. The simplest way to submit multiple tags is via checkboxes in a form. If you want a different appearance (e.g. something that looks more like buttons instead of checkboxes) then once you have the basic form working you could hide the checkboxes with CSS and show some buttons that check/uncheck the hidden checkboxes using JavaScript. This is a generic example but you can adapt it to your scenario... At the top of your template file: // Get your tag pages $tags = $pages->find("template=tag"); // Get the array of tags that the user has submitted via the form $selected_tags_raw = $sanitizer->array($input->get('tags')); // Sanitize the tags against the valid tags $selected_tags = $sanitizer->options($selected_tags_raw, $tags->explode('name')); // Build up a selector string $selector = "template=document"; // Add the selected tags to the selector string - these will use AND logic foreach($selected_tags as $selected_tag) $selector .= ", tags=$selected_tag"; // Find the matching pages and later output them wherever you need $matches = $pages->find($selector); And later in the template file markup where you want the form to appear: <form action="./" method="get"> <?php foreach($tags as $tag): ?> <?php $checked = in_array($tag->name, $selected_tags) ? ' checked' : '' ?> <label><input type="checkbox" name="tags[]" value="<?= $tag->name ?>"<?= $checked ?>> <?= $tag->title ?></label><br> <?php endforeach; ?> <button>Submit</button> </form> Link to comment Share on other sites More sharing options...
Ksenia Posted October 28, 2021 Author Share Posted October 28, 2021 11 hours ago, Robin S said: Welcome to the PW forums! A URL segment is suitable for a single tag but not for multiple tags. The simplest way to submit multiple tags is via checkboxes in a form. If you want a different appearance (e.g. something that looks more like buttons instead of checkboxes) then once you have the basic form working you could hide the checkboxes with CSS and show some buttons that check/uncheck the hidden checkboxes using JavaScript. This is a generic example but you can adapt it to your scenario... At the top of your template file: // Get your tag pages $tags = $pages->find("template=tag"); // Get the array of tags that the user has submitted via the form $selected_tags_raw = $sanitizer->array($input->get('tags')); // Sanitize the tags against the valid tags $selected_tags = $sanitizer->options($selected_tags_raw, $tags->explode('name')); // Build up a selector string $selector = "template=document"; // Add the selected tags to the selector string - these will use AND logic foreach($selected_tags as $selected_tag) $selector .= ", tags=$selected_tag"; // Find the matching pages and later output them wherever you need $matches = $pages->find($selector); And later in the template file markup where you want the form to appear: <form action="./" method="get"> <?php foreach($tags as $tag): ?> <?php $checked = in_array($tag->name, $selected_tags) ? ' checked' : '' ?> <label><input type="checkbox" name="tags[]" value="<?= $tag->name ?>"<?= $checked ?>> <?= $tag->title ?></label><br> <?php endforeach; ?> <button>Submit</button> </form> Thank you so much! You saved me ahah I implemented it and it works well! I also made it so it includes different Page Reference fields! But another challenge for me is to use the same logic but for the Repeater fields. Each Repeater includes two Page reference fields. And I can't seem to even first print out the info I need from them (to build a selector). The structure I have looks like this: And I tried to use the code from the documentation, but it only works with simpler fields like text. Otherwise gives the id of the right page, but refuses to access the name/title of it. <h1>Relations</h1> <?php foreach($page->relation_document_technique as $relation_document_technique) { echo "Other name: {$relation_document_technique->other_name}<p>"; //gives me text echo "Technique:{$relation_document_technique->relation_document_technique_select}<p>"; //gives id of the right page echo "Type of relation: {$relation_document_technique->relation_document_technique_type->title} </p>"; //gives nothing } ?> Maybe there is a simple mistake I'm making, but I would be super grateful if you see the solution for filtering based on Repeater structure! Have a neat day! Kseniia 1 Link to comment Share on other sites More sharing options...
Ksenia Posted October 28, 2021 Author Share Posted October 28, 2021 2 hours ago, Ksenia said: Thank you so much! You saved me ahah I implemented it and it works well! I also made it so it includes different Page Reference fields! But another challenge for me is to use the same logic but for the Repeater fields. Each Repeater includes two Page reference fields. And I can't seem to even first print out the info I need from them (to build a selector). The structure I have looks like this: And I tried to use the code from the documentation, but it only works with simpler fields like text. Otherwise gives the id of the right page, but refuses to access the name/title of it. <h1>Relations</h1> <?php foreach($page->relation_document_technique as $relation_document_technique) { echo "Other name: {$relation_document_technique->other_name}<p>"; //gives me text echo "Technique:{$relation_document_technique->relation_document_technique_select}<p>"; //gives id of the right page echo "Type of relation: {$relation_document_technique->relation_document_technique_type->title} </p>"; //gives nothing } ?> Maybe there is a simple mistake I'm making, but I would be super grateful if you see the solution for filtering based on Repeater structure! Have a neat day! Kseniia Update: I have figured out that those were arrays of pages, so now I can get the values I need with this: <h1>Relations</h1> <?php foreach($page->relation_document_technique as $relation_document_technique) { $arraySelect = $relation_document_technique->relation_document_technique_select; $arrayType = $relation_document_technique->relation_document_technique_type; echo "<p>Technique:</p>"; foreach($arraySelect as $item) { echo " <li><a href='$item->url'>$item->title</a></li>"; } echo "<p>Type of relation:</p>"; foreach($arrayType as $item) { echo " <li>$item->title</li>"; } } ?> But still am lost at how to turn this into a filter checkbox! Link to comment Share on other sites More sharing options...
Ksenia Posted October 28, 2021 Author Share Posted October 28, 2021 1 hour ago, Ksenia said: Update: I have figured out that those were arrays of pages, so now I can get the values I need with this: <h1>Relations</h1> <?php foreach($page->relation_document_technique as $relation_document_technique) { $arraySelect = $relation_document_technique->relation_document_technique_select; $arrayType = $relation_document_technique->relation_document_technique_type; echo "<p>Technique:</p>"; foreach($arraySelect as $item) { echo " <li><a href='$item->url'>$item->title</a></li>"; } echo "<p>Type of relation:</p>"; foreach($arrayType as $item) { echo " <li>$item->title</li>"; } } ?> But still am lost at how to turn this into a filter checkbox! Ok sorry for hectic updating, I did figure it out! Turned out to be quite simple! //Techniques $techniques = $pages->find("template=Technique"); $selected_techniques_raw = $sanitizer->array($input->get('techniques')); $selected_techniques = $sanitizer->options($selected_techniques_raw, $techniques->explode('name')); foreach($selected_techniques as $selected_technique) $selector .= ", relation_document_technique.relation_document_technique_select= $selected_technique"; Thanks a lot for your help! 1 Link to comment Share on other sites More sharing options...
Ksenia Posted November 2, 2021 Author Share Posted November 2, 2021 On 10/28/2021 at 12:35 AM, Robin S said: Welcome to the PW forums! A URL segment is suitable for a single tag but not for multiple tags. The simplest way to submit multiple tags is via checkboxes in a form. If you want a different appearance (e.g. something that looks more like buttons instead of checkboxes) then once you have the basic form working you could hide the checkboxes with CSS and show some buttons that check/uncheck the hidden checkboxes using JavaScript. This is a generic example but you can adapt it to your scenario... At the top of your template file: // Get your tag pages $tags = $pages->find("template=tag"); // Get the array of tags that the user has submitted via the form $selected_tags_raw = $sanitizer->array($input->get('tags')); // Sanitize the tags against the valid tags $selected_tags = $sanitizer->options($selected_tags_raw, $tags->explode('name')); // Build up a selector string $selector = "template=document"; // Add the selected tags to the selector string - these will use AND logic foreach($selected_tags as $selected_tag) $selector .= ", tags=$selected_tag"; // Find the matching pages and later output them wherever you need $matches = $pages->find($selector); And later in the template file markup where you want the form to appear: <form action="./" method="get"> <?php foreach($tags as $tag): ?> <?php $checked = in_array($tag->name, $selected_tags) ? ' checked' : '' ?> <label><input type="checkbox" name="tags[]" value="<?= $tag->name ?>"<?= $checked ?>> <?= $tag->title ?></label><br> <?php endforeach; ?> <button>Submit</button> </form> Hello Robin! You really helped me out the last time in this topic and I was wondering if you could help me figure out a much more complex filter using the logic you suggested? So, basically, what I need to do is: - filter one type of pages (Organisations) based on a field in another page type (Individuals) which has connection to (Techniques) and (Organisaions) How it looks: Individual has page reference field (a repeater, but it doesn't matter in the context, hence I only need one field in this repeater) called "Techniques" and a page reference field "Organisation". My filter is by Technique. What I am doing is: 1) filtering out all individuals who have selected technique in their technique_relation field; 2) looping through them to get the Organisations they are connected to in their organisation_relation field; 3) (where I fail) trying to match titles of Organisations from action (2) with titles of the list of Organisations I am filtering to make them appear in my matches. My issue is that I overwrite the variable with titles and not loop through it, so it works, but only for the last title. My code: // Build up a selector string $selector_org = "template=Organisation"; //making global var to not get error if there are no matches $org_name = $page->title; //create second selector $selector_ind = "template=Individual"; // get my Techniques $techniques = $pages->find("template=Technique"); //Get the array of techniques that the user has submitted via the form $selected_techniques_raw = $sanitizer->array($input->get('techniques_ind_org')); // Sanitize the techniques against the valid tags $selected_techniques = $sanitizer->options($selected_techniques_raw, $techniques->explode('name')); // Add the selected techniques to the selector string foreach ($selected_techniques as $selected_technique) { //I create another selector to pick the pages from Individuals who have selected technique picked in the field $selector_ind .= ", relation_individual_technique.relation_individual_technique_select= $selected_technique"; //find matching Individuals $matches_ind = $pages->find($selector_ind); //start looping through them foreach ($matches_ind as $match_ind) { //log Individuals echo " <li><a href='$match_ind->url'>$match_ind->title</a></li>"; //for each Individual I get the organisations they are related to // (because i need to end up with the list of Organisations in the end) foreach ($match_ind->relation_individual_organisation as $relation_individual_organisation) { //get array of selected Organisaions $array_orgs_ind = $relation_individual_organisation->relation_individual_organisation_select; //loop through hem to get the names foreach ($array_orgs_ind as $item_select) { //log names echo " <a href='$item_select->url'>$item_select->title</a>"; //try to create a variable for the selector to only pick pages with same titles from my list $org_name = $item_select->title; } } } //try to use the selector, but ofc var re-writes itself and works only for the last name $selector_org .= ", title= $org_name"; } If you can see something obvious I would really appreciate help! I am kind of stuck on that now... Best, Ksenia Link to comment Share on other sites More sharing options...
Robin S Posted November 3, 2021 Share Posted November 3, 2021 @Ksenia, I read your post a few times but I'm not sure I understand what you're trying to do. A couple of things that might help... 1. The space after the equals sign in the selector here is wrong: $selector_ind .= ", relation_individual_technique.relation_individual_technique_select= $selected_technique"; It should be: $selector_ind .= ", relation_individual_technique.relation_individual_technique_select=$selected_technique"; 2. If you're trying to get a PageArray of organisations, and those organisations are referenced in a Page Reference field in individual pages then you can do something like this: // A new empty PageArray that will hold the organisations selected for all the individuals $organisations = new PageArray(); // The $individuals PageArray has been created elsewhere in your code foreach($individuals as $individual) { // Add to $organisations from the "organisations" Page Reference field $organisations->add($individual->organisations); } // Now do something with $organisations Link to comment Share on other sites More sharing options...
Ksenia Posted November 3, 2021 Author Share Posted November 3, 2021 10 hours ago, Robin S said: @Ksenia, I read your post a few times but I'm not sure I understand what you're trying to do. A couple of things that might help... 1. The space after the equals sign in the selector here is wrong: $selector_ind .= ", relation_individual_technique.relation_individual_technique_select= $selected_technique"; It should be: $selector_ind .= ", relation_individual_technique.relation_individual_technique_select=$selected_technique"; 2. If you're trying to get a PageArray of organisations, and those organisations are referenced in a Page Reference field in individual pages then you can do something like this: // A new empty PageArray that will hold the organisations selected for all the individuals $organisations = new PageArray(); // The $individuals PageArray has been created elsewhere in your code foreach($individuals as $individual) { // Add to $organisations from the "organisations" Page Reference field $organisations->add($individual->organisations); } // Now do something with $organisations Hello! Thanks for the response! As to what I am trying to do: it is convoluted because of the way pages are logged. But I tried to draw it, maybe it helps: I tried to implement your approach but now it only works if there is only one organisation inside an array! If there are more, it shows nothing. How I implemented it: // Build up a selector string $selector_org = "template=Organisation"; $selector_ind = "template=Individual"; $techniques = $pages->find("template=Technique"); $selected_techniques_raw = $sanitizer->array($input->get('techniques_ind_org')); $selected_techniques = $sanitizer->options($selected_techniques_raw, $techniques->explode('name')); // A new empty PageArray that will hold the organisations selected for all the individuals $organisations = new PageArray(); foreach ($selected_techniques as $selected_technique) { // The $individuals PageArray has been created elsewhere in your code $selector_ind .= ", relation_individual_technique.relation_individual_technique_select=$selected_technique"; $matches_ind = $pages->find($selector_ind); foreach ($matches_ind as $match_ind) { echo " <li><a href='$match_ind->url'>$match_ind->title</a></li>"; foreach ($match_ind->relation_individual_organisation as $relation_individual_organisation) { $organisations->add($relation_individual_organisation->relation_individual_organisation_select); foreach ($organisations as $organisation) { echo " <li><a href='$organisation->url'>$organisation->title</a></li>"; } } // Now do something with $organisations foreach ($organisations as $organisation) { $selector_org .= ", title=$organisation->title"; } } } Link to comment Share on other sites More sharing options...
Robin S Posted November 4, 2021 Share Posted November 4, 2021 11 hours ago, Ksenia said: I tried to implement your approach but now it only works if there is only one organisation inside an array! If there are more, it shows nothing. I think you must have some other bug in your code. When doing $some_pagearray->add() this is WireArray::add() and the argument can be a single item or a WireArray of items. So there shouldn't be any problem adding multiple pages to the PageArray like this. My two "basic_page" pages with colours in a "multiple" Page Reference field: And you can see the result: By default a Page can only exist once in a PageArray so you'll notice that the "Orange" page is not duplicated in the PageArray despite being selected on both pages. The order of colours looks a bit random but you can apply whatever sort you want by doing something like... $colours->sort('title'); ...or... $colours->sort('sort'); ...before the PageArray is output in your template. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now