cakephp 3 matching not working - cakephp

I have categories, products and seller_products table and want to select all from three tables group by categories where seller_products.stock > 0 and seller_products.status = 1
This is my code
$pros1 = $this->Products->Categories->find()
->where([
])
->select(['Categories.id', 'Categories.title'])
->distinct(['Categories.id'])
->contain(['Products'])
->matching('Products.SellerProducts', function(\Cake\ORM\Query $q) {
return $q->where(['SellerProducts.stock >' => 0, 'SellerProducts.status' => 1]);
});
But this is not working. I'm getting all products group by categories by matching is not working and still getting products whose stock is 0 or status is not 1
Their associations are
$categories->hasMany('Products');
$products->hasMany('SellerProducts');

You need to understand that contain() and matching() are different. You are asking for categories having products where there is stock. But if you want what products are those that matched, you need to filter them in contain() as well:
$matcher = function(\Cake\ORM\Query $q) {
return $q->where(['SellerProducts.stock >' => 0, 'SellerProducts.status' => 1]);
};
$pros1 = $this->Products->Categories->find()
->select(['Categories.id', 'Categories.title'])
->distinct(['Categories.id'])
->contain(['Products' => function ($q) use ($matcher) {
return $q->matching('SellerProducts', $matcher);
}])
->matching('Products.SellerProducts', $matcher);

Related

How to apply datatable filter when my table is passed from js?

I have js code which gets results from a query and pastes table to a div.So table is created in js and set with js.how do I apply datatables filter in such a case?
if you are using ajax request filter ,So url parameter filter may help :
for example :
This is your ajax get request url :
$.get(root_url+'index/user?id='+id+'&name='+name+'&gender='+gender, function(response){
console.log(response);
});
And this how you can caught those parameter in cakephp
public function user(){
if ($this->request->is('ajax')) {
$id = $this->request->getQuery('id');
$name = $this->request->getQuery('name');
$gender = $this->request->getQuery('gender');
$condition = [];
if ($id){
$condition = ['Users.id' => $id];
}
if (name) {
$condition = ['Users.name' => $name];
}
if (gender) {
$condition = ['Users.gender' => $gender];
}
$user = $this->Users->find()
->select([
'Users.id',
'Users.name',
'Users.gender',
'Users.create_date',])
->where(condition);
response = ['user' => $user];
return $this->response->withType('application/json')
->withStringBody(json_encode($response));
} else {
return $this->redirect(['controller' => 'pages','action' => 'error404']);
}
}

Laravel sort by keys

I make an array with collections with code:
$hours = Voucher::where('created_at','>=', Carbon::now()->startOfMonth())->get()->groupBy(function($item)
{
return $item->created_at->format('H');
});
dd($hours);
so I get data like this:
SO keys are hours (00-23).
How I can arrange it starting by 00, 01, 02, 03 ... ...
https://laravel.com/docs/5.6/collections#method-sortkeys
$collection = collect([
'id' => 22345,
'first' => 'John',
'last' => 'Doe',
]);
$sorted = $collection->sortKeys();
Update for 5.1
Since you're using Laravel 5.1 and there is no sortKeys method, you can sort the collection before you group it which should result in the keys being in the correct order.
$collection->sort(function($a, $b) {
return strcmp($a->created_at->format('H'), $b->created_at->format('H'));
})->groupBy(function($item) {
return $item->created_at->format('H');
});
Try this code.
$hours = Voucher::where('created_at','>=', Carbon::now()->startOfMonth())->get()->sortBy(function($item)
{
return $item->created_at->format('H');
});
$hours = $hours->groupBy(function($item)
{
return $item->created_at->format('H');
});
dd($hours);

Multiple matching() and contain(), condition on related records count

With CakePHP 3.x, I have 3 models : StudentProfile, Diploma1 and Diploma2.
StudentProfile hasMany Diploma1
StudentProfile hasMany Diploma2
Diploma1 has an integer "state" field.
I need to get StudentProfiles which :
have one (or more) related Diploma1 where Diploma1.state = 2
OR
have one (or more) Diploma2 (no condition on Diploma2 fields)
I need to retrieve the matching Diploma1 and Diploma2 with my StudentProfiles.
I'm using the Search and Paginator components, so I have to do this with one query.
For now, I'v been able to get the first part by doing :
$query = $this->StudentProfiles
->find('search', $this->StudentProfiles->filterParams($this->request->query))
->contain(['Diploma1' => function ($q) {
return $q->where(['Diploma1.state' => 2]);
}])
->matching('Diploma1', function($q) {
return $q->where(['Diploma1.state' => 2]);
})
->distinct(['StudentProfiles.id'])
;
$this->set('studentProfiles', $this->paginate($query));
Combining matching and contain allows me to add the condition and get related Diploma1 (as I understand it).
Now I need to get also all the StudentProfiles with a related Diploma2, this is where I get stuck. If I add
->contain(['Diploma2'])
...to my query, I only get Diploma2 for StudentProfiles that have a matching Diploma1 (where state=2), but I don't get StudentProfiles with related Diploma2 only (without matching Diploma1), which is perfectly normal.
So I have 2 questions :
how can I get all StudentProfiles that have a related Diploma2 (i.e add a condition using count(...) > 0 maybe ?)
how can I combine this with a matching clause with a condition (state=2) ?
I hope this is clear.
Thanks
A slightly different approach, but maybe it helps
$query = $this->Profile->find();
$query->select([
'Profile.id',
'Profile.name',
'D1.id',
'D1.name',
'D1.status',
'D1.profile_id',
'D2.id',
'D2.name',
'D2.status',
'D2.profile_id',
'd1c' => 'COUNT(D1.id)',
'd2c' => 'COUNT(D2.id)',
]);
$query->contain([
'D1' => function($q) {
$q->where(['D1.status' => 2]);
return $q;
},
'D2'
]);
$query->leftJoinWith('D1', function($q) {
return $q->where(['D1.status' => 2]);
});
$query->leftJoinWith('D2', function($q) {
return $q;
});
$query->having(function($q) {
return $q->or_([
'd1c >' => 0,
'd2c >' => 0,
]);
});
$query->group('Profile.id');
I couldn't really get contain to create a join, so I had to add those leftJoinWith

Cakephp3 passing an array to where condition

I'm working on this piece of code:
public function validateSelected($array = [1, 2])
{
$this->autoRender = false;
$samples = TableRegistry::get('Samples');
$samples->query()
->update()
->set(['validate' => true])
->where(function ($exp, $q) {
return $exp->in('id', $q);
})
->execute();
}
The code is pretty much self explanatory, I want to update all the rows with id's that would be passed in an array to the function.
I've tested the code by doing this:
->where(function ($exp, $q) {
return $exp->in('id', [1, 2, 3]);
})
And its working fine but i cant pass the passed array parameter to this where condition as it gives a syntax error undeclared variable $array.
Any idea about how can i achieve this thing would be great.
you can use the use language construct to include external variables into the anonymous function
$ids = [1, 2, 3];
->where(function ($exp, $q) use($ids) {
return $exp->in('id', $ids ]);
})
but you can simply do
->where(['id IN' => $ids])
or also
->where(['id' => $ids], ['id' => 'integer[]'])
see the manual

Using 'chunks' together with 'with' on Laravel query builder

dunno if this is possible at all.. I'm trying to query a large set of data with relations like so:
Parent::with([
'child' => function($query) {
$query->('published', '=', true);
$query->with('child.of.child', 'some.other.child');
$query->chunk(400, function($childs) {
// how is it now possible to add the $childs to the parent result??
});
}
]);
$parent = [];
Parent::with(
[
'childs' => function ($query) use (&$parent) {
$query->where('STATUS', '!=', 'DELETED');
$query->with('some.child', 'some.other.child');
$parent['models'] = $query->getParent()
->getModels();
$query->chunk(
400, function ($result) use ($query, &parent) {
$query->match($parent['models'], $result, 'child-relations-name');
});
}
])
->get();
Now $parent['models'] contains the tree with all the nested child relations... Dunno if this is the smartest way to do so, but it works for now.

Resources