Outputting MySQL LEFT JOIN in PHP - php

I have made a mysql query like this
SELECT
forum_subcategories.name_sub,
forum_categories.id,
forum_categories.name
FROM
forum_subcategories
LEFT JOIN forum_categories ON forum_subcategories.catid = forum_categories.id
and when i echo it like this
while($row = $result->fetch_assoc()){
echo '<h3><strong>'.$row['name'].' - '.$row['id'].'</strong></h3>';
echo '<h4>'.$row['name_sub'].' - '.$row['id'].'</h4>';
I get the following result:
Category-1
subcategory belonging to 1
Category-1
subcategory belonging to 1
Category-2
subcategory belonging to 2
Category-2
subcategory belonging to 2
Category-3
subcategory belonging to 3
Category-3
subcategory belonging to 3
But the result i am looking for is this:
Category-1
subcategory belonging to 1
subcategory belonging to 1
Category-2
subcategory belonging to 2
subcategory belonging to 2
Category-3
subcategory belonging to 3
subcategory belonging to 3
How would i achieve this result?

Your query is correct as it is, this is just a matter of only sending the category as output when it has changed. Store the category in a variable, and on each loop iteration, check to see if the new value matches the old. If it does not, print it.
// variable to hold the current one...
// It starts empty.
$current_category = '';
// Remember whether there's a <div> unclosed
$subcat_div_open = false;
while($row = $result->fetch_assoc()){
// Compare to the category of the loop iteration
if ($row['name'] !== $current_category) {
// If there's a subcategory <div> open, close it
if ($subcat_div_open) {
echo '</div>';
}
echo '<h3><strong>'.$row['name'].' - '.$row['id'].'</strong></h3>';
// Store the new one to compare next time around
$current_category = $row['name'];
// Ouput an opening <div> for the subcategories
// and keep track of its state
echo '<div>';
$subcat_div_open = true;
}
// Always output the subcategory
echo '<h4>'.$row['name_sub'].' - '.$row['id'].'</h4>';
}
// Clean up the open div
if ($subcat_div_open) echo '</div>';

how about something like this?
$last_category = '';
while($row = $result->fetch_assoc()) {
$category = '<h3><strong>'.$row['name'].' - '.$row['id'].'</strong></h3>';
echo ($category == $last_category) ? '' : $category;
echo '<h4>'.$row['name_sub'].' - '.$row['id'].'</h4>';
$last_category = $category;
}

Related

PHP foreach targeting specific item

I got a DB query that pulls out three items randomly from several categories. I want to show "Selected" if one of the items is from a specific category. Right now if one of the items is from that specific category the "Selected" is showing on all three items. How can I target the specific item only?
foreach ( $rows as $row ) {
if ($row->catid == 56) {
echo "Selected";
}
Your question isn't very clear, but I suspect you're trying to add the attribute to an option. You need to do this in the same loop that echoes the option.
foreach ($rows as $index => $row) {
$selected = $row->catid == 56 ? "SELECTED" : "";
$itemnum = $index + 1;
echo "Item $itemnum - Category $row->catid $selected | ";
}
foreach ( $rows as $row ) {
if ($row->catid == 56) {
echo "Selected";
break;
}
Not sure what u want accomplish but i guess u want to show only one selected so u need to break loop if item is found

How to display all posts under main category and its subcategories?

I have a custom php/mysql blog. In single post page, I have a sidebar where I am displaying categories. I want to display number of posts those belong to a main category and its subcategories in brackets as we usually see in WordPress. For now I have
Music
Pop
Nature
Lakes
Sea
I want to achieve
Music (5)
Pop (3)
Nature (8)
Lakes (2)
Sea (4)
As you see Music category has 5 posts under it and Pop has 3. But it doesn't mean there are 8 posts in reality. There are actually only 5 posts because Pop is subcategory of main category Music. 2 posts are directly under the main category Music while 3 posts are falling in its subcategory Pop. Thus 2 + 3 = 5.
The categories table looks as (Note that it has nth level of subcategories):
The posts table looks as (Note that a particular post can have its only one category parent i.e. a post can not have its multiple parents)
My code to fetch categories and their subcategories is
public function get_category_hierarchy(&$output, $parent_url, $parent = 0)
{
$sql = "SELECT * FROM `tb_categories` WHERE `category_parent` = " . $parent . " ORDER BY `category_name`";
$rows = $this->get_by_sql($sql);
// Removing trailing slash
$parent_url = rtrim($parent_url, "/");
if(is_array($rows))
{
foreach($rows as $row) :
$url = $parent_url . "/" . $row->category_url;
$output.= "<li><a href='" . $url . "'>" . $row->category_name . "</a></li>";
if($row->category_id != $parent)
{
$this->get_category_hierarchy($output, $url, $row->category_id);
}
endforeach;
}
return $output;
}
On sidebar, I am displaying these categories by following code:
$output = '';
// Create post object
$post_obj = new Post();
echo $post_obj->get_category_hierarchy($output, SITE_URL . 'category');
Now I'm wondering what code need to be add for fetching number of posts (post counts) that fall under a main category and subsequently its sub-sub-categories? Though I tried to fetch them but the result didn't come accurate.
Thank you in advance.
EDIT:
I modified function get_category_hierarchy() by doing following amendment:
foreach($rows as $row) :
$url = $parent_url . "/" . $row->category_url;
// Build database query
$sql = "SELECT COUNT(*) AS 'total_posts' FROM `tb_posts` WHERE `post_parent` = " . $row->category_id . " GROUP BY `post_parent`";
// Execute database query
$rows = $this->get_by_sql($sql);
$output[] = "<li><a href='" . $url . "'>" . $row->category_name . " (" . $rows[0]->total_posts . ")</a></li>";
But oops! It is giving me following result:
Music (2)
Pop (3)
Nature (2)
Lakes (2)
Sea (4)
Obviously, the parent category is having total number of post which are directly associated to it and not to its children. How to fix it?
The approach that I could think of :
Do a group by on the posts table on the post_parent.
From that form an associative array with the index as category_id and pair value as post_count.
Ex. $category_post_counts = array("4" => 5, "5" =>
10)
Now run a foreach loop for all the category ids and fetch its
corresponding parent.
Update the post count by using the indexes.
Suppose for category id 4(pop) there were 5 posts and in music there
were 10 posts. Now in the loop if the category parent is anything
else apart from 0 then update the post count in the array.
Ex.
$category_post_counts["<category_parent_id>"] =
$category_post_counts["<category_parent_id>"] +
$category_post_counts["<category_child_id>"]
This way your end result
will be an associative array with the category ids and all the post
counts.
Pseudo Code
//exctracting all the data
$rows = execute_this_query("SELECT COUNT(post_id) AS `post_count`, `post_parent` FROM `posts` GROUP BY `post_parent`");
//forming the associative array
foreach ($rows as $value) {
$associative_array[value["post_parent"]] = $value["post_count"];
}
$data = execute_this_query("SELECT `category_id`,`category_parent` FROM `categories` WHERE `category_parent` <> 0");
foreach ($data as $value) {
$associative_array[$value["category_parent"]] += $associative_array[$value["category_id"]];
}
//Your final associative array has the exact category_id and net post count as you want.
First you can populate an associative array for the quantity in each category like: category_id => post_qty.
After that, you can use the function bellow to calculate for every category and its children:
function count_posts($category_id, $cache) {
$sum = 0;
$sql = "SELECT * FROM `tb_categories` WHERE `category_parent` = " . $category_id;
$rows = $this->get_by_sql($sql);
foreach ($rows as $child) {
$sum += count_posts($child->category_id, $cache);
}
$sum += $cache[$category_id];
return $sum;
}

PHP how to echo the first value of each group in a mysql query list

To better explain my situation, please take a look at this mokeup query result:
Category Item
cat1 Item 1
cat1 Item 2
cat1 Item 3
cat23 Item x
cat23 Item y
cat23 Item z
X apples
X oranges
X bananas
and so on....
I am pulling the data off a mysql database and would like to display the results like this:
Category----Item
cat1 Item 1
Item 2
Item 3
cat23 Item x
Item y
Item z
X apples
oranges
bananas
and so on....
I tried different ways but I am coming up empty. This is the latest attempt:
PHP
//Headers
echo 'Category';
echo ' | ';
echo 'Item';
echo "\n";
$sql = "SELECT `table1`.*, `table2`.* FROM table1 INNER JOIN table2 ON `table1`.`Category` = `table2`.`Category` ORDER BY `table1`.`Category`, `table2`.`Item`";
$dbq = mysql_query( $sql );
$cat = '';
while( $data = mysql_fetch_assoc( $dbq ) ) {
if( !$cat == $data['category'] ) {
$cat = $data['category'];
echo $cat;
echo ' | ';
echo $data['item'];
echo "\n";
}
else {
echo ' ';
echo ' | ';
echo $data['item'];
echo "\n";
}
}
With this code, the current output is:
Category----Item
cat1 Item 1
Item 2
Item 3
Item x
Item y
Item z
apples
oranges
bananas
...rather than the desired output.
I am looking for the most efficient and simple way to echo each category 1 time only. Perhaps this code is not the best way to approach it, but I tried in different ways and my brain right now is shut S_S .
Took me quite some time...
Change
if( !$cat == $data['category'] ) {
To
if( $cat != $data['category'] ) {
Better to concatenate the Items of same category and store it like as Item1,Item2,Item3.
When you want to use this Items use explode() to split and
when ever you want to search in this items for a particular item use mysql's FIND-IN-SET
Not sure what you're looking for as that solution seems like it solves what you need it to do but if you want to be more tolerant of the result orders you can try this:
$concated_data = array();
while( $data = mysql_fetch_assoc( $dbq ) )
{
if ( ! array_key_exists( $data['category'], $concated_data ) )
$concated_data[$data['category']] = array();
array_push($concated_data[$data['category']], $data['item']);
}
foreach ($concated_data as $category => $items)
{
echo $category;
$count = count($items);
for ($i = 0; $i < $count; $i++)
{
if ($i == 0)
echo "\t";
else
echo "\t\t";
echo $items[$i]."\n"; // Items
}
}
Formatting up to you at this point I guess but I think putting the data into an array of arrays is the best way so the top level keys are categories and then the arrays they point to now hold an array of your items. This also doesn't assume that you get results in the same category consecutively.
Sorry code might contain bugs as I haven't coded in PHP in awhile. But basically you should accumulate the items first then go through the accumulated list and output the data.

How to fetch query where sub/child create new UL

I am trying to make un-order list for parent child categories where if there is any child category than it will create another un-order list ( like indented text) so user can understand properly.
I have fetch sql but with foreach I don't understand how to set so where child category only will display under parent category by creating another un-order list under the parent category.
Here is my code
$query_cat = "SELECT * FROM ^categories";
$query = qa_db_query_sub($query_cat);
$catsid = qa_db_read_all_assoc($query);
echo '<UL>';
foreach ($catsid as $catid){
echo '<LI>'. $catid['title'].' '. $catid['categoryid'].'</LI>';
}
echo '</UL>';
So final result would be
First Category
Sub Category1
Second Category
EDIT:
After modified code with #vlcekmi3 answer https://stackoverflow.com/a/13451136/1053190 I am getting this result
Now how to exclude subcategory from parent list?
There's no really easy solution for this with your design. The most effective way would be to add column like order_in_list (and maybe depth_in_list).
They would be pre calculated in loop (pseudocode):
START TRANSACTION
UPDATE t1 SET order_in_list = 0 // Restart whole loop
$ids = array(0);
while $id = array_shift($ids){
$record = SELECT * FROM t1 WHERE id = $id // Get id details, order_in_list is important
$children = SELECT * FROM t1 WHERE parent_id = $id // get list of all childs
// If it's root element, start indexing from 0
$root_order = ($record ? $record->order_in_list : 1)
$child_no = count($children) // How many child will be adding
// No children, nothing to do:
if $child_no < 1{
continue;
}
append_to_array($ids, $children) // Store ids to process
// Shift all later records, we'll be creating gap in order_in_list 1,2,3,4,5
// To 1,2,5,6,7 to insert items on places 3,4
UPDATE t1 SET order_in_list = (order_in_list + $child_no)
WHERE order_in_list > $record->order_in_list
// Okay, set IDs for direct children
foreach( $children as $child){
UPDATE t1 SET order_in_list = $root_order, depth_in_list = $record->depth_in_list+1
WHERE id = $child->id
$root_order++;
}
}
COMMIT
This way you'll get records like:
First category, 1, 1
Second category 3, 1
Sub category, 2, 2
Which you could display with simple loop:
$last_depth = 0;
foreach( (SELECT * FROM t1 ORDER by `order_in_list`) as $row){
if( $last_detph > $row['depth_in_list'])){
// Close level </ul>
} else if($last_detph < $row['depth_in_list']){
// Opening level <ul>
} else {
// The same depth
}
$last_depth = $row['depth_in_list'];
}
Without modifying database
It would be probably most effective to build two arrays containing root elements and all elements:
$root_elements = array();
$all_elements = array();
foreach( (SELECT * FROM t1) as $row){
// Store details into all_elements, note that entry may have already be created when
// processing child node
if( isset( $all_elements[$row['id']])){
// set details
} else {
$all_elements[$row['id']] = $row;
$all_elements[$row['id']]['children'] = array(); // Array of child elements
}
if( $row['parent_id'] == NULL){
$all_elements[] = $row['id']; // Add row element
} else {
if( isset( $all_elements[ $row[ 'parent_id']])){
$all_elements[ $row[ 'parent_id']]['children'][] = $row['id'];
} else {
// Create new record:
$all_elements[ $row[ 'parent_id']] = array();
$all_elements[ $row[ 'parent_id']]['children'] = array($row['id']);
}
}
}
And then write it as:
foreach( $root_elements as $element_id){
write_recursive( $all_elements[ $element_id]);
}
// And display
function write_recursive( $element)
{
echo '<ul>...';
if( count( $element['children'])){
foreach( $element['children'] as $child){
write_recursive( $all_elements[ $child]);
}
}
echo '</ul>';
}
You better create class for that (to replace using global variables), but you should have a solid way to do this. Anyway try avoid using this with large number of records (I wouldn't go past 2000-5000 menu entries), try to at least cache it.
Note: solutions are oriented towards minimal number of requests on database when displaying list.
you can use complicated query or something like this
foreach ($catsid as $catid) {
...
$subquery_cat = "SELECT * FROM ^categories WHERE parentid='".$catid['categoryid']."'";
$query = qa_db_query_sub($subquery_cat);
$subcatsid = qa_db_read_all_assoc($query);
// wrap into html
...
}

Grouping a SQL result set in PHP

Say I have a query the following query run:
Edit
Added order clause because the real sql statement has one.
SELECT description, amount, id FROM table ORDER BY id
In this instance, the ID is not unique to the dataset. It would return something like this.
Description Amount ID
----------- ------ --
1 Hats 45 1
2 Pants 16 1
3 Shoes 3 1
4 Dogs 5 2
5 Cats 6 2
6 Waffles 99 3
What I need to do is enclose each section of IDs in it's own div (So rows 1,2,3 in one div, 4,5 in another div and 6 in it's own div).
There are tons of solutions to this but I just can't think of one that isn't overly complicated.
I want to be able to keep the SQL how it is and somehow sort the data set in PHP so that I can loop through each section of the dataset while looping through the dataset as a whole.
Some kind of array would work but the structure of it is stumping me.
How can I get this to work? PHP solutions would be idea but theoretical will help too.
See if something like this works for you.
// execute query: Select description, amount, id from table
$results = array();
while ($row = $query_result->fetch_array()) {
if (!isset($results[$row['id']])) $results[$row['id']] = array();
$results[$row['id']][] = $row; // push $row to results for this id
}
// later on
foreach($results as $id => $data) {
// output div based on $id
foreach($data as $datum) {
// output individual data item that belongs to $id
}
}
A simple serial solution might look something like this:
$curId = ''; // track working id
$firstDiv = true; // track if inside first div
// open first div
echo '<div>';
// foreach $row
{
// when id changes, transition to new div, except when in first div
if ($row->$id != $curId) {
if ($firstDiv) {
$firstDiv = false;
} else {
// start new div
echo '</div>';
echo '<div>';
}
$curId = $row->$id; // track new current id
}
// display contents of current row
}
// close last div
echo '</div>';
Just store the id in temp variable, if the next one is different close the div and open new div
Assuming associative arrays for the db results:
$final = array();
foreach($results as $result)
{
$final[$result['id']][] = $result;
}
This leaves you with an associative array $final that groups the entries by ID

Categories