- Installation
- Search Client
To install the library you can download it from the project's releases page or use Composer.
Requires PHP 5.6 or later. Not tested in previous versions.
Just include the provided autoload.php
file and use:
require_once('path/to/php-doofinder/autoload.php');
$client = new \Doofinder\Search\Client(HASHID, API_KEY);
Add Doofinder to your composer.json
file by running:
$ composer require doofinder/doofinder
If you're already using composer your autoload.php file will be updated. If not, a new one will be generated and you will have to include it:
<?php
require_once dirname(__FILE__)."/vendor/autoload.php";
use \Doofinder\Search\Client as SearchClient;
$client = new SearchClient(HASHID, API_KEY);
require_once("path/to/php-doofinder/autoload.php");
define("HASHID", "6a9gbc4dcx735x123b1e0198gf92e6e9");
define("SERVER", "eu1-search.doofinder.com");
define("API_KEY", "384fdag73c7ff0a59g589xf9f4083bxb9727f9c3");
// Set server and API Key
$client = new \Doofinder\Search\Client(SERVER, API_KEY);
$searchParams = [
"hashid" => HASHID,
"query" => "sneakers",
"filter" => [
"brand" => ["nike", "converse"],
"color" => ["red", "blue"],
"price" => ["from" => 33.2, "to" => 99]
],
"sort" => [
["price" => "desc"],
["title" => "asc"]
],
"transformer" => "basic"
];
$results = $client->search($searchParams);
// With the results object, fetch specific properties, facets or the results
// itself as an array.
$results->getProperty('results_per_page'); // returns results per page.
$results->getProperty('page'); // returns the page of the results
$results->getProperty('total'); // total number of results
$results->getProperty('query'); // query used
$results->getProperty('hashid');
$results->getProperty('max_score'); // maximum score obtained in the search results
$results->getProperty('doofinder_status'); // special Doofinder status, see below
// special properties: banner and redirect (if defined in your control center)
$banner = $results->getProperty('banner'); // array with 'id', 'link', 'image' and 'blank' keys
$redirect = $results->getProperty('redirect'); // array with 'id' and 'url' keys
// If you use the 'basic' transformer ...
foreach($results->getResults() as $result){
echo $result['description']."\n"; // description of the item
echo $result['dfid']."\n"; // Doofinder id. uniquely identifies this item
echo $result['price']."\n"; // string, may come with currency sign
echo $result['sale_price']."\n"; // may or may not be present
echo $result['title']."\n"; // title of the item
echo $result['link']."\n" ; // url of the item's page
echo $result['image_link']."\n" ; // url of the item's image
echo $result['type']."\n" ; // item's type. "product" at the moment
echo $result['id']."\n" ; // item's id, as it comes from the xml feed
}
$category_facet = $results->getFacet('category');
foreach($category_facet['terms'] as $term) {
// Category: Trousers : 5 results found
echo "Category: ".$term['term']." : ".$term['count']." results found\n";
}
$price_facet = $results->getFacet('price');
echo "Min price found: ".$price_facet['from']."\n";
// Min price found: 33.6
echo "Max price found: ".$price_facet['to']."\n";
// You can iterate through pages too:
$params = [
'hashid' => HASHID,
"filter" => [
"country" => ["Spain"]
],
"sort" => [
["city" => "asc"]
],
"rpp" => 5
];
$results = $client->search($params);
while ($results) {
foreach ($results->getResults() as $item) {
$city = $item["city"];
$title = $item["title"];
echo "[$city] $title" . PHP_EOL;
}
$results = $client->getNextPage();
}
Notice:
- For non-numeric fields you'll have to set those fields as sortable in Doofinder's control panel before you can sort by them.
- Every search request is made through secure protocol.
When you issue a query to Doofinder, the search engine tries different types of search in order to provide the best possible results. This "types of search" are controlled by the query_name
parameter.
However, if you're going to apply filters to a query, that means you're going to make the search again with certain added restrictions, therefore you're not interested in let Doofinder find the best "type of search" for you again, but you rather do the search exactly the same way you did when first querying, so the results with applied filters are consistent with that.
$results->getProperty('query_name')
gives you the type of query that was used to fetch those results. If you plan to filter on those, you should use the same type of query. You can do that with:
// make the initial query. no filters and no "query_name" specified
$params = ['hashid' => HASHID, "query" => "baby gloves"]
$results = $client->search($params);
$params["query_name"] = $client->getSearchParam("query_name");
$params["filter"] = [
"category" => ["More than 6 years"]
];
// do the same query. this time filtered and with a specific query_name
$client->search($params);
You can load form parameters:
<form method="GET" action="search.php">
<input type="hidden" name="hashid" value="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">
<input type="text" name="query" value="sneakers">
<input type="hidden" name="query_name" value="match_and">
...
</form>
$params = $client->searchParams($_GET);
// [
// "hashid" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
// "query" => "sneakers",
// "query_name" => "match_and"
// …
// ]
You can change the name of the query parameter:
<form method="GET" action="search.php">
<input type="hidden" name="hashid" value="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">
<input type="text" name="text" value="sneakers">
<input type="hidden" name="query_name" value="match_and">
...
</form>
$params = $client->searchParams($_GET, ["queryParameter" => "text"]);
// [
// "hashid" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
// "query" => "sneakers",
// "query_name" => "match_and"
// …
// ]
And you can add a prefix if necessary:
<form method="GET" action="search.php">
<input type="hidden" name="dfParam_hashid" value="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">
<input type="text" name="dfParam_text" value="sneakers">
<input type="hidden" name="dfParam_query_name" value="match_and">
...
</form>
$params = $client->searchParams($_GET, ["prefix" => "dfParam_", "queryParameter" => "text"]);
// [
// "hashid" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
// "query" => "sneakers",
// "query_name" => "match_and",
// …
// ]
In short:
- You make a query to Doofinder either with filters or not. You don't need to specify
query_name
. Doofinder will find the more suitablequery_name
. - Once you got the results, you can use
$results->getProperty('query_name')
to know whichquery_name
was the one Doofinder chose in a specific set of results or use$client->getSearchParam("query_name")
to get the query name from the latest call. - If you want to make further filtering on those search results, you should instruct Doofinder to use the same
query_name
you got from the first search results. - Each time you do any new query, don't specify
query_name
. Let Doofinder find the best. - Warning: don't try to figure out a
query_name
on your own. Query names may change in the future. Always count on$results->getParameter('query_name')
or$client->getSearchParam("query_name")
to get thequery_name
that led to those$results
.
Dumps the parameters of the latest search done to an array.
$client->search(["hashid" => HASHID, "query" => "sneakers"]);
$client->dumpParams(["prefix" => "dfParam_", "queryParameter" => "dfParam_"]);
// [
// "hashid" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
// "query" => "sneakers",
// "query_name" => "match_and",
// "page" => 1,
// …
// ]
This is the same as the dumpParams()
method but generates a querystring instead of an array:
$client->search(["hashid" => HASHID, "query" => "sneakers"]);
$client->qs();
// hashid=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&query=sneakers&query_name=match_and&page=1
$client->qs(["queryParameter" => "text"]);
// hashid=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&text=sneakers&query_name=match_and&page=1
$client->qs(["prefix" => "dfParam_"]);
// dfParam_hashid=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&dfParam_query=sneakers&dfParam_query_name=match_and&dfParam_page=1
$client->qs(["prefix" => "dfParam_", "queryParameter" => "text"]);
// dfParam_hashid=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&dfParam_text=sneakers&dfParam_query_name=match_and&dfParam_page=1
You can use it to build links to search results:
<a href="results.php?<?php echo $client->qs(['page' => 4])?>">Next Page</a>
When specifying filters in HTML forms, follow this convention:
- All the filters are passed in an array called filter prefixed with the prefix specified in
$client
constructor (default:dfParam_
). - Each key is a filter name. Each value is filter definition.
- Filter definition for terms filter: use an array with terms
- Filter definition for range filter: use an array with
from
and/orto
keys.
Example: color (terms filter) must be blue
or red
and price (range filter) must be greater than 10.2
.
<input name="dfParam_filter[color][]" value="blue">
<input name="dfParam_filter[color][]" value="red">
<input name="dfParam_filter[price][gte]" value="10.2">
This constructs the array:
dfParam_filter = [
'color' => ['blue', 'red'],
'price' => ['from' => 10.2],
];
As with other params, the parameters must be prefixed with the prefix
specified in $client
constructor (default: dfParam_
).
If you're by only one field and in ascending order, you can simply send the sort
parameter with the name of the field to sort by as value:
<input name="dfParam_sort" value="price">
If you want to sort by one field and specify the sort direction, you'll have to to send, for the sort
param, an array, being the key the field to sort on and the value either asc
or desc
:
<input name="dfParam_sort[price]" value="desc".
If you want to sort by several fields, just compound the previous definition in an array.
Note: When sorting for several fields, sort direction must be specified for every one.
Example: sort in descending order by price and if same price, sort by title in ascending order.
<input name="dfParam_sort[0][price]" value="desc">
<input name="dfParam_sort[1][title]" value="asc">
This constructs the array:
dfParam_sort = [
['price' => 'desc'],
['title' => 'asc'],
];
Please read carefully the sort parameters section in our search API documentation.
An empty query matches all documents. Of course, if the query is filtered, even if the search term is none, the results are filtered too.
The results are always in UTF-8 encoding. If you're using it on an ISO-8859-1 encoded page, you can use utf8_decode
:
foreach ($results->getResults() as $result) {
echo utf8_decode($result['body']).PHP_EOL;
}
$results = $client->search([
"hashid" => HASHID,
"query" => "test query",
"page" => 3,
// Results Per Page (default: 10)
"rpp" => 4,
// types of item to search (default: all)
"types" => ["product", "question"],
// Template used to return items
"transformer" => "basic",
// Filtering options
"filter" => [
"brand" => ["nike", "converse"],
"price" => ["from"=> 33.2, "to"=> 99],
],
// Sorting options
"sort" => [
["price" => "asc"],
["title" => "desc"],
]
]);
In order to take the most of Doofinder stats, you can register in Doofinder certain events so you can have stats and metrics.
All metrics require a unique session id. You must generate and register it before performing any search. It's your responsability to manage its duration (recommended 24h or until a checkout is done - see Register Checkout).
$sessionId = $client->createSessionId();
$client->registerSession($sessionId, HASHID);
IMPORTANT: This method should only be used once per user until the session expires.
Call this method when a goal is achieved (a user purchases something…). Use the session id previously generated and then, regenerate the session id.
$client->registerCheckout($sessionId, HASHID);
$sessionId = $client->createSessionId();
Register the fact of a user clicking a product after a search.
$doofinderItemId = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@product@ffffffffffffffffffffffffffffffff";
$client->registerClick($sessionId, HASHID, $doofinderItemId);
If you want, you can provide the item's database id (the id you provided when indexing the item). Just provide the datatype used for indexing:
$client->registerClick($sessionId, HASHID, 12345, ["datatype" => "product"]);
You can link the click to the search query for enhanced insights:
$client->registerClick($sessionId, HASHID, 12345, [
"datatype" => "product",
"query" => "sneakers"
]);
Sometimes the response can hold banner info if you defined a banner for certain search conditions. You can register a click on a banner with:
$banner = $results->getProperty("banner");
$client->registerImageClick($sessionId, HASHID, $banner["id"]);
As with banners, you can register a redirection if you defined it in Doofinder. You're the responsible of redirecting the user or not, and you can send the event to Doofinder, optionally linking with the query, with:
$redirection = $results->getProperty("redirection");
$query = $results->getProperty("query");
$client->registerRedirection($sessionId, HASHID, $redirection["id"], $redirection["url"], [
"query" => $query
]);
In the Doofinder control center you can create:
- Banners: Clickable image banners to be displayed for certain search terms.
- Redirections: The page the user should be redirected to for certain search terms.
If present in the response, you can get both properties along with their info by simply accesing them with getProperty
:
<?php
$results = $client->search("This search term produces banner in search results");
$banner = $results->getProperty("banner"); // if no banner, this is null
?>
<?php if ($banner): ?>
<a href="<? php echo $banner['link'] ?>">
<img src="<?php echo $banner["image"] ?>">
</a>
<?php endif ?>
$redirection = $results->getProperty("redirection"); // if no redirect, this is null
if ($redirection) {
header("location: " . $redirection["url"]);
}
$client->searchParams($params, $options); // Import search params from a request into an array
$client->dumpParams($options); // Export latest search params in client into an array
$client->qs(); // Export latest search params in client to a string
$client->search($params); // Perform search
$client->getNextPage(); // Perform a search for the next page of results
$client->getPreviousPage(); // Perform a search for the previous page of results
$client->getSearchParam($paramName, $defaultValue); // Get a search parameter from the client for the latest search done
$client->createSessionId(); // Create a hash to be used as session id
$client->registerSession($sessionId, $hashid); // Initializes session for the search client
$client->registerClick($sessionId, $hashid, $id, $options); // Register a click in Doofinder
$client->registerCheckout($sessionId, $hashid); // Register a checkout in Doofinder
$client->registerImageClick($sessionId, $hashid, $imageId); // Register a banner click in Doofinder
$client->registerRedirection($sessionId, $hashid,
$redirectionId, $link, $options); // Register a redirection in Doofinder
$client->addToCart($sessionId, $hashid, $id, $amount, $options); // Add an amount of item to the cart in the current session
$client->removeFromCart($sessionId, $hashid, $id, $amount, $options); // Remove an amount of item from the cart in the current session
$client->clearCart($sessionId, $hashid); // Clear the cart in the current session
$client->setCustomHeaders($headers); // Add custom headers to all requests
$results->getProperty($propertyName); // Get the property $propertyName
$results->getResults(); // Get results
$results->getFacetsNames(); // Array with facet names
$results->getFacet($facetName); // Obtain search results for facet $facetName
$results->getFacets(); // All facets
$results->getAppliedFilters(); // Filters that have been applied to obtain these results
$results->isOk(); // Checks if all went OK
$results->status; // Account status info. 'success', 'exhausted', 'notfound'
<?php
require_once('path/to/php-doofinder/autoload.php');
define('HASHID', '6a9gbc4dcx735x123b1e0198gf92e6e9');
define('SERVER', 'eu1-search.doofinder.com');
define('API_KEY', '384fdag73c7ff0a59g589xf9f4083bxb9727f9c3');
$client = new \Doofinder\Search\Client(SERVER, API_KEY);
// if no dfParam_query, fetch all the results, to fetch all possible facets
$results = $client->search(array("transformer" => "basic"));
$query = $results->getProperty("query");
$queryName = $results->getProperty("query_name");
$page = $results->getProperty("page", 1);
$totalPages = $client->getTotalPages();
?>
<form method="GET" action="">
<input type="text" name="dfParam_query" onchange="emptyQueryName()" value="<?php echo $query ?>">
<input type="hidden" name="dfParam_rpp" value="3">
<input type="hidden" name="dfParam_transformer" value="basic">
<!-- this has to be removed via javascript if we want Doofinder to find the best search for us. -->
<input type="hidden" id="query_name" name="dfParam_query_name" value="<?php echo $queryName ?>">
<input type="submit" value="search!">
<p>Filter by:</p>
<ul>
<?php foreach ($results->getFacets() as $facetName => $facetResults): ?>
<li>
<?php echo $facetName ?>
<ul>
<?php if ($facetResults['_type'] == 'terms'): ?>
<?php foreach ($facetResults['terms'] as $term):?>
<li>
<input type="checkbox"
name="dfParam_filter[<?php echo $facetName ?>][]"
<?php echo $term['selected'] ? 'checked': ''?>
value="<?php echo $term['term']?>">
<?php echo $term['term']?>: <?php echo $term['count']?>
</li>
<?php endforeach ?>
<?php endif ?>
<?php if $facetResults['_type'] == 'range'): $range = $facetResults['ranges'][0]; ?>
<li>
Range: <?php echo $range['min']?> -- <?php echo $range['max']?><br/>
From: <input type="text" name="dfParam_filter[<?php echo $facetName?>][gte]" value="<?php echo $range['selected_from']?>">
To: <input type="text" name="dfParam_filter[<?php echo $facetName?>][lte]" value="<?php echo $range['selected_to']?>">
</li>
<?php endif?>
</ul>
</li>
<?php endforeach ?>
</ul>
</form>
<h1>Results</h1>
<ul>
<?php foreach ($results->getResults() as $result): ?>
<li><?php echo $result['header'] ?></li>
<?php endforeach ?>
</ul>
<?php if ($totalPages): ?>
<?php if ($page > 1): ?>
<a href="?<?php echo $client->qs(["page" => $page - 1]) ?>">Prev</a>
<?php endif?>
<?php if ($page < $totalPages): ?>
<a href="?<?php echo $client->qs(["page" => $page + 1]) ?>">Next</a>
<?php endif?>
<?php endif?>
<script>
// if the search box changes, a new query is being made
// don't tell Doofinder which search type to use
function emptyQueryName(){
document.getElementById('query_name').value = '';
return true;
}
</script>