Skip to content

Commit

Permalink
Merge pull request #2 from trendwerk/term
Browse files Browse the repository at this point in the history
Term dimension
  • Loading branch information
haroldangenent authored May 24, 2017
2 parents c123f12 + e824cd9 commit 269bba4
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 5 deletions.
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Search
[![Build Status](https://travis-ci.org/trendwerk/search.svg?branch=master)](https://travis-ci.org/trendwerk/search) [![codecov](https://codecov.io/gh/trendwerk/search/branch/master/graph/badge.svg)](https://codecov.io/gh/trendwerk/search)

Basic extensions for searching in WordPress. Currently only supports searching in `postmeta`.
Basic extensions for searching in WordPress.

Quick links: [Install](#install) | [Usage](#usage) | [Dimensions](#dimensions) | [Example](#example)

_Note: This basic extension is not very scalable and meant for smaller databases. This package could get slow for complex meta searches. In that case, [Elasticsearch](https://www.elastic.co/) would be a better solution._
_Note: This basic extension is not very scalable and meant for smaller databases. This package could get slow for complex searches. In that case, [Elasticsearch](https://www.elastic.co/) would be a better solution._

## Install
```sh
Expand All @@ -27,7 +27,7 @@ $search->init();
This code should be run when bootstrapping your theme.

### Dimensions
Currently this package only supports metadata as a search dimension. Dimensions can be added by using `addDimension`:
Currently this package supports metadata and terms as search dimensions. Dimensions can be added by using `addDimension`:

```php
$search->addDimension($dimension);
Expand All @@ -39,7 +39,7 @@ $search->addDimension($dimension);

### Meta
```php
$metaDimension = new \Trendwerk\Search\Dimension\Meta([
$metaDimension = new \Trendwerk\Search\Dimension\Meta($wpdb, [
'key' => 'firstName',
]);

Expand All @@ -53,13 +53,27 @@ Available options for constructing an instance of `Meta`:
| `key` | `null` | Yes | The `meta_key` to search for
| `compare` | `=` | No | The database comparison that should be made for the meta key. Currently supports `LIKE` and `=`. When using `LIKE`, make sure to include a percent symbol (`%`) in your `key` parameter as a wildcard. See [Example](#example)

### Terms
```php
$search->addDimension(new \Trendwerk\Search\Dimension\Term($wpdb, [
'taxonomy' => 'taxonomyName',
]));
```

Available options for constructing an instance of `Term`:

| Parameter | Default | Required | Description |
| :--- | :--- | :--- | :--- |
| `taxonomy` | `null` | Yes | The `taxonomy` which terms should be included in search

## Example

```php
use Trendwerk\Search\Dimension\Meta;
use Trendwerk\Search\Dimension\Term;
use Trendwerk\Search\Search;

$search = Search();
$search = new Search();
$search->init();

$search->addDimension(new Meta($wpdb, [
Expand All @@ -70,4 +84,8 @@ $search->addDimension(new Meta($wpdb, [
$search->addDimension(new Meta($wpdb, [
'key' => 'firstName',
]));

$search->addDimension(new Term($wpdb, [
'taxonomy' => 'category',
]));
```
58 changes: 58 additions & 0 deletions src/Dimension/Term.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
namespace Trendwerk\Search\Dimension;

use BadMethodCallException;
use wpdb;

final class Term implements Dimension
{
private $options;
private $tableAlias = 'searchTerm';
private $wpdb;

public function __construct(wpdb $wpdb, array $options)
{
if (! isset($options['taxonomy'])) {
throw new BadMethodCallException('`taxonomy` is a required property.');
}

$this->options = $options;
$this->wpdb = $wpdb;
}

public function join($aliasCount = 0)
{
$tableAlias = $this->tableAlias . $aliasCount;

$sql = "INNER JOIN {$this->wpdb->term_relationships} AS {$tableAlias} ";
$sql .= "ON ({$this->wpdb->posts}.ID = {$tableAlias}.object_id)";

return $sql;
}

public function search($searchWord, $aliasCount = 0)
{
$tableAlias = $this->tableAlias . $aliasCount;
$searchWord = $this->wpdb->esc_like($searchWord);

$termIds = array_map('absint', $this->termsFor($searchWord));

if (count($termIds) == 0) {
return;
}

$termIds = implode(',', $termIds);

return "{$tableAlias}.term_taxonomy_id IN ({$termIds})";
}

private function termsFor($searchWord)
{
return $this->wpdb->get_col($this->wpdb->prepare("SELECT {$this->wpdb->term_taxonomy}.term_id
FROM {$this->wpdb->term_taxonomy}
INNER JOIN {$this->wpdb->terms} USING(term_id)
WHERE taxonomy = %s
AND {$this->wpdb->terms}.name LIKE %s
", $this->options['taxonomy'], "%{$searchWord}%"));
}
}
2 changes: 2 additions & 0 deletions src/Hook/Posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ public function search($sql, WP_Query $query)
$searches[] = $dimension->search($searchWord, $index);
}

$searches = array_filter($searches);

$search = '(' . implode($or, $searches) . ')' . $or;

$clause = preg_replace('/' . $or . '/', $or . $search, $clause, 1);
Expand Down
104 changes: 104 additions & 0 deletions tests/Search/Dimension/TermTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php
namespace Trendwerk\Search\Test;

use BadMethodCallException;
use Mockery;
use Trendwerk\Search\Dimension\Term;
use WP_Mock;

final class TermTest extends TestCase
{
private $tableAlias = 'searchTerm';
private $wpdb;

public function setUp()
{
parent::setUp();

$this->wpdb = Mockery::mock('wpdb');
$this->wpdb->posts = 'wp_posts';
$this->wpdb->term_relationships = 'wp_term_relationships';
$this->wpdb->term_taxonomy = 'wp_term_taxonomy';
$this->wpdb->terms = 'wp_terms';
}

public function testKeyRequired()
{
$this->expectException(BadMethodCallException::class);
$meta = new Term($this->wpdb, []);
}

public function testJoin()
{
$tableAliasCount = 2;
$tableAlias = $this->tableAlias . $tableAliasCount;

$expectation = "INNER JOIN {$this->wpdb->term_relationships} AS {$tableAlias} ";
$expectation .= "ON ({$this->wpdb->posts}.ID = {$tableAlias}.object_id)";

$term = $this->create('taxonomyName');

$result = $term->join($tableAliasCount);

$this->assertEquals($expectation, $result);
}

public function testSearch()
{
$this->search('Testterm', 'testTaxonomy');
}

public function testSearchAliasCount()
{
$this->search('Term', 'taxonomy', 2);
}

public function testNoHit()
{
$this->search('Testterm', 'testTaxonomy', 2, []);
}

private function search($searchWord, $taxonomy, $tableAliasCount = 0, $foundTermIds = [18, 12])
{
$tableAlias = $this->tableAlias . $tableAliasCount;

if (count($foundTermIds) == 0) {
$expectation = '';
} else {
$termIds = implode(',', $foundTermIds);
$expectation = "{$tableAlias}.term_taxonomy_id IN ({$termIds})";
}

$term = $this->create($taxonomy);

WP_Mock::wpPassthruFunction('absint', ['times' => count($foundTermIds)]);

$this->wpdb->shouldReceive('esc_like')
->once()
->with($searchWord)
->andReturn($searchWord);

$this->wpdb->shouldReceive('prepare')
->once()
->andReturnUsing(function ($sql, $taxonomy, $search) {
return sprintf($sql, $taxonomy, $search);
});

$this->wpdb->shouldReceive('get_col')
->once()
->andReturn($foundTermIds);

$result = $term->search($searchWord, $tableAliasCount);

$this->assertEquals($expectation, $result);
}

private function create($taxonomy)
{
$term = new Term($this->wpdb, [
'taxonomy' => $taxonomy,
]);

return $term;
}
}

0 comments on commit 269bba4

Please sign in to comment.