silverstripe-framework/src/ORM/Filters/FulltextFilter.php

101 lines
3.0 KiB
PHP
Executable File

<?php
namespace SilverStripe\ORM\Filters;
use SilverStripe\ORM\DataQuery;
use SilverStripe\ORM\DataObject;
use Exception;
/**
* Filters by full-text matching on the given field.
*
* The following column types are supported:
* - Char
* - Varchar
* - Text
*
* To enable full-text matching on fields, you also need to add an index to the
* database table, using the {$indexes} hash in your DataObject subclass:
*
* <code>
* private static $indexes = [
* 'SearchFields' => [
* 'type' => 'fulltext',
* 'columns' => ['Name', 'Title', 'Description'],
* ];
* </code>
*
* @todo Add support for databases besides MySQL
*/
class FulltextFilter extends SearchFilter
{
protected function applyOne(DataQuery $query)
{
$this->model = $query->applyRelation($this->relation);
$predicate = sprintf("MATCH (%s) AGAINST (?)", $this->getDbName());
return $query->where([$predicate => $this->getValue()]);
}
protected function excludeOne(DataQuery $query)
{
$this->model = $query->applyRelation($this->relation);
$predicate = sprintf("NOT MATCH (%s) AGAINST (?)", $this->getDbName());
return $query->where([$predicate => $this->getValue()]);
}
public function isEmpty()
{
return $this->getValue() === [] || $this->getValue() === null || $this->getValue() === '';
}
/**
* This implementation allows for a list of columns to be passed into MATCH() instead of just one.
*
* @example
* <code>
* MyDataObject::get()->filter('SearchFields:fulltext', 'search term')
* </code>
*
* @throws Exception
* @return string
*/
public function getDbName()
{
$indexes = DataObject::getSchema()->databaseIndexes($this->model);
if (array_key_exists($this->getName(), $indexes ?? [])) {
$index = $indexes[$this->getName()];
} else {
return parent::getDbName();
}
if (is_array($index) && array_key_exists('columns', $index ?? [])) {
return $this->prepareColumns($index['columns']);
} else {
throw new Exception(sprintf(
"Invalid fulltext index format for '%s' on '%s'",
var_export($this->getName(), true),
var_export($this->model, true)
));
}
}
/**
* Adds table identifier to the every column.
* Columns must have table identifier to prevent duplicate column name error.
*
* @param array $columns
* @return string
*/
protected function prepareColumns($columns)
{
$prefix = DataQuery::applyRelationPrefix($this->relation);
$table = DataObject::getSchema()->tableForField($this->model, current($columns ?? []));
$fullTable = $prefix . $table;
$columns = array_map(function ($col) use ($fullTable) {
return "\"{$fullTable}\".\"{$col}\"";
}, $columns ?? []);
return implode(',', $columns);
}
}