<commit_before><?php
namespace DataTables\Controller\Component;

use Cake\Controller\Component;
use Cake\ORM\TableRegistry;

/**
 * DataTables component
 */
class DataTablesComponent extends Component
{

    protected $_defaultConfig = [
        'start' => 0,
        'length' => 10,
        'order' => [],
        'conditions' => [],
        'matching' => []
    ];

    protected $_viewVars = [
        'recordsTotal' => 0,
        'recordsFiltered' => 0,
        'draw' => 0
    ];

    protected $_isAjaxRequest = false;

    protected $_tableName = null;

    protected $_plugin = null;

    /**
     * Process query data of ajax request
     *
     */
    private function _processRequest()
    {
        // -- check whether it is an ajax call from data tables server-side plugin or a normal request
        $this->_isAjaxRequest = $this->request->is('ajax');

        // -- add limit
        if( isset($this->request->query['length']) && !empty($this->request->query['length']) )
        {
            $this->config('length', $this->request->query['length']);
        }

        // -- add offset
        if( isset($this->request->query['start']) && !empty($this->request->query['start']) )
        {
            $this->config('start', (int)$this->request->query['start']);
        }

        // -- add order
        if( isset($this->request->query['order']) && !empty($this->request->query['order']) )
        {
            $order = $this->config('order');
            foreach($this->request->query['order'] as $item) {
                $order[$this->request->query['columns'][$item['column']]['name']] = $item['dir'];
            }
            $this->config('order', $order);
        }

        // -- add draw (an additional field of data tables plugin)
        if( isset($this->request->query['draw']) && !empty($this->request->query['draw']) )
        {
            $this->_viewVars['draw'] = (int)$this->request->query['draw'];
        }

        // -- check columns

        if( isset($this->request->query['columns']) && !empty($this->request->query['columns']) )
        {
            foreach($this->request->query['columns'] as $column) {
                if( !empty($column['search']['value']) ) {
                    $this->_addCondition( $column['name'], $column['search']['value'] );
                }
            }
        }

    }

    /**
     * Find data
     *
     * @param $tableName
     * @param array $options
     * @return array|\Cake\ORM\Query
     */
    public function find($tableName, array $options = [])
    {

        // -- get table object
        $table = TableRegistry::get($tableName);
        $this->_tableName = $table->alias();

        // -- get query options
        $this->_processRequest();
        $data = $table->find('all', $options);

        // -- record count
        $this->_viewVars['recordsTotal'] = $data->count();

        // -- filter result
        $data->where( $this->config('conditions') );
        foreach($this->config('matching') as $association => $where) {
            $data->matching( $association, function ($q) use ($where) {
                return $q->where($where);
            });
        };

        $this->_viewVars['recordsFiltered'] = $data->count();

        // -- add limit
        $data->limit( $this->config('length') );
        $data->offset( $this->config('start') );

        // -- sort
        $data->order( $this->config('order') );

        // -- set all view vars to view and serialize array
        $this->_setViewVars();
        return $data;

    }

    private function _getController()
    {
        return $this->_registry->getController();
    }

    private function _setViewVars()
    {
        $_serialize = [];
        foreach($this->_viewVars as $field => $value) {
            $_serialize[] = $field;
        }
        $this->_getController()->set($this->_viewVars);
        $this->_getController()->set('_serialize', $_serialize);
    }

    private function _addCondition($column, $value)
    {
        $condition = ["$column LIKE" => "$value%"];

        list($association, $field) = explode('.', $column);
        if( $this->_tableName == $association) {
            $this->config('conditions', $condition); // merges
        } else {
            $this->config('matching', [$association => $condition]); // merges
        }
    }

}
<commit_msg>implement table-wide search functionality<commit_after><?php
namespace DataTables\Controller\Component;

use Cake\Controller\Component;
use Cake\ORM\TableRegistry;

/**
 * DataTables component
 */
class DataTablesComponent extends Component
{

    protected $_defaultConfig = [
        'start' => 0,
        'length' => 10,
        'order' => [],
        'conditionsOr' => [],  // table-wide search conditions
        'conditionsAnd' => [], // column search conditions
        'matching' => [],      // column search conditions for foreign tables
    ];

    protected $_viewVars = [
        'recordsTotal' => 0,
        'recordsFiltered' => 0,
        'draw' => 0
    ];

    protected $_isAjaxRequest = false;

    protected $_tableName = null;

    protected $_plugin = null;

    /**
     * Process query data of ajax request
     *
     */
    private function _processRequest()
    {
        // -- check whether it is an ajax call from data tables server-side plugin or a normal request
        $this->_isAjaxRequest = $this->request->is('ajax');

        // -- add limit
        if( isset($this->request->query['length']) && !empty($this->request->query['length']) )
        {
            $this->config('length', $this->request->query['length']);
        }

        // -- add offset
        if( isset($this->request->query['start']) && !empty($this->request->query['start']) )
        {
            $this->config('start', (int)$this->request->query['start']);
        }

        // -- add order
        if( isset($this->request->query['order']) && !empty($this->request->query['order']) )
        {
            $order = $this->config('order');
            foreach($this->request->query['order'] as $item) {
                $order[$this->request->query['columns'][$item['column']]['name']] = $item['dir'];
            }
            $this->config('order', $order);
        }

        // -- add draw (an additional field of data tables plugin)
        if( isset($this->request->query['draw']) && !empty($this->request->query['draw']) )
        {
            $this->_viewVars['draw'] = (int)$this->request->query['draw'];
        }

        // -- don't support any search if columns data missing
        if( !isset($this->request->query['columns']) ||
            empty($this->request->query['columns']) )
        {
            return;
        }

        // -- check table search field
        $globalSearch = (isset($this->request->query['search']['value']) ?
            $this->request->query['search']['value'] : false);

        // -- add conditions for both table-wide and column search fields
        foreach($this->request->query['columns'] as $column)
        {
            if( $globalSearch && $column['searchable'] == 'true' ) {
                $this->_addCondition( $column['name'], $globalSearch, 'or' );
            }
            $localSearch = $column['search']['value'];
            /* In some circumstances (no "table-search" row present), DataTables
               fills in all column searches with the global search. Compromise:
               Ignore local field if it matches global search. */
            if( !empty($localSearch) && ($localSearch !== $globalSearch) ) {
                $this->_addCondition( $column['name'], $column['search']['value'] );
            }
        }

    }

    /**
     * Find data
     *
     * @param $tableName
     * @param array $options
     * @return array|\Cake\ORM\Query
     */
    public function find($tableName, array $options = [])
    {

        // -- get table object
        $table = TableRegistry::get($tableName);
        $this->_tableName = $table->alias();

        // -- get query options
        $this->_processRequest();
        $data = $table->find('all', $options);

        // -- record count
        $this->_viewVars['recordsTotal'] = $data->count();

        // -- filter result
        $data->where($this->config('conditionsAnd'));
        foreach($this->config('matching') as $association => $where) {
            $data->matching( $association, function ($q) use ($where) {
                return $q->where($where);
            });
        };
        $data->andWhere(['or' => $this->config('conditionsOr')]);

        $this->_viewVars['recordsFiltered'] = $data->count();

        // -- add limit
        $data->limit( $this->config('length') );
        $data->offset( $this->config('start') );

        // -- sort
        $data->order( $this->config('order') );

        // -- set all view vars to view and serialize array
        $this->_setViewVars();
        return $data;

    }

    private function _getController()
    {
        return $this->_registry->getController();
    }

    private function _setViewVars()
    {
        $_serialize = [];
        foreach($this->_viewVars as $field => $value) {
            $_serialize[] = $field;
        }
        $this->_getController()->set($this->_viewVars);
        $this->_getController()->set('_serialize', $_serialize);
    }

    private function _addCondition($column, $value, $type = 'and')
    {
        $condition = ["$column LIKE" => "$value%"];

        if( $type === 'or' ) {
            $this->config('conditionsOr', $condition); // merges
            return;
        }

        list($association, $field) = explode('.', $column);
        if( $this->_tableName == $association) {
            $this->config('conditionsAnd', $condition); // merges
        } else {
            $this->config('matching', [$association => $condition]); // merges
        }
    }
}

{'fraction_non_alphanumeric': 0.18241735956195498, 'fraction_numerical': 0.0012167917258162643, 'mean_word_length': 2.056399132321041, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 181, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6979917', 'n_tokens_mistral': 2741, 'n_tokens_neox': 2674, 'n_words': 554}