Welcome Guest, Not a member yet? Register   Sign In
Is a beforeFind on a Model a bad idea?
#1

Reworking one project into a pure front/back-end solution with a REST API in CI4 I have been thinking about a way to implement searching and paging of the data returned by find. I have currently a working solution by creating a clone of Model::findAll() and handling paging there. Working, but not the nicest maybe.
Thinking about other possibilities, I wonder why there isn't a beforeFind event where I can get access to the QueryBuilder object to allow manipulation of the find parameters.
What am I missing?

/Mattias
Reply
#2

This was actually one of the things that bothered me when I started using ci4. The base model I used to use back in ci3 also had callbacks and it had before and after find. Don't really know why ci4 have all the callbacks built in except this one.
Website: marcomonteiro.net  | Blog: blog.marcomonteiro.net | Twitter: @marcogmonteiro | TILThings: tilthings.com
Reply
#3

I think I sort of have worked around this. I created a new BaseModel containing the logic for certain things:


PHP Code:
<?php


namespace App\Models;


use 
App\Classes\JsonStoreResponse;
use 
Config\Services;

class 
BaseModel extends \CodeIgniter\Model
{

    protected $useTimestamps true;
    protected $useSoftDeletes false;

    /**
    * Handle pagination with Ext.data.virtual.Store
    * 
    * @return JsonStoreResponse
    */
    public function getVirtualStoreData()
    {
        $request Services::request();

        $start $request->getGetPost('start');
        $limit $request->getGetPost('limit');

        $builder $this->builder();
        $builder->limit($limit$start);

        $sql str_replace('SELECT''SELECT SQL_CALC_FOUND_ROWS'$builder->getCompiledSelect());
        echo $sql;
        $query $this->db->query($sql);
        $row $query->getResult($this->tempReturnType);
        $query->freeResult();

        $query $this->db->query('SELECT FOUND_ROWS() AS `total`');
        $res $query->getRowObject();

        $result = new JsonStoreResponse(true$res->total);
        $result->setData($row);

        return $result;
    }

    /**
    * Check the item_version of the new data and make sure the data is in sync between
    * front- and back-end before updating.
    *
    * @param $id
    * @param $new_data
    *
    * @return bool
    * @throws \ReflectionException
    */
    public function validateVersionAndUpdate($id$new_data)
    {
        $record $this->find($id);

        // Ext JS increments the item_version by one when saving.
        // item_version - 1 must match what we have in database, otherwise another edit
        // has been made and we bail out.
        if ((int)$new_data->item_version !== (int)$record->item_version) {
            return false;
        }

        $this->update($id$new_data);
        return true;
    }



All my models then extend from this BaseModel and get access to common functions without using events.
Working so far...
Reply
#4

(06-29-2020, 10:29 PM)tgix Wrote: What am I missing?

/Mattias

It is possible to define custom events. The (static) class method Events::on() allows you to register an event using any name you like.

File: /app/Config/Events.php

PHP Code:
Events::on('before_findAll', [$modelInstance'modelMethod']);  // Call modelMethod on an existing Model instance 


At the appropriate logical spot in your code, something like this

PHP Code:
\CodeIgniter\Events\Events::trigger('before_findAll'$model'modelMethod'); 

You can pass other arguments in trigger if needed.

Events Docs
Reply
#5

I never found a consistent use for a beforeFind event, honestly If people feel it's useful then feel free to submit a PR.

For things like searching and filtering for an index page you can always chain methods together. Something like:

In controller:
PHP Code:
$users $userModel->filter($this->request->getPost())->paginate(20); 

and the new method in the model might be something like:

PHP Code:
public function filter($params
{
    if(isset(
$params['search'] && ! empty($params['search'])) {
        
$this->like('name'$params['search'].'%');
    }
    if(isset(
$params['city_id'] && ! empty($params['city_id'])) {
        
$this->like('city_id'$params['city_id'].'%');
    }

    
// Sorting
    
$sortBy = isset($params['sort']) && trim($params['sort'] !== '')
        ? 
$params['sort']
        : 
'first_name';
    
$sortDir = isset($params['sort_dir']) && in_array($params['sort_dir'], ['asc''desc'])
        ? 
$params['sort_dir']
        : 
'asc';
    
$this->orderBy($sortBy$sortDir);

    return 
$this;

Reply
#6

(06-30-2020, 12:02 PM)kilishan Wrote: For things like searching and filtering for an index page you can always chain methods together.

Thanks. By creating the BaseModel and then extending all my models from that base I think the code is pretty clean and contained in the Model to offload the Controllers.
Reply
#7

(This post was last modified: 07-02-2020, 10:37 PM by Wells.)

Doing a project into a front-end / backend solution with the REST API in CI4 I've been thinking of a way to optimize the availability and paging of data returned by search.We are here.  I have a current working solution by creating a clone of Model :: FindAll () and maintaining paging there. Seriously, but not the best either.
Reply
#8

Could everyone invested in this thread please check out my beforeFind PR and provide any feedback before it is merged?

https://github.com/codeigniter4/CodeIgniter4/pull/3344
Reply




Theme © iAndrew 2016 - Forum software by © MyBB