CakePHP Sub Querys, How To? - mysql

I have a post model (mysql table), and the structure is something like:
id, parent, user_id, title, desc, created, modified
A single thread of conversation consists of a post, followed by all subsequent posts where post.parent matches the original post_id.
Sample Conversation between user_32 and user_40:
110, 0, 32, thread_title, sample_description_here, created_time, modified_time
121, 110, 40, comment_title, sample_comment_here, created_time, modified_time
130, 110, 32, comment_title, sample_comment_here, created_time, modified_time
140, 110, 32, comment_title, sample_comment_here, created_time, modified_time
166, 110, 40, comment_title, sample_comment_here, created_time, modified_time
290, 110, 32, comment_title, sample_comment_here, created_time, modified_time
With plain PHP I simply do the outer query, followed by the inner query and then echo the conversation to the screen, but how do you achieve this with CakePHP??
QUESTION:
How do I construct the query in CakePHP, to display a single conversation composed of (see above) post_id_110, followed by all subsequent posts where post.parent == 110, ordered by created_time DESC.??

In Post model:
var $hasMany = array(
'Children'=>array(
'className' => 'Post',
'foreignKey' => 'parent_id'
)
);
(and change 'parent' to 'parent_id' in DB, just a convention). Then in posts controller:
$this->Post->find('first',array(
'conditions'=>array(...),
'contain'=>array('Children')
));
oh yeah, and use Containable. You don't really need that for the purpose, but it makes the find() clearer and definitely helpful later on.

in post model:
var $belongsTo = array(
'ParentPost' => array(
'className' => 'Post',
'foreignKey' => 'parent_id',
),
)
and then use as every other associate model
btw - parent_id is more cakeish than parent and works with scaffolds

Related

create array of record counts with query

DB::table('visitors')
->join('event_visitor', 'visitors.id', '=', 'event_visitor.visitor_id')->where('sex', 0)
->where('event_visitor.event_id', 1)
->count();
this is the query to get the count of men at the visitor's table with an event id of 1
I want to get the record count of men women and kids in 10 events and formate it like this
$men = [100, 200, 300 ,400,500,600,700,800,900,1000];
$women = [100, 200, 300 ,400,500,600,700,800,900,1000];
$kids = [100, 200, 300 ,400,500,600,700,800,900,1000];
is there a way to do this in the database without getting into the n+1 problem?
0 = men
1 = women
2 = kids
Don't know about laravel, but in MySQL you could write a query like
SELECT visitors.sex, event_visitor.event_id, count(*) as num_visits
FROM visitors INNER JOIN event_visitor ON visitors.id = event_visitor.visitors_id
WHERE event_visitor.event_id IN (1, 10, 15, 21, 25, 28, 30, 48, 59, 61)
GROUP BY visitors.sex, event_visitor.event_id;
The numbers given after the IN keyword are ten sample event ids, for which you want to get a result. Note, that this counts visits not visitors. If you want to count distinct visitors, you can count(DISTINCT visitors.id).
the answer is going to be like this in laravel
DB::table('visitors')
->select('visitors.sex', 'event_visitor.event_id', DB::raw('count(*) as num_visits'))
->join('event_visitor','visitors.id','=','event_visitor.visitor_id')
->whereIn('event_visitor.event_id',[1,2,3,4,5])
->groupBy('visitors.sex','event_visitor.event_id')
->get();

Laravel - How to create a query with where clause using subquery?

I'm trying to generate this query using Laravel:
select mygames.id, mygames.name, mygames.slug, mygames.cover from mygames
left join mygame_mygenre on mygames.id = mygame_mygenre.mygame_id
left join mygame_myplatform on mygames.id = mygame_myplatform.mygame_id
where mygame_mygenre.mygenre_id in (8, 9, 31, 32, 33)
and mygame_myplatform.myplatform_id in (3, 6, 14, 34, 37, 39, 46, 48, 72, 130)
and mygames.id <> 1990
and mygames.summary is not null
and (select count(mygame_id) from mygame_myplatform where mygame_id = mygames.id) > 1
group by mygames.id, mygames.name, mygames.slug, cover
order by RAND()
limit 6
My current code is:
$games = DB::table('mygames')
->leftjoin('mygame_mygenre', 'mygames.id', '=', 'mygame_mygenre.mygame_id')
->leftjoin('mygame_myplatform', 'mygames.id', '=', 'mygame_myplatform.mygame_id')
->select('mygames.id', 'mygames.name', 'mygames.slug', 'cover')
->when($genres_id, function ($query, $genres_id) {
return $query->whereIn('mygame_mygenre.mygenre_id', $genres_id);
})
->when($platforms_id, function ($query, $platforms_id) {
return $query->whereIn('mygame_myplatform.myplatform_id', $platforms_id);
})
->where('mygames.id', '<>', $this->id)
->whereNotNull('mygames.summary')
->where(function ($query) {
$query->selectRaw('count(mygame_id)')
->from('mygame_myplatform')
->where('mygame_id', 'mygames.id');
}, '>', 1)
->groupBy('mygames.id', 'mygames.name', 'mygames.slug', 'cover')
->inRandomOrder()
->take(6)
->get();
This code is not working because inside the closure function I was unable to pass the name of the mygames table with the id field. Laravel is interpreting as a text parameter and not as a table.field
->where(function ($query) {
$query->selectRaw('count(mygame_id)')
->from('mygame_myplatform')
->where('mygame_id', 'mygames.id'); <<<<<<<<<<<<<
}, '>', 1)
I tried to use 'use ()' but it didn't work either.
Could you help me?
Here I assume you're trying to compare 2 columns, right?
->where('mygame_id', 'mygames.id');
In that case, use the whereColumn/orWhereColumn method.
->whereColumn('mygame_id', 'mygames.id')
It's because you must use the whereColumn method to achieve this.
https://laravel.com/docs/8.x/queries#additional-where-clauses
Another solution is to use the whereRaw method.
See :
DB::table('mygames')
->select(['mygames.id', 'mygames.name', 'mygames.slug', 'mygames.cover'])
->leftJoin('mygames_mygenre', 'mygames.id', '=', 'mygame_mygenre.mygame_id')
->leftJoin('mygame_myplatform', 'mygames.id', '=', 'mygame_myplatform.mygame_id')
->whereIn('mygame_mygenre.mygenre_id', [8, 9, 31, 32, 33])
->whereIn('mygame_myplatform.myplatform_id', [3, 6, 14, 34, 37, 39, 46, 48, 72, 130])
->where('mygames.id', '<>', 1990)
->whereNotNull('mygames.summary')
->where(1, '<', function ($query) {
$query->selectRaw('COUNT(mygame_id)')
->from('mygame_myplatform')
->whereColumn('mygame_id', 'mygames.id');
})
->groupBy('mygames.id', 'mygames.name', 'mygames.slug', 'cover')
->inRandomOrder()
->take(6)
->get();
looks similar to #jascar_destin answer, but on the group by, you ll have to specify which table the cover is been picked from except if cover does not exists on other tables rather than mygames.
DB::table('mygames AS mg')
->leftJoin('mygame_mygenre AS mgmg', 'mgmg.mygame_id', '=', 'mg.id')
->leftJoin('mygame_myplatform AS mgmp', 'mgmp.mygame_id', '=', 'mg.id')
->select(['mg.id', 'mg.name', 'mg.slug', 'mg.cover'])
->whereIn('mgmg.my_genre_id', [8, 9, 31, 32, 33])
->whereIn('mgmp.my_platform_id'. [3, 6, 14, 34, 37, 39, 46, 48, 72, 130])
->where('mg.id', '<>', 1990)
->whereNotNull('mg.summary')
->where(1, '<', function ($query) {
$query->selectRaw('COUNT(mygame_id)')
->from('mygame_myplatform')
->whereColumn('mygame_id', 'mg.id');
})
->groupBy('mg.id', 'mg.name', 'mg.slug', 'mg.cover')
->inRandomOrder()
->take(6)
->get();

How to do 2 groupings in a SQL query, and export to JSON

Here's an example, in csv form, of my mySQL store when.
trek_id, number, name, value, units, userID, deviceID, lat , lng
--------------------------------------------------------------------------------
88, 4, Hum , 720, PPB , 96, 1, 40.0215268, -105.2177324
88, 4, PM10, 720, PPB , 96, 1, 40.0215268, -105.2177324
88, 6, Pres, 730, PPB , 96, 1, 40.0215299, -105.2177096
88, 6, PM10, 730, PPB , 96, 1, 40.0215299, -105.2177096
So a trek_id has multiple number values, and each number contains multiple measurements. I would love to know how to query this so I could eventually insert into a json object that looks like this:
{
"88":{
"4":{
"lat":"40.0215268",
"lng":"-105.2177324",
"userID":96,
"deviceID":"1",
"measurements":[
["Hum",
"PPB",
720
],
["PM10",
"PPB",
720
]
]
},
"6":{
"lat":"40.0215299",
"lng":"-105.2177096",
"userID":96,
"deviceID":"1",
"measurements":[
["Pres",
"PPB",
730
],
["PM10",
"PPB",
720
]
]
}
}
}
So essentially I need to group on trek_id and then again on number.
Ok, first things first: SQL results are FLAT tables. They don't have sub groups or sub array. So you need to handle all sub-grouping in the display code.
Second: You existing data is already perfect for the DISPLAY code (let's say PHP) to create a multi-dimensional array to transform into JSON.
--- HINT PSEUDO_CODE ---
<?php
// assume $results contains your data AND SORTED BY trek_id, number, name
$cur_trek = null;
$cur_number = null;
$json = array();
foreach ($results as $res) {
if ($cur_trek != $res['trek_id']) {
$cur_trek = $res['trek_id'] ;
$json[$cur_trek] = array();
$cur_number = null;
}
if ($cur_number != $res['number']) {
$cur_number = $res['number'];
$json[$cur_trek][$cur_number] =
array(
'lat' => $res['lat'],
'lng' => $res['lng'],
'userID' => $res['userID'],
'deviceID' => $res['deviceID'],
'measurements' => array();
);
}
$json[$cur_trek][$cur_number]['measurements'][] =
array($res['name'], $res['units'], $res['value']);
}
$json = json_encode($json);

I want to pull data from an array in specific sequence using FIND_IN how?--never recived a right answer

Below is my code and I want to pull the data based on the sequence 3, 10 then 7, how can I do that? so far it pulls first 10, then 7, then 3.
$cars = $this->car->find('all', array(
'conditions' => array(
'car.id' => array(3, 10, 7)
),
'limit' => 3,
'order' => array('car.id' => 'desc')
));
Where do the conditions come from? Anyway i believe this will work:
$cars = $this->car->find('all', array(
'conditions' => array(
'car.id' => array(3, 10, 7)
),
'limit' => 3,
'order' => array('FIELD(car.id,3,10,7)')
));
Might need to play around with the cakePHP specific syntax, but the ORDER BY FIELD(fieldname, value, value value); should work

WordPress get specific posts in arbitrary order

Right now, I'm doing:
$posts = get_posts(array('post_type' => 'page', 'post__in' => array(1, 3, 2, 9, 7)));
and am having two issues:
Post 3 is of 'post_type' => 'post', so it doesn't get selected, but I want it! If I leave out 'post_type' => 'page', then only post 3 is selected (because it must assume 'post_type' => 'post'.).
I want to be able to order the posts arbitrarily by their ids. If I knew how to use MySQL, I could do:
SELECT * FROM wp_posts WHERE ID IN (1, 3, 2, 9, 7)
ORDER BY FIND_IN_SET(ID, '1,3,2,9,7');
But, how should I do this with WordPress?
First fetch all posts arbitrarily by their ids and then loop through all the posts
You can do in this way:-
$posts=$wpdb->get_results("SELECT ID FROM $wpdb->posts WHERE ID IN (1, 3, 2, 9, 7)
ORDER BY FIND_IN_SET(ID, '1,3,2,9,7')");
$count=count($posts);
for ($counter=0 ; $counter < $count; $counter++)
{
$post=get_post( $posts[$counter]->ID, $output );
//do your stuffs with posts
}
Hope this helps
Kawauso on the #wordpress IRC channel informed me that "post_type takes an array of values." From that, I found that the following also selects post 3:
So, I did the following:
$post_ids = array(1 => 0, 3 => 1, 2 => 2, 9 => 3, 7 => 4);
$posts = get_posts(array('post_type' => array('post', 'page'),
'post__in' => array_keys($post_ids)));
$ordered_posts = array(0,0,0,0,0); // size of five; keeps order
foreach ($posts as $p) {
setup_postdata($p);
$ordered_posts[$post_ids[$p->ID]] = array(
'permalink' => get_permalink($p->ID),
'title' => $p->post_title,
'excerpt' => get_the_excerpt(),
'date' => date('F j, Y', strtotime($p->post_date)));
}
According to this thread, you can use this code and then use the classic WordPress loop:
$args = array(
'post_type'=>'page',
'orderby'=>'menu_order',
'order'=>'ASC'
);
query_posts($args);