ypnos-web / cakephp-datatables

CakePHP3 Plugin for DataTables plug-in for jQuery
MIT License
27 stars 24 forks source link

Matching HasMany Data question #33

Closed spacebiscuit closed 7 years ago

spacebiscuit commented 7 years ago

Hi Ypnos!

Is it possible to add 'matching' to the options paramater when calling dataTables. For example

Listings hasMany ListingsUsers

My $options paramater:

$options = [
    'conditions' => [
        (int) 0 => [
            'Listings.title LIKE' => '%foo%'
        ]
    ],
    'contain' => 'ListingsUsers',
    'matching' => [
        'Listings.id' => ListingsUsers.listing_id
    ]
];

The condition and contain are applied but not the matching to filter. Perhaps the solution is to pass a join in the $options?

Thank you in advance for your time.

ypnos-web commented 7 years ago

The options you provide are just passed-through to the finder: https://github.com/ypnos-web/cakephp-datatables/blob/master/src/Controller/Component/DataTablesComponent.php#L137

Can you check if it works as expected when you call the table finder directly?

spacebiscuit commented 7 years ago

Thanks - I figured and found where the finder is called for the model, unfortunately passing the 'matching' array didn't work, however I did manage to get it to work as follows:

$data = $table->find($finder);

if(isset($options['contain'])){
     $data->contain($options['contain']);
}

if(isset($options['conditions'])){
     $data->where($options['conditions']);
}     

if(isset($options['matching'])){

     foreach ($options['matching'] as $key => $match) {
          $data->matching($key, function ($q) use ($match) {
               return $q->where($match);
          });
     }   

}

I am setting my 'matching' option as follows:

$conditions[]; $contain[] = 'Vehicles'; $matching['Vehciles'] = ['Vehicles.id IN' => [1,2,3]];

$data = $this->DataTables->find('Listings', 'all', ['conditions' => $conditions, 'contain' => $contain, 'matching' => $matching]);

For the matching array I found I had to set the key to be the model which we want to match. I'm not sure if this is the most efficient way of doing this but I do get the expected results.

I could create a PR if you think this is an agreeable solution.

ypnos-web commented 7 years ago

First of all, there is no sense in replicating contains and conditions, as these already work as expected.

Second, I believe that the real issue at hands is that there is no matching option key for querys. Here is a list of recognized options I found in Cake\ORM\Query:

 * - fields: Maps to the select method
 * - conditions: Maps to the where method
 * - limit: Maps to the limit method
 * - order: Maps to the order method
 * - offset: Maps to the offset method
 * - group: Maps to the group method
 * - having: Maps to the having method
 * - contain: Maps to the contain options for eager loading
 * - join: Maps to the join method
 * - page: Maps to the page method

I understand that it is inconvenient not to be able to pass matching from the Controller right now. However what you can easily do is to write a custom finder in the Listings table that does the matching call for you. For example:

    public function findMatching(Query $query, array $options)
    {
        foreach ($options['matching'] as $key => $match) {
            $query->matching($key, function ($q) use ($match) {
                return $q->where($match);
            });
        }
       return $query;
    }

Then you can call $this->DataTables->find('Listings', 'matching', …. Obviously, you don't have to keep it as generic but can make your finder more specific to your needs.

For the future I suggest:

spacebiscuit commented 7 years ago

Thank you Ypnos - that's a very elegant solution. I agree that there is little point in replicating the 'contain' and 'condition' features since this already works. Your solution is an approach I wouldn't have thought of and is good practice for me.