Andy
Andy

Reputation: 5395

CakePHP 3 - Paginating a custom finder

CakePHP 3.7

I'm trying to paginate some data from a custom finder and it's either not working or giving errors.

What I have:

// src/Model/Table/FiltersTable.php
public function findFilters($rf_keywords = '')
{
    // ...
}

The function above, findFilters() is a custom finder which accepts a parameter $rf_keywords and performs a search across various tables. I know this works because in a Controller I can do this:

$Filters = TableRegistry::getTableLocator()->get('Filters');
debug($Filters->findFilters('foo'));

This outputs an array of data based on searching for a string "foo". However this can return >1000 records so I want to paginate it to 25 per page.

So I've read https://book.cakephp.org/3.0/en/controllers/components/pagination.html and tried to modify my Controller:

  1. Loading the Paginator component:

    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Paginator');
    }
    
  2. Setting $paginate to use my custom finder:

    public $paginate = [
        'finder' => 'filters',
        'limit' => 25
    ];
    
  3. Following the information on the docs above passing in the request data and getting it to paginate:

    $request_data = $this->request->getParam('pass');
    
    $customFinderOptions = [
        'filters' => $request_data
    ];
    
    $this->paginate = [
        'finder' => [
            'filters' => $customFinderOptions
        ]
    ];
    

When I do debug($this->paginate($this->Filters)); it gives a Runtime Exception:

Unable to locate an object compatible with paginate.

The Controller I'm using is src/Controller/WizardController.php. In this case there is no FiltersController.php but I don't think that's the source of the error?

Please can someone advise how to get this working?

I'm also unsure how to pass in $rf_keywords. Previously I passed it in as a parameter, i.e.

$rf_keywords = trim($this->request->getData('rf_keywords'));
$data = $Filters->getFilters($rf_keywords); 

Since this is request data is it passed in the equivalent of $request_data in my code above? I don't see how that would work as it's using a parameter called pass.

Upvotes: 0

Views: 600

Answers (1)

Dariusz Majchrzak
Dariusz Majchrzak

Reputation: 1237

Firstly, cakephp has convenient method to create custom finders.

In model, You should create finder like this:

// src/Model/Table/FiltersTable.php
public function findFilters(Query $query, array $options = []): Query
{
    return $query 
        ->where(['column' => $options['rf_keywords']]);
}

In WizardsController, You should load model Filters first, then call this finder not directly by calling method FiltersTable::findFilters() but by passing finder name as param to FiltersTable::find() method

/**
 *  @method GET
 *  @return Cake\Http\Response|NULL
 */
public function index() 
{
    $this->loadComponent('Paginator');
    $this->loadModel('Filters');

    $finder = $this
        ->Filters
        ->find('filters' , [
            'rf_keywords' => $this->request->getData('rf_keywords')
        ]);

    $result = $this
        ->paginate($finder)
        ->toArray();

    dump($result);die;
}

Remember: In controller $this->request->getData($param) allows You to access POST params

To access GET params, You can use $this->request->getQuery($param)

Upvotes: 1

Related Questions