Jump to content

Search form drop down category/child page output support


RyanJ
 Share

Recommended Posts

Need some help with these this filter if anyone can point me in the right direction. I altered the below from the skyscraper profile to meet my needs, but can not seem to get it to work in my case. The actual drop down on the form works properly displaying all locations, but I cant get the output to search what is selected. What am I missing?

I am trying to grab all the employees when a specific location is selected.

My structure is

root
Location 1                     
  - employee                         
    -- jane doe

    -- jone doe

Location 2

  -employee

   -- jane smith

form drop down for child page

<select name="location">
           <option value="">any</option>
            <?php
         foreach($pages->get("/")->children("id!=10") as $location) {
              $selected = $location->title == $input->whitelist->location ? " selected='selected' " : '';
              echo "<option$selected value='{$location->id}'>{$location->title}</option>";
            } ?>
</select>

output for child page

if($input->get->location) {
$location = $pages->get("template=location" . $sanitizer->pageName($input->get->location));
if($location->id) {
  $selector .= "location={$location}, ";
  $input->whitelist('location', $location->name);
}}
Edited by RJay
Link to comment
Share on other sites

I'm not sure I get the context right, but there are some things that don't look right to me.

Here you've got a malformed selector:

$location = $pages->get("template=location" . $sanitizer->pageName($input->get->location));

Should be:

$location = $pages->get("template=location, name=" . $sanitizer->pageName($input->get->location));

This seems to have no function (but could of course be in use outside the provided clip):

$selector .= "location={$location}, ";

And the dropdown should use name as a value instead of title to match the search and whitelisting (and to be unique), like this:

$selected = $location->name == $input->whitelist->location ? " selected='selected' " : '';
echo "<option$selected value='{$location->name}'>{$location->title}</option>";

Maybe these will help? I didn't take a look at the original by Ryan so I may just be missing something obvious here.

  • Like 1
Link to comment
Share on other sites

Hi Nik,

Thanks for the response and advise. The $selector is used in a function to find the results. After reading a whole lot, I saw that Ryan recommended using integers, so I replaced $location->name with $location->id.

I think the problem I am having is that I have to grab each location. In my search form, I want to just grab all results pertaining to whichever location is selected.. I have updated the above code and the structure. Maybe it will help explain a little better. Thanks again. 

Link to comment
Share on other sites

I think I need version control to keep up with the changes in your post. ;)

It seems I'm even more lost now than I was before. Which template file are those snippets from exactly, which "child page" are you referring to? And what are the templates assigned to the pages in your structure? Which template has field 'location' you're using in $selector? What else goes into $selector? Can't see the whole picture here...

At least you've got a nice mess there as you're using id, name and title like they were the same. What I mean is

  1. there's page id as the value for an option in the dropdown
  2. you're whitelisting page name as location
  3. check for previously chosen item uses page title

Those three should be done using the same property, preferably id or name.

  • Like 1
Link to comment
Share on other sites

haha, Yes, version control would be nice and a mess is kind words for what I have and I do sincerely appreciate your patience. This is day 4 of figuring out my mess.  

 
1. there's page id as the value for an option in the dropdown
2. you're whitelisting page name as location
3. check for previously chosen item uses page title

I fixed this part as I realized it was not correct by any means and I'm a silly person for not realizing it.

The first snippet is from searchform.php which contains the drop down. So in the drop down are the options

"location 1"

"location 2"

The second is from search.php and is supposedly my logic to process the selected location.

My goal is in my search results is to output all employees that are children of its selected location. 

Location 1 

Location 2

are both assigned the same template named "location"

Each employee is assigned a template named "employee"

The employee template does not have a field of location, they are just children of their location. Here is a post of my complete structure. It may help you understand my logic and once again, I appreciate your time as I am trying to learn pw, so I dove in deep.

Link to comment
Share on other sites

OK, now we're back on track I think. :)
 
So, you've lined up those three things to use page id's? Then your search form would be like this:

<select name="location">
    <option value="">any</option>
    <?php
    foreach($pages->get("/")->children("id!=10") as $location) {
        $selected = $location->id == $input->whitelist->location ? " selected='selected' " : '';
        echo "<option$selected value='{$location->id}'>{$location->title}</option>";
    }
    ?>
</select> 

And now you could use something like this in search.php:

# has a location been selected?
if($input->get->location) {
    # sanitize (page id is always an integer)
    # no need to fetch the actual page as the search selector restricts results to employees
    $location = (int)$input->get->location;
    # employees have template 'employee' and their parent page is the chosen location
    $selector .= "parent={$location}, template=employee";
    # whitelist the sanitized value (and use from whitelist later on)
    $input->whitelist('location', $location);
}

Hope I didn't introduce any new errors there...

  • Like 2
Link to comment
Share on other sites

Hi Nik,

Definitely leaps and bounds from where I started, but I am getting no results found if a location is selected. The correct id's are showing in the url, so I am assuming it has something to do with the $selector maybe? Thank again for helping me out and cleaning up my mess :).

Looking at it further, I think because "Location 1" is not the actual parent of the list of employees, but "employee". Is there anyway to go one step down further? I guess I could just delete the employee container.

Edited by RJay
Link to comment
Share on other sites

Doh, I got that structure of yours wrong, sorry! So you're right, using parent wouldn't give the right results.

Yes, you could get rid of the container if there's no real need for one. But if that extra level is needed, you can change "parent" in the selector to "has_parent". Then the chosen location has to be one of the parents of the employee, but not the first one necessarily.

If it still doesn't work, try echoing $selector right before it's being used for finding the results so that we can see if there's still something wrong with it. And you do have $config->debug = true; in your site/config.php and no errors in site/assets/logs/errors.txt, right?

We'll get there, eventually. :D

Link to comment
Share on other sites

Hi Nik,

ahhhhh, you are a kind soul and life saver! :)    Yes, I have debug on and has_parent seems to work with no errors and although I only have two locations now the proper results are appearing. I did not see has_parent in the selectors cheat sheet? Any other selectors out there not on it? I cannot thank you enough for your help. I owe you BIG!

Link to comment
Share on other sites

Thanks, glad I could help you RJay! :)

I don't know why has_parent isn't in the cheatsheet (Soma/Ryan?) as it is mentioned in the API documentation (http://processwire.com/api/selectors/). There are one or two other selectors that are not mentioned in either of those I'm sure, but I think it's intentional as they're only for internal use for a reason or another. So everything you really need to know is documented ;).

Happy studying with ProcessWire and don't give up if you ever hit a wall. Ask away and someone will help you for sure. But do try and describe the context and what you're trying to achieve detailed enough and there will be a solution in way less than four days next time. :P

  • Like 1
Link to comment
Share on other sites

Hi Nik, 

I can not thank you enough. My first trip to Finland will be to buy you some beers or food or whatever. I will heed your advise and be sure I describe my problem thoroughly before I post it to be sure it is understood completely from others perspectives other then mine. I thought I had a good grasp on what I was trying to learn/achieve as this is just a mock site for me to learn from, but I was a tad bit over my head. 

I still have one other part of the form that I need to tackle, but I will attempt to achieve those results a few more days before asking for help. Thanks again and cheers!

  • Like 1
Link to comment
Share on other sites

Just a follow up question if I may. What logic would you recommend to only show the available selectors in the search form per location. For example

Location 1

  --employee

    --- Jane Doe

  --department

    --- Newbie

 -- year

   --- 2012

location 2

  --employee

  --- John Doe

 --department

  --- Newbie Trainer

 --year

   ---2010

Department children pages and year children pages are page fields and are both drop downs in the current search form. So when I am on the location 1 page, currently it shows all children of department and year from both locations.

Is it possible to only show the children of the location you are one. So if I am on the location 1 page, the search forms drop down would only show the Newbie department and the year 2012. 

If in location 2, the search form drop down would only show department Newbie Trainer and Year 2010. 

Hopefully I explained this well.  :undecided:

Link to comment
Share on other sites

Yes, explanation accepted :D.

OK, see above how the selector is formed to find the search results; there you're searching for employees under the chosen location. And now you need to search for department/year under current location (and list those as options in the dropdown). Anything familiar there? ;)

The biggest difference - the only one you haven't solved already - is chosen vs. current location. You do know about $page variable holding the page object of the current page, right? So what you need is a way to use the $page variable in your selector somehow.

Currently you're probably using something like $pages->find('template=department') to list options, correct? Now you need to add another part to that selector constraining the departments to your current location. As the same search form is used elsewhere as well (front page at least) you'd want that location constraint only when you actually are on a location page. Something like this should do the trick:

$departmentSelector = 'template=department';
# variable holding a page object in a string enclosed in double quotes is interpolated to id of the page
if($page->template == 'location') $departmentSelector .= ", parent=$page";
$departments = $pages->find($departmentSelector);

And same for years. Got it?

  • Like 1
Link to comment
Share on other sites

Hi Nik,

As you suggested, I am currently using this in searchform.php to pull in all of the departments in all locations. 

<?php 
    foreach($pages->find("template=department") as $department) {
    $selected = $department->id == $input->whitelist->department ? " selected='selected' " : '';
   echo "<option$selected value='{$department->id}'>{$department->title}</option>"; }
?>

I took your snippet and altered it to this but with no luck. I am not getting any results with it.

<select name="department">
  <option value="">Any</option>
     <?php
       $departmentSelector = 'template=department';
         if($page->template == 'location') $departmentSelector .= ", parent=$page";
         foreach($pages->find($departmentSelector) as $department) {
         $selected = $department->id == $input->whitelist->department ? " selected='selected' " : '';
         echo "<option$selected value='{$department->id}'>{$department->title}</option>"; }
      ?>
  </select>

I  was initially thinking of an if/ elseif statement or better a switch to check and see what location page the user was on and then just grab the locations department/year children, but If there are a lot of locations, this could be very time consuming and as always, thanks for educating us newbies :).

Link to comment
Share on other sites

Oops!... I Did It Again  :P

I somehow thought departments (and years) were direct children of locations, but there's this extra container there as well. So try changing parent to has_parent and it should work better.

Link to comment
Share on other sites

Hi Nik,

Strangely enough I had tried the has_parent last night after I learned it from your first lesson, but it did not work. I was either tired, delusional or thinking I misspelled location because it works perfect this morning.   :)

So to break down the code to make sure I understand it, 

$departmentSelector is grabbing all pages with the department template

the if is checking if the current page is using the template location and if so, it is grabbing that pages id and appending it to $departmentSelector?

You sir have excelled as my hero to now my Super Hero!

Link to comment
Share on other sites

Great, we're clearly making progress. Looks like you've understood it as well - and that's the ultimate goal when helping out. :)

Understanding selector formation is if not a necessity at least the key to unleashing the vast possibilities of ProcessWire. So to recap: $departmentSelector is initialized to a selector string that would match all the pages with template 'department'. Then, if current page has template 'location', the initial selector string is concatenated with another selector that would match all the pages having the current page as one of their parents. Comma separated selectors all have to match so that the whole selector matches (comma stands for 'and'). Thus with both of those selectors only pages with template 'department' that have the current page as one of their parents would match.

Still that selector string that has to be passed to a find() method to actually get the pages (or get() when only a single page is wanted). Using find() method from $pages object you're aiming at all the pages in your site that match the given selector string. But there are also other places where there's a find() method available. To restrict finding to a specific subtree you can first get the wanted root page and call find() for that page object. Or if you want to filter further a previously retrieved or formed PageArray, they also have a find() function. But do note that this one's different to the previous examples as the page objects are already in memory and the filtering happens there instead of the database.

But now I'm way off the original subject. I was supposed to describe things just a little bit deeper but got carried away... :P

Link to comment
Share on other sites

Hi Nik, I appreciate your detailed explanation and gain to absorb as much as possible, so please continue to get carried away. You answered a second question I had in my head but never asked. So the find() method gets pages and the get() gets a page? Again Sir, I cannot thank you enough. :)

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Similar Content

    • By donatas
      Hello,
      how would I do a multi-language website search with just a selector?
      I have many multi-lang fields and I want to do a search through all of them at once and through all of their language values.
      Is there a "selector way" of doing this? Maybe something like `title|title:de|title:it`? It seems I have seen this somewhere a long time ago but can't find in any documentation or forum search...
      Or the only way of doing it is by running separate searches for each language with output formatting off and then consolidating it all in one single results array?
      Because I still want to give users a result, even if it is in another language than current $user. Visitors mostly will be searching for specific terms that are very similar in all languages, but might be not used in one language version of a single page, for example. Or the user might not have switched language tohis prefered and did the search first, etc.. (many use cases in my situation)
      Example:
      $pages->find('title~='.$q) - maybe different operator is needed? /en/search/?q=visit = 1 results /it/search/?q=visit = 0 results Thanks for any advice!
    • By sebr
      Hi
      In my search page, I used a selector like this :
      $searchQuery = $sanitizer->entities($input->get('q')); $searchQuery = $sanitizer->selectorValue($searchQuery); $selector = 'title|subtitle|summary|html_body_noimg~=' . $searchQuery; $matches = $pages->find($selector); I don't have the same results if $searchQuery contains accent or not.
      For example,
      with « bâtiment » I have no result with « batiment » I have onea result : « Les bâtiments et les smart-city » Normally I should have the same results? How can I do that ?
      Thanks for your help
    • By michelangelo
      Hello guys, I am building a sort of an archive. Relatively simple, although I have about 8000 records, each with 15 fields (text, int, images, url). I created a crude search system with a form (emulating the famous Skyscrapper example) to filter through the system. Everything works but it is quite slow... I have 2 questions which are related:

      1. How can I search through the database?
      2. What is a good practice to display many records like these?
      -----------------------------------------
      1. I am retrieving the results with
      $songs = $pages->findMany('template=nk-song'); Then I do a foreach to render them all. I am unsure if that is a good way. If I render all of them on the page, it creates thousands of divs with a bit of text, and this can take a while (10s-15s).
       
      2. This one is even worse :D as every time I retrieve my desired records with something like this:
      $page->find("field_to_search_through~=my_query_string") I get between 20 and 200, but when I render them I am creating iframes with YouTube videos and that can take up to 10s to finish. I "solved" it by only loading the iframes if they are in view with IntersectionObserver on the client-side. But I feel there is a more precise PHP / ProcessWire approach.
       
      Just to clarify, I started doing all of this custom rendering and querying because tools like ElasticSearch or SearchEngine were a bit complicated and I needed a simple to retrieve information and then display it in my own way.
      Thank you!
    • By snobjorn
      I have a website with multiple content types that I want to be accessible through search. I really like the live search on processwire.com, that sorts content types while typing. I tried to find the code to recreate this, with no luck. Does anyone know if this is jquery, specific jquery plugins, json/xml cached files, and what kind of PHP code is used? Any tip that point me in the right direction would be much apperciated.
      The search result listing seems fairly easy to create with sorting through parameters.
    • By jacmaes
      I have a series of videos, and the following search form (translated into English here) that allows to filter these videos on the frontend:

      I've built a few of these search forms, but only with text fields, selects and radio buttons. Here I'm using an array with checkboxes ("Level" field above), and it's causing me grief when I try to paginate these results. I've done a lot of searching in the forum and spent too many hours to try to get it to work. Here's how I'm building the selector:
      <?php if(count($input->get)): // Level is an array. Code adapted from Ryan's snippet here: // https://processwire.com/talk/topic/3472-enable-pagination-for-search-results/?tab=comments#comment-38042 if($input->get->level) { $level = array(); foreach($input->get->level as $id) $level[] = (int) $id; // sanitize to INTs $level = implode('|', $level); // convert to 123|456|789 string, ready for selector } else { $level = ''; } $data = array( 'training_type' => array('=', (int) $input->get->training_type), 'duration' => array('=', (int) $input->get->duration), 'level' => array('=', $level), 'limit' => array('=', (int) $input->get->limit) ); $selector = ''; // iterate through the $data we made above to create a selector string foreach($data as $field => $a) { list($operator, $value) = $a; if(empty($value)) continue; // send value to the whitelist so that it can be used in pagination $input->whitelist($field, $value); // append to our selector string $selector .= "$field$operator$value, "; } $videos = $page->children("$selector"); When I hit search, I get the expected results. So far so good. The GET parameters are the following with the options selected in the screenshot above:
      videos/?level[]=1476&level[]=1477&training_type=1473&duration=1485&limit=10 $selector echoes the following as the "level" field is an array with a pipe character:
      level=1476|1477, training_type=1473, duration=1485, limit=10 Now, when paginating these results, the following page (page 2) shows these GET parameters:
      videos/page2/?level=1476|1477&training_type=1473&duration=1486&limit=10 And I think that's where the problem lies. The "level" field is "lost" and I'm getting more results than expected on subsequent pages. If I manually add "page2" to the initial results in the URL, just to test, everything works fine:
      videos/page2/?level[]=1476&level[]=1477&training_type=1473&duration=1486&limit=10 But how can I achieve this in code? Do I need to revert to "level[]=1475&level[]=1477" instead of "level=1476|1477" for the pagination to work correctly, and can you PHP gods illuminate me?
      Any help would be really appreciated, really.
×
×
  • Create New...