Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ionas/10b8f0bb847d48028bc2 to your computer and use it in GitHub Desktop.
Save ionas/10b8f0bb847d48028bc2 to your computer and use it in GitHub Desktop.
An CakePHP3 autosearch behavior for the friendsofcake/search plugin
<?php
namespace App\Model\Behavior;
use Cake\ORM\Behavior;
/**
* AutoSearch behavior
*/
class AutoSearchBehavior extends Behavior
{
/**
* Constructor hook method.
*
* Implement this method to avoid having to overwrite
* the constructor and call parent.
*
* @param array $config The configuration settings provided to this behavior.
* @return void
*/
public function initialize(array $config)
{
parent::initialize($config);
if ($this->_isBake()) {
return;
}
$this->setupSearch();
}
public function setupSearch() {
$table = $this->_table;
$columns = $table->schema()->typeMap();
foreach ($table->associations() as $association) {
if ($association->type() == 'manyToOne') {
$table->searchManager()->add(
$association->aliasField($association->displayField()), 'Search.Like', [
'field' => $association->aliasField($association->displayField()),
'filterEmpty' => true,
'before' => true,
'after' => true,
]);
unset($columns[$association->foreignKey()]);
}
}
foreach ($columns as $columnName => $columnType) {
if (strpos($columnType, 'integer') !== false) {
$table->searchManager()->add($columnName, 'Search.Like', [
'field' => $table->aliasField($columnName),
'filterEmpty' => true,
]);
} elseif ($columnType === 'boolean') {
$table->searchManager()->add($columnName, 'Search.Value', [
'field' => $table->aliasField($columnName),
'filterEmpty' => false,
]);
} else {
$table->searchManager()->add($columnName, 'Search.Like', [
'field' => [$table->aliasField($columnName)],
'filterEmpty' => true,
'before' => true,
'after' => true,
]);
}
}
}
private function _isBake() {
global $argv;
if (PHP_SAPI === 'cli' && $argv[1] === 'bake') {
return true;
}
return false;
}
}
@ionas
Copy link
Author

ionas commented Dec 22, 2015

Vanilla interface (@2015-12-22 dev-master) towards friendsofcake/search, except that you are required to...

  • prefix associated records' displayNames in the filter forms, like so: $this->Form->input('Authors.name'), and...
  • wrap the request query pass to filterParams() with a Hash::flatten() like so: $this->Post->filterParams(Hash::flatten($this->request->query)).

@ionas
Copy link
Author

ionas commented Dec 22, 2015

Update:

  • Behavior does not run during bake time (crashes on recursion).
  • Integers are filtered by LIKE, too.

@ionas
Copy link
Author

ionas commented Dec 22, 2015

  • EDIT: BROKEN, see BELOW for a working version.
  • Update stopping recursion on self-joining models (with help by savant 💯xTHX)
<?php
namespace App\Model\Behavior;

use Cake\ORM\Behavior;

/**
 * AutoSearch behavior
 */
class AutoSearchBehavior extends Behavior
{

    /**
    * Constructor hook method.
    *
    * Implement this method to avoid having to overwrite
    * the constructor and call parent.
    *
    * @param array $config The configuration settings provided to this behavior.
    * @return void
    */
    public function initialize(array $config)
    {   
        parent::initialize($config);
        if ($this->_isBake()) {
            return;
        }
        $this->setupSearch();
    }

    public function setupSearch() {
        $table = $this->_table;
        $columns = $table->schema()->typeMap();
        foreach ($table->associations() as $association) {
            if ($association->type() !== 'manyToOne') {
                break;
            }
            if (($table instanceof \App\Model\Table) === false) {
                break;
            }
            $associationClass = get_class($association);

            $displayField = $table->displayField();
            $field = $table->aliasField($displayField);
            $foreignKey = $table->foreignKey();

            if ($currentClass !== $associationClass) {
                $displayField = $association->displayField();
                $field = $association->aliasField($displayField);
                $foreignKey = $association->foreignKey();
            }

            $table->searchManager()->add(
                $association->aliasField($displayField), 
                'Search.Like', 
                [
                    'field' => $field,
                    'filterEmpty' => true,
                    'before' => true,
                    'after' => true,
                ]
            );
            unset($columns[$foreignKey]);
        }
        foreach ($columns as $columnName => $columnType) {
            if (strpos($columnType, 'integer') !== false) {
                $table->searchManager()->add($columnName, 'Search.Like', [
                    'field' => $table->aliasField($columnName),
                    'filterEmpty' => true,
                ]);
            } elseif ($columnType === 'boolean') {
                $table->searchManager()->add($columnName, 'Search.Value', [
                    'field' => $table->aliasField($columnName),
                    'filterEmpty' => false,
                ]);
            } else {
                $table->searchManager()->add($columnName, 'Search.Like', [
                    'field' => [$table->aliasField($columnName)],
                    'filterEmpty' => true,
                    'before' => true,
                    'after' => true,
                ]);
            }
        }
    }

    private function _isBake() {
        global $argv;
        if (PHP_SAPI === 'cli' && $argv[1] === 'bake') {
            return true;
        }
        return false;
    }

}

@ionas
Copy link
Author

ionas commented Dec 23, 2015

Above code does not run. This runs (interim solution):

<?php
namespace App\Model\Behavior;

use Cake\ORM\Behavior;

/**
 * AutoSearch behavior
 */
class AutoSearchBehavior extends Behavior
{

    /**
    * Constructor hook method.
    *
    * Implement this method to avoid having to overwrite
    * the constructor and call parent.
    *
    * @param array $config The configuration settings provided to this behavior.
    * @return void
    */
    public function initialize(array $config)
    {   
        parent::initialize($config);
        if ($this->_isBake()) {
            return;
        }
        $this->setupSearch();
    }

    public function setupSearch() {
        $tableClass = $this->_table;
        $columns = $tableClass->schema()->typeMap();
        foreach ($tableClass->associations() as $associationKey => $association) {
            if (strpos($association->name(), 'Parent') === 0 || strpos($association->name(), 'Child') === 0) {
                break;
            }
            // foreach ($association->source()->associations() as $nestedAssociation) {
            //     if ($association->name() === $nestedAssociation->name()) {
            //         break 2;
            //     }
            // }
            if ($association->type() == 'manyToOne') {
                $tableClass->searchManager()->add(
                    $association->aliasField($association->displayField()), 'Search.Like', [
                        'field' => $association->aliasField($association->displayField()),
                        'filterEmpty' => true,
                        'before' => true,
                        'after' => true,
                ]);
                unset($columns[$association->foreignKey()]);
            }
        }
        foreach ($columns as $columnName => $columnType) {
            if (strpos($columnType, 'integer') !== false) {
                $tableClass->searchManager()->add($columnName, 'Search.Like', [
                    'field' => $tableClass->aliasField($columnName),
                    'filterEmpty' => true,
                ]);
            } elseif ($columnType === 'boolean') {
                $tableClass->searchManager()->add($columnName, 'Search.Value', [
                    'field' => $tableClass->aliasField($columnName),
                    'filterEmpty' => false,
                ]);
            } else {
                $tableClass->searchManager()->add($columnName, 'Search.Like', [
                    'field' => [$tableClass->aliasField($columnName)],
                    'filterEmpty' => true,
                    'before' => true,
                    'after' => true,
                ]);
            }
        }
    }

    private function _isBake() {
        global $argv;
        if (PHP_SAPI === 'cli' && $argv[1] === 'bake') {
            return true;
        }
        return false;
    }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment