Cakephp 3.0: Multiple matching conditions - cakephp

I'm trying to daisy-chain matching() calls on a query, or find an equivalent way to achieve the same result. Specifically, here's what I'd like to do:
$matchedItemVersion = $this->ItemVersions->find()
->where(['version' => $verion])
->matching('Items', function($q) use ($ipnCore) {
return $q->where(['ipn_core' => $ipnCore]);
})
->matching('Items.ItemTypes.ItemSuperGroups', function($q) use ($itemSuperGroupName) {
return $q->where(['name' => $itemSuperGroupName]);
})
->first()
->toArray();
This would let me filter my ItemVersions results by two conditions on associated models. However, right now, the second matching() call basically overrides the first one, so the first matching() call has no effect on the query.
We know that you cannot chain a matching() and a contain()(https://github.com/cakephp/cakephp/issues/5109), but is there a way to do it with matching(), or a way to get the same result?
Appreciate the help.

Related

Sorting on nested relationship

I have the following relationship:
Rounds hasMany Results
Results belongsTo Drivers
Results are shown on Rounds's view page: rounds/view/{id}. I want to sort Results based on Drivers.name.
This is my RoundsController::view method:
public function view($id = null)
{
$this->paginate = [
'sortableFields' => [
'Results.Drivers.name',
]
];
$round = $this->Rounds->get($id, [
'contain' => ['Championships' => ['Teams'], 'Tracks', 'Results' => ['Drivers', 'Constructors']],
]);
$this->set(compact('round'));
}
And in my rounds/view.ctp file, I have this:
<?= $this->Paginator->sort('Results.Drivers.name', __('Driver')); ?>
However, no matter what I change, it doesn't sort by driver name. Also, when I click it multiple times, the direction stays asc.
Why is it not working?
Like #ndm mentioned, your code is showing only one specific Round. View method/function is receiving an $id arg, this $id is related to the round id you want to show in view page.
You can not order this (you can do it but it doesnt make sense) because you are receiving only one Round, and it doesnt make sense to order only one row.
This view method is usually used with this goal: show individual/personal information about an Entity (Round entity in your case). Please, note I use the word USUALLY.
I think you could use the index method for this purpose (this method/function is usually used to list entities from a model and its association data). You could create a new method/function for your porpuse as well if you already are using index method for another goal.
I suggest something like this, assuming you will use the index function:
public function index()
{
$rounds = $this->Rounds->find('all')
->contain([
'Championships' => ['Teams'],
'Tracks',
'Results' => [
'Constructors'
[
'Drivers' => function (Query $q) {
return $q->order('Drivers.name');;
}
]
]
]);
$rounds= $this->paginate($rounds);
$this->set(compact('rounds'));
}
You could pass conditions into the contain or do the same into paginate method. I always use the first (just preferences).
NOTE: this code may not work, it is an idea of what you could do.
Finders documentation: https://book.cakephp.org/4/en/orm/retrieving-data-and-resultsets.html#using-finders-to-load-data
Passing conditions to contains: https://book.cakephp.org/4/en/orm/query-builder.html#passing-conditions-to-contain

I have a few questions about Join in Cakephp

Thank you for reading it.
I'm studying cakephp and have a few questions about Join in Cakephp.
I coudn't find any appropriate answer on internet
1, What is difference between innerjoin and innerjoinwith (like leftjoin and leftjoinwith) I thought It is related with performence issue, but couldn't find any clue.
2, I can't get the exact difference between "matching" and "contain"
I know the way they retrieving data is different, but I thought the result looks same as the result of matching and matching is using innerjoin, and contain is using leftjoin.
but but I can't find what is the difference between the code below(using contain) and matching.
$query = $articles->find()->contain([
'Comments' => function ($q) {
return $q
->select(['body', 'author_id'])
->where(['Comments.approved' => true]);
}
]);
3, what is "_matchingData" in matching?
I read this description, that is
"The innerJoinWith() method works the same as matching(), that means that you can use dot notation to join deeply nested associations: Again, the only difference is that no additional columns will be added to the result set, and no _matchingData property will be set."
but I couldn't find any description about _matchingData. even in API... what it is?
you can response one of them, it is okay. please help me out
1, What is difference between innerjoin and innerjoinwith (like leftjoin and leftjoinwith) I thought It is related with performence issue, but couldn't find any clue.
=>
(1) here 'innerJoinWith' is a callable which will apply right/inner join
(2) for left join 'leftJoinWith' is the callable
https://book.cakephp.org/3.0/en/orm/query-builder.html#using-innerjoinwith
https://book.cakephp.org/3.0/en/orm/query-builder.html#using-leftjoinwith
2, I can't get the exact difference between "matching" and "contain"
I know the way they retrieving data is different, but I thought the result looks same as the result of enter code here matching and matching is using innerjoin, and contain is using leftjoin.
but but I can't find what is the difference between the code below(using contain) and matching.
$query = $articles->find()->contain([
'Comments' => function ($q) {
return $q
->select(['body', 'author_id'])
->where(['Comments.approved' => true]);
}
]);
=> In short contain applies leftJoin and matching apply right/inner join to the query.
please read this first line on this link https://book.cakephp.org/3.0/en/orm/query-builder.html#using-innerjoinwith
3, what is "_matchingData" in matching?
I read this description, that is
"The innerJoinWith() method works the same as matching(), that means that you can use dot notation to join deeply nested associations: Again, the only difference is that no additional columns will be added to the result set, and no _matchingData property will be set."
but I couldn't find any description about _matchingData. even in API... what it is?
=> _matchingData() is an entity property created when matching() is used. It contains data which is selected matching
$query = $articles->find();
$query = $query->matching('Comments', function($q){
$q->select(['Comments.body', 'Comments.author_id']);
$q->where(['Comments.is_approved' => 1]);
return $q;
});
Above query will have _matchingData property contained with Comments.body, Comments.author_id

Can't use paginate on a query result

According to the doc http://book.cakephp.org/3.0/en/controllers/components/pagination.html, I'd like to paginate a result of a query like below:
$unlocked_sitesQuery = $this->Deviceconnections
->find()
->contain([
'Agthemes.Sites',
'Agthemes.Agpois',
'Agthemes.Agthemelanguages'
])
->where(['request' => 'unlock'])
->groupBy('agtheme.site.id');
$unlocked_sites = $this->paginate($unlocked_sitesQuery);
But I get the following error:
Error: Call to undefined method ArrayIterator::alias()
File /home/mywebsite/vendor/cakephp/cakephp/src/Controller/Component/PaginatorComponent.php
Line: 154
What does it mean?
EDIT
It seems that #ndm is right but the doc says:
By default the paginate() method will use the default model for a controller. You can also pass the resulting query of a find method:
public function index()
{
$query = $this->Articles->find('popular')->where(['author_id' => 1]);
$this->set('articles', $this->paginate($query));
}
So it should work on a result set. Or I didn't understand what the doc explains. Possible.
It means that you are passing the wrong type of object. Pagination on result sets is not supported, only tables (either objects or names) and queries are.
groupBy is not a method of the query class, it's one of the magic methods that are causing the query to be executed, and forward the method call to the resulting result set. So you end up calling Cake\ORM\ResultSet::groupBy(), which returns another collection.
Cookbook > ... ORM > Query Builder > Queries Are Collection Objects
Cookbook > ... ORM > Retrieving Data & Results Sets > Working with Result Sets
So if you need such grouped results in pagination, then you have to solve this (at least partially) on SQL level, for example by fetching the results the other way around, ie fetch Sites and their associations, and filter by Deviceconnections.request, something like this (no guarantee that this will give you the desired result, but the example should give you a hint!):
$query = $Sites
->find()
->contain([
'Agthemes.Deviceconnections',
'Agthemes.Agpois',
'Agthemes.Agthemelanguages'
])
->matching('Agthemes.Deviceconnections', function(\Cake\ORM\Query $query) {
return $query
->where([
'Deviceconnections.request' => 'unlock'
]);
});
You'd of course have to adapt your view code accordingly.

How do I build this complex database query in CakePHP 3.0?

This is my database design, or at least the tables that are relevant to this question.
I want to build a query that returns a single page (find will be based on the path attribute), with the associated container, with its associated child-containers in threaded form and all of those containers should individually have their associated blocks with them (preferably in the right order sorted by the index column from the blocks_pages table).
Can anybody give me a clue how to wrap that all up with the query-builder? Or if that is not possible, then is it possible to do it using the new map/reduce feature, since the after-find function has been removed?
In case it helps, this will be the visualized result, if you just ignore the magenta Article-box for a moment.
Try this
$pagesTable
->find()
->where(['path' => $myPath])
->contain([
'Containers.ChildContainers' => function($q) {
return $q->formatResults(function($results) {
return $results->map(function($container) {
$container->nested = $container->source()
->find('children', ['for' => $container->id])
->find('threaded')
->contain(['Blocks']);
return $container;
});
});
},
'Containers.ChildContainers.Blocks'
])

cakephp beforesave question

I follow in book.cake and I don't know I should send something to the parameters.
function beforeSave() {
if (!empty($this->data['Article']['create_dt']) && !empty($this->data['Article']['modified_dt'])) {
$this->data['Article']['create_dt'] = $this->dateFormatBeforeSave($this->data['Article']['create_dt']);
$this->data['Article']['modified_dt'] = $this->dateFormatBeforeSave($this->data['Article']['modified_dt']);
}
return true;
}
I try to search example but don't found.
I need many example
somebody can help me to find big resource
thank for suggest
beforeSave is called automatically by Cake before it saves data. In it, you can do whatever you want to do before each save. Typically this means altering $this->data, which is the data that is about to be saved.
The method is passed one parameter: an array of the form array('validate' => true/false, ('fieldList' => array(...)). This corresponds to the two extra parameters you can supply to save():
$this->Model->save($this->data, false, array('foo', 'bar'));
In this case the array would look like
array('validate' => false, 'fieldList' => array('foo', 'bar')).
You can accept this array by specifying an argument:
public function beforeSave($options) { ... }
$options will look like described above. You can use this information any way you want.
If you don't return true from beforeSave, the save operation will be canceled altogether.
That's all.
try using created and modified magic fields with datetime type in table cake would automatically handle them
i want to mention, that beforeSave() should be used carefully, because it is used on every time when data is saved with this model.
if you forget that it is used, you will get unexpected results.
Happens to me several times... ;)

Resources