Skip to content

Commit

Permalink
Merge pull request #701 from joomlatools/feature/700-searchable
Browse files Browse the repository at this point in the history
Searchable model behavior re-factoring
  • Loading branch information
amazeika authored Mar 27, 2024
2 parents 5dc01ce + 9504d7f commit d9f7998
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 35 deletions.
2 changes: 1 addition & 1 deletion code/libraries/joomlatools/library/koowa.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Koowa
*
* @var string
*/
const VERSION = '5.0.0';
const VERSION = '5.1.0';


/**
Expand Down
142 changes: 110 additions & 32 deletions code/libraries/joomlatools/library/model/behavior/searchable.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ class KModelBehaviorSearchable extends KModelBehaviorAbstract
*
* @var array
*/
protected $_columns;
protected $_columns = [];

protected $_alias_map = [];

/**
* Constructor.
Expand All @@ -33,7 +35,22 @@ public function __construct(KObjectConfig $config)
{
parent::__construct($config);

$this->_columns = (array)KObjectConfig::unbox($config->columns);
$columns = (array) KObjectConfig::unbox($config->columns);

foreach ($columns as $column)
{
$parts = explode('.', $column);

if (count($parts) >= 2)
{
$alias = array_shift($parts);
$column = implode(',', $parts);

$this->_alias_map[$column] = $alias;
}

$this->_columns[] = $column;
}

$this->addCommandCallback('before.fetch', '_buildQuery')
->addCommandCallback('before.count', '_buildQuery');
Expand Down Expand Up @@ -67,7 +84,8 @@ public function onMixin(KObjectMixable $mixer)
parent::onMixin($mixer);

$mixer->getState()
->insert('search', 'string');
->insert('search', 'string')
->insert('search_by', 'string', 'exact');
}

/**
Expand All @@ -79,51 +97,111 @@ public function onMixin(KObjectMixable $mixer)
*/
protected function _buildQuery(KModelContextInterface $context)
{
$state = $context->state;
$search = $state->search;

$combination = $context->_combination ?? 'AND';

$model = $context->getSubject();

if ($model instanceof KModelDatabase && !$context->state->isUnique())
{
$state = $context->state;
$search = $state->search;
list($conditions, $binds) = $this->_getConditions($search, $context);

if ($search)
if ($conditions)
{
$search_column = null;
$columns = array_keys($this->getTable()->getColumns());
$context->query->where('(' . implode(' OR ', $conditions) . ')', $combination);

// Parse $state->search for possible column prefix
if (preg_match('#^([a-z0-9\-_]+)\s*:\s*(.+)\s*$#i', $search, $matches))
{
if (in_array($matches[1], $this->_columns) || $matches[1] === 'id') {
$search_column = $matches[1];
$search = $matches[2];
}
foreach ($binds as $key => $value) {
$context->query->bind(array($key => $value));
}
}
}
}

// Search in the form of id:NUM
if ($search_column === 'id')
{
$context->query->where('(tbl.' . $this->getTable()->getIdentityColumn() . ' = :search)')
->bind(array('search' => $search));
protected function _getConditions($search, KModelContextInterface $context)
{
$state = $context->state;

$prefix = $context->_prefix ?? '';

$conditions = [];
$binds = [];

if ($search)
{
$search_column = null;

// Parse $state->search for possible column prefix
if (preg_match('#^([a-z0-9\-_]+)\s*:\s*(.+)\s*$#i', $search, $matches))
{
if (in_array($matches[1], $this->_columns) || $matches[1] === 'id') {
$search_column = $matches[1];
$search = $matches[2];
}
else
}

// Search in the form of id:NUM
if ($search_column !== 'id')
{
foreach ($this->_columns as $column)
{
$conditions = array();

foreach ($this->_columns as $column)
if (!$search_column || $column === $search_column)
{
if (in_array($column, $columns) && (!$search_column || $column === $search_column)) {
$conditions[] = 'tbl.' . $column . ' LIKE :search';
}
}
$alias = $this->_alias_map[$column] ?? 'tbl';

switch ($state->search_by)
{
case 'any':

$conditions[] = $alias . '.' . $column . ' RLIKE :search' . $prefix;

if ($conditions)
{
$context->query->where('(' . implode(' OR ', $conditions) . ')')
->bind(array('search' => '%' . $search . '%'));
if (empty($binds)) {
$binds['search' . $prefix] = implode('|', explode(' ', $search));
}

break;

case 'all':

$i = 0;

$subconditions = [];

foreach (explode(' ', $search) as $keyword)
{
$subconditions[] = $alias . '.' . $column . " LIKE :search$prefix$i";

$binds["search$prefix$i"] = '%'.$keyword.'%';

$i++;
}

$conditions[] = '(' . implode(' AND ', $subconditions) . ')';

break;

case 'exact':
default:

$conditions[] = $alias . '.' . $column . " LIKE :search" . $prefix;

if (empty($binds)) {
$binds['search' . $prefix] = '%' . $search . '%';
}

break;
}
}
}
}
else
{
$conditions[] = '(tbl.' . $this->getTable()->getIdentityColumn() . ' = :search' . $prefix . ')';
$binds['search' . $prefix] = $search;
}
}

return [$conditions, $binds];
}
}
4 changes: 2 additions & 2 deletions code/plugins/system/joomlatools/joomlatools.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
<extension version="3.5" type="plugin" group="system" method="upgrade" overwrite="true">
<name>plg_system_joomlatools</name>
<author>Joomlatools</author>
<creationDate>February 2024</creationDate>
<creationDate>March 2024</creationDate>
<copyright>Copyright (C) 2007 Timble CVBA (http://www.timble.net)</copyright>
<license>GNU GPLv3 - http://www.gnu.org/licenses/gpl.html</license>
<authorEmail>[email protected]</authorEmail>
<authorUrl>www.joomlatools.com</authorUrl>
<version>5.0.0</version>
<version>5.1.0</version>
<description />

<scriptfile>script.php</scriptfile>
Expand Down

0 comments on commit d9f7998

Please sign in to comment.