Jump to content
MateThemes

Add Repeater Matrix Fields to Search Selector

Recommended Posts

Hello everyone!

I am trying to add my repeater matrix fields to the search selector, but unfortunately nothing seems to work for me.

I have following search code in my search.php:

<?php namespace ProcessWire;

// look for a GET variable named 'q' and sanitize it
$q = input()->get('q');

// sanitize to text, which removes markup, newlines, too long, etc.
$q = sanitizer()->text($q);

// did $q have anything in it after sanitizing to text?
if($q) {

	// Make the search query appear in the top-right search box.
	// Always entity encode any user input that also gets output
	echo '<input id="search-query" value="' . sanitizer()->entities($q) . '">';

	// Sanitize for placement within a selector string. This is important for any 
	// values that you plan to bundle in a selector string like we are doing here.
	// It quotes them when necessary, and removes characters that might cause issues.
	$q = sanitizer()->selectorValue($q);

	// Search the title and body fields for our query text.
	// Limit the results to 50 pages. The has_parent!=2 excludes irrelevant admin
	// pages from the search, for when an admin user performs a search. 
	$selector = "title|body~=$q, limit=50, has_parent!=2";

	// Find pages that match the selector
	$matches = pages()->find($selector);
	
} else {
	$matches = array();
}

// unset the variable that we no longer need, since it can contain user input
unset($q);

?>
<main pw-replace='main'>
	<?php include('./includes/_pageheadersearch.php'); ?>

	<div id='content-body' class='uk-section uk-section-large uk-section-large'>
		<div class='uk-container uk-container-small'>
			<?php
			// did we find any matches?
			if(count($matches)) {
				// yes we did, render them
				echo ukAlert(sprintf(_n('Found %d page', 'Found %d pages', $matches->count), $matches->count), "default", "check");
				echo ukDescriptionListPages($matches);
			} else {
				// we didn't find any
				echo ukAlert(__('Sorry, no results were found'), "danger", "warning");
			}
			?>
		</div>
	</div>
</main>

I have tried to add my fields to the selector code (repeater_matrix.aboutsblock_repeaters.mytextfield) . But I didn't get any results.

What I am doing wrong?

Thanks for your help!

Share this post


Link to post
Share on other sites

You could check if one of the newer fieldtype selectors work for you:

https://processwire.com/blog/posts/processwire-3.0.91-core-updates/

I just quickly did a test: It seems if you omit the matrix-fieldname, it works, e.g.

If I have a matrix field called matrix, and inside it is a body field with a matrix-name "richtext":

matrix.richtext.body%=keyword // doesn't work

matrix.body%=keyword // works

$res = $pages->find("matrix.body%=fräsen");
if($res->count) {
    echo '<ul>';
    foreach($res as $match) {
        echo "<li><a href='{$match->url}'>{$match->title}</a></li>";
    }
echo '</ul>';
} else {
    echo "<p>No results found.</p>";
}

 

matrix-gotcha.PNG

  • Like 1

Share this post


Link to post
Share on other sites

Thank you for your answer.

It seems like the problem is, that the field is in a repeater matrix and then in a repeater.

The hierarchy is:

- repeater_matrix
  -- aboutusblock_repeater
  ---- textfield

Could that be?

Share this post


Link to post
Share on other sites

I'm going to jump on this train as well. I've got a matrix with repeater inside and then a body field, just like MateThemes example above.

This works

title|headline|lead|body%=$q

Also this works

matrix.repeater_field.body%=$q

But if I combine both like so, it wont return anything

$selector .= ", title|headline|lead|body%=$q";
$selector .= ", matrix.repeater_field.body%=$q;

If I try to enclose each line in parenthesis it returns every single page on the website, even if the search word is something that doesn't exist on the site

$selector .= ", (title|headline|lead|body%=$q)";
$selector .= ", (matrix.repeaters.body%=$q)";

So I'm kinda almost there, but not quite.

Share this post


Link to post
Share on other sites

I guess this should do it:

$selector .= "title|headline|lead|body|matrix.repeater_field.body%=$q";

 

  • Like 1

Share this post


Link to post
Share on other sites

Unfortunately that doesn't work. "Multidot" selectors can't be used with the | symbol.
 

Share this post


Link to post
Share on other sites

Hmm yes, I just tried it out myself and saw this msg: Exception: Multi-dot 'a.b.c' type selectors may not be used with OR '|' fields on line: 896 in /wire/core/PageFinder.php 😞

Guess you'd have to do two separate queries then, one for regular fields, and one for Matrix fields (?). I guess you could file an issue @ Github, if it isn't mentioned already.

edit: Just found this related thread: 

 

Edited by dragan
added link

Share this post


Link to post
Share on other sites
On 4/25/2019 at 9:14 PM, Hurme said:

If I try to enclose each line in parenthesis it returns every single page on the website, even if the search word is something that doesn't exist on the site

Maybe you made a typo somewhere in your selector - the OR-group syntax is working for me:

2019-04-29_232222.png.022de0e098bd5488d3cece209e98b84e.png

 

Share this post


Link to post
Share on other sites

@Robin S What happens if you have multiple pages, where some of the pages have the matrix and some don't? Does it still work for you?

Share this post


Link to post
Share on other sites
9 hours ago, Hurme said:

What happens if you have multiple pages, where some of the pages have the matrix and some don't? Does it still work for you?

There are multiple pages existing in the example above, so yes it does work.

I saw your post in the other thread - the problem is that you are using a single unnamed OR-group for all your selectors. Only one parenthesised selector in the OR-group has to match. So when you do...

$selector = "(template=home|basic-page|product-category, status!=hidden), (template=news-item|product-page)";

...this means that any unhidden page with the home, basic-page or product-category template and any page with the news-item or product-page template will match regardless of any other selectors you later add to the same OR-group. Have another look at the documentation for OR-groups and try giving names to the different OR-groups for template and field values.

  • Like 1

Share this post


Link to post
Share on other sites

Cheers @Robin S, this makes sense to me. I did read about the named groups while trying to solve this, but the explanation felt a little short for me. What does naming the groups actually achieve? 

Share this post


Link to post
Share on other sites
2 hours ago, Hurme said:

What does naming the groups actually achieve?

It tells PW that there is more than one OR-group in the selector string. It's all about specifying what has to match in order for the whole selector string to match - remember that for every OR-group only one selector in the group has to match.

So previously you were doing this...

// Templates
$selector = "(template=home|basic-page|product-category, status!=hidden), (template=news-item|product-page)";
// Fields
$selector .= ", (title|headline|lead|body%=$q), (content_matrix.columns.body%=$q, content_matrix.columns.count>0)";

...which we can represent as this...

// Templates
$selector = "(template selector 1), (template selector 2)";
// Fields
$selector .= ", (field selector 1), (field selector 2)";

...and it is the same thing as...

// Templates
$selector = "foo=(template selector 1), foo=(template selector 2)";
// Fields
$selector .= ", foo=(field selector 1), foo=(field selector 2)";

...which is saying "find any pages that match template selector 1 OR template selector 2 OR field selector 1 OR field selector 2". So this will match a lot of pages that you don't actually want to match.

Whereas this...

// Templates
$selector = "foo=(template selector 1), foo=(template selector 2)";
// Fields
$selector .= ", bar=(field selector 1), bar=(field selector 2)";

...is saying "find any pages that match (template selector 1 OR template selector 2) AND (field selector 1 OR field selector 2)".

  • Like 2

Share this post


Link to post
Share on other sites

Thank you very much for taking the time to explain it. This does makes things much more clear to me. I'll report back once I've had the time to test this.

Share this post


Link to post
Share on other sites

Hello everyone!

So as far as I understand the discussion, it is not possible to make a repeaterfield searchable in a repeater matrix. Is this right?

Thank you!

Share this post


Link to post
Share on other sites

@Robin S It works fine now, thanks for your help. Ryan basically gave the same advice in another thread so it should be doubly fixed now. 😉
 
@MateThemes It works just fine.

You'll want to write it like this:

$pages->find("matrix_field.repeater_field.regular_field%=$searchquery, matrix_field.repeater_field.count>0");

Note that you'll want to leave out matrix type. The count part at the end is to make sure it only returns pages that actually have content in them (seems to be a bug and I haven't checked if it's been fixed yet or not).

Share this post


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 fruid
      Hi,
      this is the first time I'm using ProcessWire.
      I thought I get how fields, template and pages work, but when I create a template in the CMS, it doesn't generate any file in site/templates/
      Then I thought I might need to create a blank file myself manually on the FTP (which already seems odd to me).
      Once I did that, I tried to add fields to the template but again, doesn't write to the php file.
      When I create a new page and apply said template to it, the page stay blank.
      AFAIK the mod_rewrite of the apache is on and I went for the worst case scenario described here https://processwire.com/docs/security/file-permissions/ and set all file-permissions for future files to 0666 and folders to 0777 in the config.php
      What am I not getting and what am I doing wrong?
      Help is appreciated, stay save everybody,
      Fred
    • By Anders
      I want to allow full text search on my site. There is a very nice solution that comes right out of the box:
      $selector = "title|body~=$q, limit=50"; This works, but to make it even better I would want to give higher weight to pages where the search term occurs in the title, than if it just occurs in the body. After all, a page with the title "Wine from France" is probably the best match for the search "france wine". How do I accomplish this in ProcessWire?
      I can see three possible paths, but I am not very fond of any of them:
      Do a direct SQL query, circumventing the API, along these lines. But I would prefer to abstract away the database layout if at all possible. Use something like ElasticSearch, but to be honest that would be to complicated to set up and maintain in the long run. Make multiple lookups, first for matches in the title, then for matches in the body, and merge and sort in PHP. My suspicion is that this would get complicated quite quickly. For instance, how do you deal with a page that has two of the three search terms in the title and the third in the body? Is there a magic option four I should look into? Or are any of the above options better than the others? Any input is welcome!
    • By jds43
      Hello,
      I have a search page loosely based on Skyscrapers where I'm parsing a selector with options 'beds', 'bathrooms', 'size' fields. It is working well until I select 'Any' after I've run a search. This is where no results are returned (/?beds=&bathrooms=&size=&submit=). I want it to reset and show all results.
      I hope this isn't too vague.
       
    • By cboetens
      Hi guys
      I guess this is the right forum to post my question. If not, do let me know. Ok, I'm fairly new to ProcessWire and so far I really like the CMS! It's magnificent to use and really straight forward. Big thanks to Ryan for that.
      Now, the question I have. I have a template called 'products_categories.php' in which I collect and render all the different categories (the categories basically act as a filter) for a variety of products and next to the categories, I render all the products, like so:
      <?php include('includes/header.php') ?> <?php $categoryName = $sanitizer->pageName($input->urlSegment(1)); $subcategoryName = $sanitizer->pageName($input->urlSegment(2)); pre($categoryName); pre($subcategoryName); $categories = $page->get("template=products_categories"); $category = $categories->get("template=products_categories_item, name=" . $categoryName); if($category->id){ $session->category = $category->id; $subcategory = $category->get("template=products_categories_item, name=" . $subcategoryName); if($subcategory->id){ $session->subcategory = $subcategory->id; } } $selector = "template=products_item"; if($category->id){ $selector .= ", products_categories=" . $category; } if(isset($subcategory->id)){ $selector .= ", products_categories=" . $subcategory; } $products_items = $page->find($selector); ?> <div class="uk-section"> <div class="uk-container"> <?php if(!empty($page->headline)) { ?> <h1><?= $page->headline ?></h1> <?php } ?> <!-- **** COMMENT: create grid for products **** ---> <div class="row"> <div class="col-md-3"> <div class="uk-card mr-3 uk-card-default uk-card-body"> <h3 class="uk-card-title">Productcategorieën</h3> <ul class="uk-list"> <?php foreach($categories->children() as $c){ ?> <li class="<?= ($category->id===$c->id ? 'active ' : '') ?>"> <a href="<?= $page->url . $c->name . '/' ?>"><?= $c->title; ?></a> <?php if($c->hasChildren){ ?> <ul> <?php foreach($c->children() as $sc) {?> <li class="<?= ($category->id==$c->id && $subcategory->id==$sc->id ? 'active ' : '') ?>"> <a href="<?= $page->url . $c->name . '/' . $sc->name . '/' ?>"><?= $sc->title; ?></a> </li> <?php } ?> </ul> <?php } ?> </li> <?php } ?> </ul> </div> </div> <div class="col-md-9"> <div class="uk-child-width-1-3@s uk-grid-match uk-grid-margin-small uk-grid-small" uk-grid> <?php foreach($products_items as $product) { ?> <div class="uk-card"> <?php if(isset($product->image)) { ?> <div class="uk-card-media-top"> <img src="<?= $product->image->URL; ?>" title="<?= $product->title; ?>" alt="<?= $product->intro; ?>"> </div> <?php } ?> <?php if(!empty($product->title) || !empty($product->intro)) { ?> <div class="uk-card-body uk-card-default"> <?php if(!empty($product->title)) { ?> <h3 class="uk-card-title"><?= $product->title; ?></h3> <?php } ?> <?php if(!empty($product->intro)) { ?> <p><?= $product->intro; ?></p> <?php } ?> <?php if (!empty($product->price)): ?> <h3 class="uk-card-title">&euro;&nbsp;<?= $product->price; ?>&nbsp;(excl. btw)</h3> <?php endif; ?> <a class="uk-button uk-button-primary" href="<?= $product->url; ?>">Bekijk</a> </div> <?php } ?> </div> <?php } ?> </div> </div> </div> </div> </div> <?php include('includes/footer.php'); ?> Now, I can't seem to get the filter to work. The URL behind every (sub)category should go straight back to the template 'products_categories.php', that way I can get a range of products according to the selected/ clicked URL.
      What am I missing here? Is this not the correct way to handle things?
      Any help is welcome!
      Thanks.
      Cédric
    • By sww
      Hey there,
      i am trying to add a kind of "intelligent" search.
      Is there any way to ignore punctuation and extra chars.
      e.g.
      When I type "somebody elses" that I still get the result "SOMEBODY ELSE´S CAR, 2005"
      The problem is the extra ´ … if I don't type it I don't get the result.
      So far I am using %=query (which isn't enough, I know)
      So the idea would be something like that:
      $selector = "sanitize(title|text)%=$q, limit=50";
      I know, it's not gonna work like that … just to illustrate.
      Best,
      Stefan
×
×
  • Create New...