Jump to content

$pages->find() with OR


ethanbeyer
 Share

Recommended Posts

Hello,

I've been working on building a find query and I think I need to be able to do an OR search on multiple selectors, all of which have a number of possible values.

The first way I did this was with three queries:

// Categories
if($page->Categories->count > 0) {
    foreach($page->get("Categories") as $category) {
        $category_array[] = $category->name;
    }
    $category_string = implode("|", $category_array);
    $same_categories = $pages->find("template={$page_type}, Categories={$category_string}")->filter("id!={$page->id}");
}

// Tags
if($page->Tags->count > 0) {
    foreach($page->get("Tags") as $tag) {
        $tag_array[] = $tag->name;
    }
    $tag_string = implode("|", $tag_array);
    $same_tags = $pages->find("template={$page_type}, Tags={$tag_string}")->filter("id!={$page->id}");
}

// ConnectedClient
if(in_array('ConnectedClient', $page->fields)) {
    $same_client = $pages->find("template={$page_type}, ConnectedClient={$page->ConnectedClient}")->filter("id!={$page->id}");
}

if($same_categories->count > 0)         $matches->import($same_categories);
if($same_tags->count > 0)               $matches->import($same_tags);
if($same_client->count > 0)             $matches->import($same_client);

But for some reason, only the first "$matches" seems to stick.

So to me, it would make sense to be able to structure a find query like this:

$search = $pages->find("
        template=post|project,
        [Categories=one|two|three]|[Tags=yes|no|maybe]|[ConnectedClient=1065]
    ")->filter("id!={$page->id}");

Does this make sense? I need to find all pages that match [Tags=yes OR no OR maybe] OR [Categories= one OR two OR three] OR [ConnectedClient=1065].

I realize it might turn into a SQL-needed scenario, but given the flexibility of Processwire, the ability to string together a query like this would be super nice.

 

Is there a way?

Link to comment
Share on other sites

Yes, there is a way :)...there has been for a while actually. It's called OR:groups

$search = $pages->find("template=post|project, (categories=one|two|three), (tags=yes|no|maybe), (connectedClient=1065), id!={$page->id}");

That should find results if any of the selectors in the brackets find a match AND the template='blah blah'  and id!=1234 are also true . 

 

  • Like 7
Link to comment
Share on other sites

Thank you both - @kongondo and @LostKobrakai!

I knew there had to be a way to do that, but wasn't sure even how to google it. I'm sure I read right past that part of the docs.

Here's what I needed the Or Groups for: "related posts"!

I've got two page-types that can be related via Categories, Tags or a Connected Client, and I wanted to be able to show other Posts or Projects that had the same Category, Tag or Client attached. Previous versions of the following function were always finding related posts only when there was an exact match of Categories+Tags.

But with your help, I was able to rewrite the function to the following:

public function getRelatedPages( $base_page_id, $page_type )
{
    // set values
    $pages = wire("pages");
    $page = $pages->get($base_page_id);
    $search = array();
    $matches = new WireArray;

    // Categories
    if($page->Categories->count > 0) {
        foreach($page->get("Categories") as $category) {
            $category_array[] = $category->name;
        }
        $category_string = implode("|", $category_array);
        $search[] = "(Categories={$category_string})";
    }

    // Tags
    if($page->Tags->count > 0) {
        foreach($page->get("Tags") as $tag) {
            $tag_array[] = $tag->name;
        }
        $tag_string = implode("|", $tag_array);
        $search[] = "(Tags={$tag_string})";
    }

    // ConnectedClient
    if(isset($page->ConnectedClient)) {
        $search[] = "(ConnectedClient={$page->ConnectedClient})";
    }

    $search_string = implode(", ", $search);
    $matches = $pages->find($search_string)->sort("random")->filter("id!={$page->id}, template={$page_type}, limit=3");
    
    // Debug
    // $this->dd($search);
    
    // Output the final matches
    return ($matches->count > 0) ? $matches : NULL;
}

 

Thanks again!

  • Like 3
Link to comment
Share on other sites

7 hours ago, ethfun said:

But with your help, I was able to rewrite the function to the following:

Nice one!

But just a small thing: you set $search to an empty WireArray and then immediately re-set it to an empty array:

$search = new WireArray;
$search = array();

You only need the latter.

  • Like 2
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

×
×
  • Create New...