Select multiple columns from inner join in Silverstripe 3.1 - php

In my events calendar module for Silverstripe 3.1, i have added some additional fields to CalendarEvent. I want to make use of these in my template, but from my researches i have seen that events are exported as being CalendarDateTime so i can't use my additional fields.
I have found that in getStandardEvents function, there is a inner join which i think is causing the problem, but i can't figure it out to join the columns from CalendarEvent
$list = DataList::create('CalendarDateTime')
->filter(array(
'EventID' => [139, 140, 141, 143]
))
->innerJoin('CalendarEvent', "EventID = \"{CalendarEvent}\".\"ID\"")
->innerJoin("SiteTree", "\"SiteTree\".\"ID\" = \"{CalendarEvent}\".\"ID\"")
->where("Recursion != 1");
Note: In my code, i have some of the columns as variables, so i have written them as the values that are reffering to.
Here is the original code:
$list = DataList::create($datetimeClass)
->filter(array(
$relation => $ids
))
->innerJoin($eventClass, "$relation = \"{$eventClass}\".\"ID\"")
->innerJoin("SiteTree", "\"SiteTree\".\"ID\" = \"{$eventClass}\".\"ID\"")
->where("Recursion != 1");
I have tried to add the 'CalendarEvent' as a second parameter inside DataList::create(), but no result. I have the same output.
So how can i select columns from CalendarDateTime and CalendarEvent tables ?
Any help would be appreciated.Thanks

I have found a manual way:
$list = DB::query('SELECT * FROM "CalendarDateTime" INNER JOIN "CalendarEvent" ON "EventID" = "CalendarEvent"."ID" INNER JOIN "SiteTree" ON "SiteTree"."ID" = "CalendarEvent"."ID" WHERE "Recursion" != 1');
Any other solutions are welcome.

Related

Cake PHP: Find condition on deeper level of recursion

My Models are related as follows:
Post BELONGS_TO Parent
Parent HAS_MANY ParentAdmin
Now I wish to find all Parent admins of all post if a post's attribute's value is met. The equivalent sql query is :
SELECT parent_admins.* FROM posts
LEFT JOIN parents AS Parent ON Parent.id = posts.target_id
LEFT JOIN parent_admins ON parent_admins.parent_id = Parent.id
WHERE posts.admin_notification = 0 AND Parent.maxtime > posts.created
Assuming Models are linked in cakephp accordingly and recursive = 2, what will be the condition array in find query?
You can both use the bindModel & unbindModel methods to get the exact query you want or you can use the Containable behavior.
More information about how to create and destroy associations on the fly with CakePHP: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#creating-and-destroying-associations-on-the-fly
And you'll need this array of conditions:
$conditions = array(
'Post.admin_notification' => 0,
'Parent.maxtime >' => 'Post.created'
);
according to your query :-
$conditions=array(
'posts.admin_notification'=>0,
'Parent.maxtime >'=>'posts.created'
);
If your query do the job:
Inside PostsController:
$query = "SELECT parent_admins.* FROM posts
LEFT JOIN parents AS Parent ON Parent.id = posts.target_id
LEFT JOIN parent_admins ON parent_admins.parent_id = Parent.id
WHERE posts.admin_notification = 0 AND Parent.maxtime > posts.created"
$result = $this->Posts->query($query);
$debug($result);

How to retrieve all posts with all comments using 2 SQL queries

I am trying to make a page with a list of posts, and underneath each post all the comments belonging to that post. Initially I wanted to use just one query to retrieve all the posts + comments using the SQL's JOIN, but I find it impossible to for example retrieve a post with multiple comments. It only displays the posts with a maximum of 1 comment per post, or it just show a post multiple times depending on the amount of comments.
In this related question, somebody talked about using 2 queries:
How to print posts and comments with only one sql query
But how do I do this?
I've got the query and a while loop for posts, but I obviously don't want to run a query for comments for each post inside that loop.
$getPost = mysql_query('SELECT p.post_id,
p.user_id,
p.username,
p.content
FROM post p
ORDER BY p.post_id DESC');
while($row = mysql_fetch_array($getPost))
{
...
}
Table structure (reply is the table for storing comments):
POST (post_id (primary key), user_id, username, content, timestamp)
REPLY (reply_id (primary key), post_id, username, reply_content, timestamp)
You can do it in a single query, which is OK if the amount of data in your original posts is small:
$getPost = mysql_query('SELECT
p.*,
r.reply_id, r.username r_username, r.reply_content, r.timestamp r_timestamp
FROM post p
left join reply r
ORDER BY p.post_id DESC'
);
$posts = array();
$last_id = 0;
while($row = mysql_fetch_array($getPost))
{
if ($last_id != $row['post_id']) {
$posts[] = array(
'post_id' => $row['post_id'],
'user_id' => $row['user_id'],
'username' => $row['username'],
'content' => $row['content'],
'timestamp' => $row['timestamp'],
'comments' => array()
);
}
$posts[sizeof($posts) - 1]['comments'][] = array(
'reply_id' => $row['reply_id'],
'username' => $row['r_username'],
'reply_content' => $row['reply_content'],
'timestamp' = $row['r_timestamp']
);
}
Otherwise, break it into two queries like so:
$getPost = mysql_query('SELECT
p.*,
FROM post p
ORDER BY p.post_id DESC'
);
$rows = array();
$ids = array();
$index = array();
while($row = mysql_fetch_assoc($getPost)) {
$row['comments'] = array();
$rows[] = $row;
$ids[] = $row['post_id'];
$index[$row['post_id']] = sizeof($rows) - 1;
}
$getComments = mysql_query('select r.* from replies r where r.post_id in ("'
. join('","', $ids)
. '")');
while ($row = mysq_fetch_assoc($getComments)) {
$rows[$index[$row['post_id']]]['comments'][] = $row;
}
... Or something like that. Either option allows you to litter your first query with WHERE clauses and so forth to your heart's content. The advantage of the 2nd approach is that you don't re-transmit your original post data for each comment!
In order to also get those posts without comments, you need to use a LEFT OUTER JOIN. In that case, any row in the first table without any corresponding rows in the second table will be paired with a row consisting of null values.
SELECT * FROM posts
LEFT OUTER JOIN comments ON posts~post_id = comments~post_id;
By the way: there is also a RIGHT OUTER JOIN. When you would use that, you would get all comments, including those where the parent post got lost somehow, but no posts without comments.

Filtering HABTM with AND Condition

I'm trying to filter some car parts depending on the categories they are related to.
A part can have many categories (in the code they are called tags), so I chose the HABTM relation with a join table.
Filtering works so far, but only with an OR condition with cake using the SQL command IN.
But I'm trying to filter only the parts that have all the selected categories, so I need to use an AND condition on the category array.
Here's the extracted code from the controller:
$this->Part->bindModel(array('hasOne' => array('PartsTagsJoin')));
$params['conditions'] = array('AND' => array('PartsTagsJoin.tag_id' => $selectedCats));
$params['group'] = array('Part.id');
$parts = $this->Part->find('all',$params);
$this->set('parts',$parts);
$selectedCats is an array like this: array(1,2,3,4,5);
The SQL output is:
'SELECT `Part`.`id`, `Part`.`name`, `Part`.`image`, `Part`.`image_dir`, `Part`.`time_created`, `Part`.`time_edited`, `Part`.`user_id`, `Part`.`editor_id`, `Part`.`notice`, `User`.`id`, `User`.`username`, `User`.`password`, `User`.`usergroup_id`, `User`.`realname`, `PartsTagsJoin`.`id`, `PartsTagsJoin`.`part_id`, `PartsTagsJoin`.`tag_id`
FROM `c33rdfloor`.`parts` AS `Part`
LEFT JOIN `c33rdfloor`.`users` AS `User` ON (`Part`.`user_id` = `User`.`id`)
LEFT JOIN `c33rdfloor`.`parts_tags_join` AS `PartsTagsJoin` ON (`PartsTagsJoin`.`part_id` = `Part`.`id`)
WHERE `PartsTagsJoin`.`tag_id` IN (1, 4, 8, 24)'
How can I filter the parts that have every id that is committed through the $selectedCats Array.
Thank you in advance for your help.
I've got it working thanks to this blog post:
http://nuts-and-bolts-of-cakephp.com/2008/08/06/habtm-and-join-trickery-with-cakephp/
It seems to be a little tricky to filter entries with all selected tags:
The key in achieving an AND relation is to get the count of the selected cats and match it with the ones of the query inside the group parameter.
This line did it:
$params['group'] = array('Part.id','Part.name HAVING COUNT(*) = '.$numCount);
In the End the code looked like this (for people interested in a solution):
// Unbinds the old hasAndBelongsToMany relation and adds a new relation for the output
$this->Part->unbindModel(array('hasAndBelongsToMany'=>array('PartsTag')));
$this->Part->bindModel(array('hasOne'=>array(
'PartsTagsJoin'=>array(
'foreignKey'=>false,
'type'=>'INNER',
'conditions'=>array('PartsTagsJoin.part_id = Part.id')
),
'PartsTag'=>array(
'foreignKey'=>false,
'type'=>'INNER',
'conditions'=>array(
'PartsTag.id = PartsTagsJoin.tag_id',
'PartsTag.id'=>$selectedCats
)))));
$numCount = count($selectedCats); // count of the selected tags
// groups the entries to the ones that got at least the count of the selected tags -> here the 'AND' magic happens
$params['group'] = array('Part.id','Part.name HAVING COUNT(*) = '.$numCount);
$parts = $this->Part->find('all', $params); // sends the query with the params
$this->set('tagCategories', $categories);
$this->set('selectedCats', $selectedCats);
$this->Part->recursive = 4;
$this->set('parts',$parts);

Create a Multidimensional Array with PHP and MySQL

I am new to PHP and am looking at efficient ways to return data from the database. Lets say I have a UserProfile table that has a one to many relationship with UserInterest and UserContact:
Select p.Id, p.FirstName, p.LastName, i.Name as Interests, c.Email, c.Phone
from UserProfile p
left join UserInterest i on p.Id = i.UserProfileId
left join UserContact c on p.Id = c.UserProfileId
where p.Id = 1
An efficient way to retrieve data would be to create a multidimensional array such as:
$user = array( "FirstName" => "John",
"LastName" => "Doe",
"Gender" => "Male",
"Interests" => array(
"Hiking",
"Cooking"),
"Contact" => array(
"Email" => "john.doe#gmail.com",
"Phone" => "(555) 555-5555"));
I can't seem to get my head around how this would be constructed in PHP. For simple data like interests I could use group_concat(i.Name) as Interests in the query to return interests back as a comma separated list in a single row, however, for an associative array such as Contact, I'd like to be able to get a handle on each key in the array using $user['Contact']['Email'].
From a "Best Practices" standpoint, I would assume that constructing an array like this in one query is a lot better than hitting the database multiple times to retrieve this data.
Thanks!
Neil
You can construct this array in one pass through the data returned by your query. In pseudo-code:
for each row
$user["FirstName"] = row->FirstName;
$user["LastName"] = row->LastName;
$user["Interests"][] = row->Interests;
$user["Contact"]["Email"] = row->Email;
$user["Contact"]["Phone"] = row->Phone;
next
The syntax $user["Interests"][] = $data is valid PHP code. It is equivalent to array_push($user["Interests"], $data).
My guess is that you would have to run separate queries and manually construct the array yourself. I don't personally know of any MySQL database resources in PHP that return rowsets like that in a multidimensional array the way you described.
A better way of doing this could be:
$query = "SELECT p.Id, p.FirstName, p.LastName, i.Name as Interests,
c.Email, c.Phone
FROM UserProfile p
LEFT JOIN UserInterest i ON p.Id = i.UserProfileId
LEFT JOIN UserContact c ON p.Id = c.UserProfileId
WHERE p.Id = 1";
$res = GIDb::pg_query($query);
if(pg_num_rows($res) > 0)
{
while($data = pg_fetch_assoc($res))
{
$id = $data['Id'];
$f_name = $data['FirstName'];
$l_name = $data['LastName'];
$interests = $data['Interests'];
$email = $data['Email'];
$phone = $data['Phone'];
}
}
//You can also directly use $data['Id'] etc.
Also don't forget to include in the beginning of the php code : include_once(dirname(__FILE__)."/../../class.GIDb.php");
EDIT :
This is for Postgres.
For MySQL use: mysql_query(), mysql_fetch_assoc(), mysql_num_rows()

PHP: Loop over a grouped SQL query array but echo one value once for each “grouping”?

I’ve a small piece of code I’ve tried to wrap my brain around for some hours now.
I’m trying to create a query which retrieves some elements and in the end group them by a selector.
I’ll try to demonstrate: I’m querying all the members in my organization. Each member is a member of a “department” and there can be several members in each department, but only one department for each member.
So I’ve created the query to select all members in my organization group by departments.
When I’m displaying it using a simple foreach($my_query as $q) … I want to output $q->department_name but only once for that department. Right now it says “Marketing” under all five members and then it changes to “HR” for all those members. I’ve tried some different methods such as array_unique, creating a function that checks for the string to see if it is the same and other but with no result.
I could create two foreach loops but if it is at all possible not to I would prefer that because two foreach loops would affect the performance.
Any help or suggestion would be very much appreciated.
Sinerely
- Mestika
--- Edited ---
By the way, my query looks like this:
SELECT *
FROM wp_term_taxonomy AS cat_term_taxonomy
INNER JOIN wp_terms AS cat_terms ON cat_term_taxonomy.term_id = cat_terms.term_id
INNER JOIN wp_term_relationships AS cat_term_relationships ON cat_term_taxonomy.term_taxonomy_id = cat_term_relationships.term_taxonomy_id
INNER JOIN wp_posts AS cat_posts ON cat_term_relationships.object_id = cat_posts.ID
INNER JOIN wp_postmeta AS meta ON cat_posts.ID = meta.post_id
WHERE cat_posts.post_status = 'publish'
AND meta.meta_key = 'active'
AND meta.meta_value = 'active'
AND cat_posts.post_type = 'member'
AND cat_term_taxonomy.taxonomy = 'deparment'
GROUP BY cat_terms.slug, cat_term_relationships.object_id
$prev_department = NULL;
foreach ($my_query AS $q)
{
if ($prev_department != $q->department_name)
echo '<h2>'. htmlentities($q->department_name, ENT_COMPAT, 'UTF-8') .'</h2>';
// ...
$prev_department = $q->department_name;
}

Categories