Jump to content

Search form with multiple fields


Recommended Posts

I have a small search form that searches products by selected manufacturer, price or manufacturer and price combined.

Manufacturer is a page list field, and the generated prices are a range.

This is the form:


<label for="search-manufacturer">Manufacturer</label>

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

<label for="search-price">Price</label>

<select id="search-price" name="price">
<option value="">Any</option>
<!--?php
foreach(array('0-50', '50-250', '250-500', '500-750', '750-1000', '1000+') as $price) {
$selected = $price == $input--->whitelist->price ? " selected='selected'" : '';
echo "$price";
}
?>
</select>

<input type="submit" id="search-submit" name="submit" value="Search">

What code do I need to put into "manufacturer-search" template to search products by criterions mentioned above?

I've looked into the skyscapers profile but couldn't make it work...

Thanks

Link to post
Share on other sites

I'm not sure there's enough to go on here to say for sure. Where are you setting the $input->whitelist values? That's probably the same place where you want to build your search selector. Something like this:

$selector = 'parent=/products/, ';

$name = $sanitizer->pageName($input->get->manufacturer);
$man = $pages->get("parent=/manufacturer/, name=$name");
if($man->id) {
$input->whitelist('manufacturer', $man->name);
$selector .= "manufacturer=$man, ";
}

list($priceMin, $priceMax) = explode('-', $input->get->price);
$priceMin = (int) $priceMin;
$priceMax = (int) $priceMax;
if(!$priceMax) $priceMax = 99999; // or some other max value
$input->whitelist('price', "$priceMin-$priceMax");
$selector .= "price>=$priceMin, price<=$priceMax, ";

$selector .= 'limit=25';
$products = $pages->find($selector);

if(count($products)) echo $products->render();
else echo "<h2>Sorry no products found</h2>";
Link to post
Share on other sites

Ryan, thanks for the exapmle.

It's working fine with manufacturer if I remove code block from selector:

list($priceMin, $priceMax) = explode('-', $input->get->price);
$priceMin = (int) $priceMin;
$priceMax = (int) $priceMax;
if(!$priceMax) $priceMax = 99999; // or some other max value
$input->whitelist('price', "$priceMin-$priceMax");
$selector .= "price>=$priceMin, $price<=$priceMax, ";

If I include this block of code PW throws me an error:

PHP Notice: Undefined offset: 1 in C:\inetpub\wwwroot\pw\site\templates\manufacturer-search.php on line 13 PHP Notice: Array to string conversion in C:\inetpub\wwwroot\pw\site\templates\manufacturer-search.php on line 18 PHP Fatal error: Exception: Field does not exist: Array (in C:\inetpub\wwwroot\pw\wire\core\PageFinder.php line 244)

manufacturer-search.php template consist only of code you posted above.

Link to post
Share on other sites

nikola, the problem is probably here:

$selector .= "price>=$priceMin, $price<=$priceMax, ";

should probably be:

$selector .= "price>=$priceMin, price<=$priceMax, ";

(notice the dollar sign in the second condition)

  • Like 1
Link to post
Share on other sites
  • 2 months later...

Hi! Search realization is impossible to me, prompt as it is possible to solve it if the form with selectors at me looks so:

<form id="city_obj_search" method="get" action="<?php echo $config->urls->root?>city_obj_search/">
<ul>
<li>
<?php
foreach($pages->get("/business_type/")->children() as $type_of_oper) {
$checked = $type_of_oper->name == $input->whitelist->type_of_oper ? " checked='checked' " : '';
echo "<div>
<label>
<input$checked type='checkbox' name='type_of_oper' value='{$type_of_oper->name}'>{$type_of_oper->title}
</label>
</div>" ;
}
?>
</li>
<li>
<label for="district_location">District</label>
<select id="district_location" name="distr">
<option value="any">Any</option>
<?php
foreach($pages->get("/district/")->children() as $distr) {
$selected = $distr->name == $input->whitelist->distr ? " selected='selected' " : '';
echo "<option$selected value='{$distr->name}'>{$distr->title}</option>";
}
?>
</select>
</li>
<li>
<label for="subway_station">Subway station</label>
<select id="subway_station" name="subway">
<option value="any">Any</option>
<?php
foreach($pages->get("/subway_station/")->children() as $subway) {
$selected = $subway->name == $input->whitelist->subway ? " selected='selected' " : '';
echo "<option$selected value='{$subway->name}'>{$subway->title}</option>";
}
?>
</select>
</li>
<li>
<label for="object_cost_ru">Cost in ru</label>
<select id="object_cost_ru" name="costru">
<option value="any">Any</option>
<?php
foreach(array('30000-50000', '50000-100000', '100000-250000', '250000-500000', '500000-1000000', '1000000+') as $costru) {
$selected = $costru == $input->whitelist->costru ? " selected='selected'" : '';
echo "<option$selected value='$costru'>$costru руб.</option>";
}
?>
</select>
</li>
<li>
<label for="object_cost_dol">Cost in dollars</label>
<select id="object_cost_dol" name="costdol">
<option value="any">Any</option>
<?php
foreach(array('1000-3000', '3000-10000', '10000-20000', '20000-30000', '30000+') as $costdol) {
$selected = $costdol == $input->whitelist->costdol ? " selected='selected'" : '';
echo "<option$selected value='$costdol'>$costdol $</option>";
}
?>
</select>
</li>
<li>
<label for='search_id'>ID</label>
<input type="text" placeholder="00000" name="id" id="search_id" value="<?php
if($input->whitelist->id) echo htmlentities($input->whitelist->id, ENT_QUOTES); ?>" />
</li>
<li>
<input type="submit" id="search_submit" name="submit" value="Find!">
</li>
</ul>
</form>

I wish that this request would be processed the search page «city_obj_search/», but the information from the forum (http://processwire.c...op-down-search/) that I use does not help me either an error or an empty result. Help to understand, please.

Link to post
Share on other sites

I'm not sure I understand the question 100%, but I think we need to take a look at the code that actually generates the selector (rather than just the code that generates the form). Also, while developing search engines, I always find it helpful to output the selector that is being used. Once the site is finished with the development then I remove it. But to do this, you may want to add something like this at the top of your search results:

<?php echo "<h2>" . htmlentities($selector, ENT_QUOTES, "UTF-8") . "</h2>"; ?>
Link to post
Share on other sites

Thanks for the reply Ryan and code, it clarifies some things )

Question that I asked - I solved (so delete it) and walked right up to the search, the results of which I can not get it.

Form code I've described above, in a search template I use the following code (searching only two fields participate now):

include("./header.inc");
$selector = 'parent=/direction/city/, ';
if($input->get->id) {
	$value = $sanitizer->selectorValue($input->get->id);
	$selector .= "object_id~=$value, ";
	$input->whitelist('id', $value);
}
if($input->get->distr) {
	$distr = $pages->get("/district/" . $sanitizer->pageName($input->get->distr));
	if($distr) {
	$selector .= "parent=$distr, ";
	$input->whitelist('distr', $distr->name);
	}
}
$objects = $pages->find($selector);
$count = count($objects);
echo "<h2>" . htmlentities($selector, ENT_QUOTES, "UTF-8") . "</h2>";
if($count) {
echo "<h2>Found pages: $count</h2>";
foreach($objects as $obj) {
echo "<div><a href='{$obj->url}'>{$obj->title}</a></div>" ;
}
} else echo "<h2>There are no results matching the query</h2>";
include("./footer.inc");

I made them for example, but they do not work, and the reason that I can not yet understand.

upd:

Slowly I begin to understand the structure.

It appears in the search involved a hierarchy of pages. On an example of skyscrapers it: cities -> city -> object -> its properties (height, floors, year).

What if such structure isn't present and my objects aren't enclosed, and are parallel?

What role does this value $selector = 'parent=/direction/city/, '; ?

Differently: how add in search of architects of skyscrapers? )

Edited by AlexV
Link to post
Share on other sites

AlexV, what was the output of your $selector (in the <h2>?). The code looks good to me, but I don't know the full site structure either, so don't necessarily have the context to relate it to that.

Link to post
Share on other sites

Ryan, when I remove the code

/*if($input->get->distr) {
	$district = $pages->get("/district/" . $sanitizer->pageName($input->get->distr));
	if($district) {
			$selector .= "parent=$district, ";
			$input->whitelist('distr', $distr->name);
	}
}*/

rest of the code begins to work.

When I turn on, I get:

parent=/direction/city/, parent=1253,

There are no results matching the query

I can guess why. Here is the hierarchy of pages:

|- direction

| |

| |- city

| |- object 1 (object has a Pagefield named «district», in which selected pages «district 1 and so on»)

| |- object 2

| |- and so on

|

|- district

| |- district 1

| |- district 2

I want to get a list of objects from the section «city» criteria «district». How can I do?

upd:

I found the solution, it was too easy, instead of the «parent» it is necessary to use a «field»

$selector .= "parent district_location=$district, ";

But now another question, not displayed «whitelist» )

In what may be the reason?

upd2:

I found the solution works until I add another criterion in the search, for example:

if($input->get->type_of_oper) {
	$type_of_oper = $pages->get("/business_type/" . $sanitizer->pageName($input->get->type_of_oper));
	if($type_of_oper) {
			$selector .= "type_of_operation=$type_of_oper, ";
			$input->whitelist('type_of_oper', $type_of_oper->name);
	}
}

after adding it, I get:

parent=/direction/city/, district_location=, type_of_operation=1021,

how to avoid it?

Edited by AlexV
Link to post
Share on other sites

$pages->get() will always return a Page object, so rather than doing this:

if($type_of_oper) {

you should do this:

if($type_of_oper->id) {

When a $pages->get() doesn't match anything, it returns a NullPage. The easiest way to check for a NullPage is by seeing if it has an id, like above. Though you could also do this if you preferred:

$p = $pages->get($selector); 
if($p instanceof NullPage) {
  // no page found
} else {
  // page found
}
  • Like 1
Link to post
Share on other sites
  • 2 weeks later...

There are still questions of search. While I can't answer them itself.

First question. Now I have two checkbox, it work by a principle "or", in what case they will work as "and". Now the code in the search template looks like this:

$name = $sanitizer->pageName($input->get->type_of_oper);
$man = $pages->get("parent=/business_type/, name=$name");
if($man->id) {
       $input->whitelist('type_of_oper', $man->name);
       $selector .= "type_of_operation=$man, ";
}

Second question. In the search form has two text fields "from" and "before". How to realize search for them?

Сode search form:

<label for="square">Square</label>
<input type="text" name="squareMin" value="<?php if($input->whitelist->squareMin) echo htmlentities($input->whitelist->squareMin, ENT_QUOTES, "UTF-8"); ?>" />
<input type="text" name="squareMax" value="<?php if($input->whitelist->squareMax) echo htmlentities($input->whitelist->squareMax, ENT_QUOTES, "UTF-8"); ?>" />

Code that I tried to use in search template, but it does not work:

$valueMin = $sanitizer->selectorValue($input->get->squareMin);
$valueMax = $sanitizer->selectorValue($input->get->squareMax);
$selector .= "object_square>=$valueMin, object_square<=$valueMax, ";
Link to post
Share on other sites

I think that your first example looks fine in terms of the code. I'm not sure I understand the question on that one.

For the second one, what kind of field is object_square? I don't see anything amiss in your code, but based on the operators ">=" and "<=" it makes me think that object_square is an integer and should probably be sanitized/typecast as such, rather than as a string. i.e.

$valueMin = (int) $input->get->squareMin; 

Also there's no reason to run an integer through htmlentities(). There's also no harm in it, but just wanted to mention it might not be necessary if you are keeping it as an integer.

Link to post
Share on other sites

Thanks for the reply Ryan!

In the second case, I used a «text» field, so that did not work. I changed it on the field «float» and added your code, thank you, it works!

I will describe in more detail the first question.

I have two checkboxes and code which I specified above.

Now when I choose one checkbox search works well. When I choose two checkboxes, search shows that one is chosen only. How it is possible to change a code that search was on two values (which I select checkboxes)?

Link to post
Share on other sites

It's hard to answer for sure, because I don't see anything in your first code example that draws a relation to multiple checkboxes on the front-end. But based on what you've said, I'm guessing one of the checkboxes has a name of "type_of_oper"? i.e.

<input type='checkbox' name='type_of_oper' value='something' />

If there is another checkbox, then you would need some code to handle that one too. Since these are checkboxes rather than radio buttons, you'll want to give each one a different name in their <input> tag. You could also take an array approach, using the same name and appending "[]" to it so that they come through as an array. But if we're only talking about 2 checkboxes here, then I think it's better to just make them separate.

Link to post
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
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By RyanJ
      I have quite the pickle. 
      The admin no matter where I am at in it, times out constantly. I either get a 504 gateway or and execution timeout. I am banging my head why its across the board. The frontend of the site works great.
      The site does have a lot of pages, 164,889 to be exact.  Most of that is due to image pages created by media manager and having repeater matrix installed which is used on most of the pages. For some reason, if I can manage  click the "Clear file compiler" without it timing out, the site perks back up and I can access the admin again and it is quite speedy. However if I walk away leaving the browser sitting for about an hour and come back, I have to start the process all over again.
      The site has 52 various fields. Not every field is assigned to every template. a lot of the fields are used by the matrix to create a page builder.
      I can't accept that PW can't handle such. I have other sites that run triple the amount of pages, but don't rely on the matrix to build the layout.
      I have disabled any custom module that I thought could possibly cause an issue. I have overwrote every module and the wire core thinking something may have got corrupted. I guess my next step is starting with a fresh install, but I thought I would reach out to see if I am missing something. 
      Some background on pages:
      All the pages are imported using the command line which bootstraps PW and uses the api. This is a dev site, so I blow away the pages if I need to reimport them, until I get it correct.
      PW Version 3.0.172, PHP 7.3, Innodb 5.7.27-30.
      I appreciate any suggestions or thoughts. 

       

       

    • By dfile
      Hello,
      I am running ProcessWire 3.0.165 but $input->urlSegmentLast or $input->urlSegment(-1) does not work as expected.
      Or do I something wrong?
      Thank you.

    • By bramwolf
      Hi Guys,

      I found here: https://processwire.com/docs/selectors/#finding2 that I can exclude results from my $pages->find($selctor); query by using parent!=123 ( parent ID ) But I was wondering if I can
      also do so based on the parent template? I've tried doing it by itterating over the matches array and dropping the matches with the parent but since the result is limited for use with the paginator
      it only drops the items from the batch displayed on this page. Say from the results within matches 0 - 20 and not the entire 0 - 220.

      Anybody have a idea of how to achieve this? 🙂

      Thanks in advace!
      Bram 
    • By LAPS
      I've pages using the custom-page-template and I would like to display a simple message at the top of the page form when editing those pages in the Admin.
      I thought about hooking ProcessPageEdit::buildForm() but I do not well how to use that hook in order to display the message just for the custom-page-template pages.
      Any help?
       
       
      UPDATE: Solution found
      wire()->addHookAfter('ProcessPageEdit::buildForm', function(HookEvent $event) { $ProcessPageEdit = $event->object; $form = $event->arguments('form'); switch ($ProcessPageEdit->getPage()->template->name) { case 'custom-page-template': $form->prependMarkup("<div style='margin-bottom: 3.6em; margin-top: 1em; background: #ffd;'>MY MESSAGE</div>"); break; default: // do nothing } }); Any improvement is welcome!
    • By Sten
      Sorry I can't find any submit button to save my configuration on a template in the admin interface. The file is fine.
×
×
  • Create New...