How to create a menu tree using php? - php

I am developing one e-commerce platform where admin can manage menu, category and sub-category. Now, I want display it in mega menu.
Menu1
Category1.1
SubCategory1
SubCategory2
Category1.2
SubCategory
Menu2
Category2.1
SubCategory1
This is a possible structure with I came up.
I have 2 db tables - Menu, Category. In category, there is a column called sub_category where I am storing sub category values with , separator.
Now, after running below query -
$data = DB::select('SELECT m.id AS mId, m.name AS menu,
m.meta_keywords AS menu_keywords, m.meta_description AS menu_description,
c.id AS cId, c.name AS category, c.sub_category, c.meta_keywords AS
category_keywords, c.meta_description AS category_description
FROM `menu` AS m LEFT JOIN `category` AS c ON c.menuId = m.id
WHERE m.is_publish = 1 AND c.is_publish = 1');
I am getting result.
Now, I want to build a tree where I can get a result something like this -
"id" => 1,
"name" => "Fashion"
"category" => [
1 => [
"cId" => 1,
"category" => "Men"
"sub_category" => "Shirt, T-shirt"
],
2 => [
"cId" => 2,
"category" => "Women"
"sub_category" => ""
],
]
Please help me out to find out solution. Thank you in advance.

As per your db structure and requirement, you can use below function -
public function menuTree($data){
$menuId = $menu = $sub = $op = array();
foreach($data as $d){
if(in_array($d->mId, $menuId)){
$sub['cId'] = $d->cId;
$sub['category'] = $d->category;
$sub['sub_category'] = $d->sub_category;
$sub['category_keywords'] = $d->category_keywords;
$sub['category_description'] = $d->category_description;
$menu[$d->mId]['category'][] = $sub;
} else {
$menuId[] = $d->mId;
$op['id'] = $d->mId;
$op['menu'] = $d->menu;
$op['menu_keywords'] = $d->menu_keywords;
$op['menu_description'] = $d->menu_description;
$menu[$d->mId] = $op;
if($d->cId != NULL){
$sub['cId'] = $d->cId;
$sub['category'] = $d->category;
$sub['sub_category'] = $d->sub_category;
$sub['category_keywords'] = $d->category_keywords;
$sub['category_description'] = $d->category_description;
$menu[$d->mId]['category'][] = $sub;
}
}
}
return $menu;
}
Still I believe there will be a better solution. Hope this works for you.

Related

SQL bind two tables into one SQL statement in a specific way

I have two SQL tables with columns:
menu:
id [AI]
name
description
subcategories:
id [AI]
name
pid (subcategory parent id)
mid (menu id)
In the new version of my website there is no menu anymore and "subcategories" are now changed into "categories":
id
name
description
parent_id
The old database is still in use, so I'm making my migration script and here is the part with categories:
$new_db->query("TRUNCATE TABLE `categories`");
$ids = [];
$menu_list = $old_db->fetch("SELECT * FROM `menu` ORDER BY `id`");
foreach($menu_list as $menu)
{
$id = $new_db->insert("categories", [
"name" => $menu["name"],
"description" => $menu["description"],
"parent_id" => "0"
]);
$ids[$menu["id"]] = $id;
}
$subcategories = $old_db->fetch("SELECT * FROM `subcategories` ORDER BY `id`");
foreach($subcategories as $subcategory)
{
$pid = 0;
$desc = "";
if($subcategory["mid"] > 0)
{
$menu = $old_db->fetch_first("SELECT `id`, `description` FROM `menu` WHERE `id` = '".$subcategory["mid"]."' LIMIT 1");
$pid = $ids[$menu["id"]];
$desc = $menu["description"];
}
else
{
$pid = $subcategory["pid"];
}
$new_db->insert("categories", [
"name" => $subcategory["name"],
"description" => $desc,
"parent_id" => $pid
]);
}
It works but I'm pretty sure it could be done better with lesser cost.
Can I make a SQL statement which will bind menu and subcategories into one result list and then insert them all?
I think you just want a JOIN?
SELECT *
FROM `categories` c
JOIN `subcategiries` s
ON s.pid = c.id

How to properly use for loop when fetching data in mysql and storing it in array then implode it to display in my jquery data table?

need help for my code. What i want is to display my data that looks like this format:
Store | Category | Product Name | Description | Price | Timestamp | Actions
Sample : Sample cat : Sample Product : Sample Desc : Sample Label - Sample Price : Sample Timestamp : Sample Actions
My query for that is this:
$get_product = $mysqli->query("SELECT b.menu_name AS store, a.category AS category, a.product AS product, a.description AS description, a.stamp AS stamp, a.id AS id, a.image AS image FROM lfood a JOIN branch_map b ON a.menu_map = b.id WHERE a.status = 1 ".$searchQuery." ORDER BY ".$columnName." ".$columnSortOrder." LIMIT ".$row." , ".$rowperpage." ");
Then i fetch them like this:
$data = array();
while ($row = $sms_subs->fetch_assoc()) {
$menu_id[] = $row['id'];
$image[] = $row['image'];
$store[] = $row['store'];
$category[] = $row['category'];
$product[] = $row['product'];
$description[] = $row['description'];
$stamp[] = $row['stamp'];
}
for ($i=0; $i < count($menu_id); $i++) {
$get_price = $mysqli->query("SELECT * FROM lfood_price WHERE menu_id = '".$menu_id[$i]."'");
while ($row_price = $get_price->fetch_assoc()) {
$price[] = $row_price['label'].' : '.number_format($row_price['price']);
}
$new_price = implode("<br>", $price);
$data[] = array(
"store" => $store[$i],
"category" => $category[$i],
"product" => $product[$i],
"description" => $description[$i],
"price" => $new_price,
"stamp" => date("F d Y h:i:s A", strtotime($stamp[$i])),
"actions" => "sample"
);
}
I have another table that stores all the prices of my product. And the result weren't what it want,
This is the actual result
On my second row of data it displays all records of prices even it doesn't belong to that id. My thought of that is the improper use of my for when fetching prices. Please someone help / enlighten the proper use of for loop in regards of my problem. Thank you so much!
The issue is $new_price = implode("<br>", $price);
Walk through the for loop:
First time through it finds prices for shrimp and olive oil. The price array is now ["FS:570", "PS:1085"] and then it's imploded and saved to the data array.
The second time through it gets the prices for New Zealand Muscles. The price array is now ["FS:570", "PS:1085", "FS(16pcs):695", "PS(32pcs):1315"]. It's imploded and saved to the data array.
The fix would be to empty out $price each time through the loop.
for ($i=0; $i < count($menu_id); $i++) {
$price = [];
$get_price = $mysqli->query("SELECT * FROM lfood_price WHERE menu_id = '".$menu_id[$i]."'");
while ($row_price = $get_price->fetch_assoc()) {
$price[] = $row_price['label'].' : '.number_format($row_price['price']);
}
$new_price = implode("<br>", $price);
$data[] = array(
"store" => $store[$i],
"category" => $category[$i],
"product" => $product[$i],
"description" => $description[$i],
"price" => $new_price,
"stamp" => date("F d Y h:i:s A", strtotime($stamp[$i])),
"actions" => "sample"
);
}
However, you should probably look into multidimensional arrays, foreach loops, and mysql joins. All of those would greatly simplify your code.

Updating multiple rows using Yii's updateAll()

I'm trying to update multiple rows in Yii:
$list = array(1, 2, 3, 4, 5);
foreach($list as $id) {
$query = "UPDATE products SET photos=crawler.photos, status=crawler.status WHERE id=crawler.product_id AND crawler.product_id=$id;";
}
I'm new to Yii. Basically, I want to update rows from table products with the values from table scrape (using specific id, which are coming from $list). But not with foreach(), but with updateAll().
Active directory pattern. Good to write mode.
$movies_updated = SomeModel::updateAll([
'language' => 'pl',
'age' => 3,
], ['id' => 1]); # condition
I think should be somethings like this
public function actionMyProductsUpdate($id)
{
foreach ($queue as $item) {
$myUpdate = "UPDATE products as a join crawler as b
on ( a.id=b.product_id and b.product_id= $item->id)
SET a.photos=b.photos, a.status=b.status;";
\Yii::$app->db->createCommand($myUpdate)->execute();
}
}
version 2
public function actionMyProductsUpdate($id)
{
$myUpdate = "UPDATE products as a join crawler as b
on ( a.id=b.product_id and b.product_id in $list )
SET a.photos=b.photos, a.status=b.status;";
\Yii::$app->db->createCommand($myUpdate)->execute();
}
I know this is an old question, but I happened across it whilst researching something a little more complex. If I'm understanding the intention correctly, this should work:
Yii::$app->db->createCommand(
"UPDATE products p, crawler c
SET p.photos = c.photos, p.status = c.status
WHERE p.id = c.product_id;"
)->execute();
(I prefer the direct join syntax, particularly where the join is being used in an update.)

left join only fetches one row

This SQL only fetches one row from the table category when I'm using fetchAll(), I know there is atleast two rows with the same info. Any ideas?
Click here to see a more detailed explanation of what I thought the problem was
$query = " SELECT
category.*,
GROUP_CONCAT('category_hierarchy.category_id' SEPARATOR ',') AS subcategories
FROM category
LEFT JOIN category_hierarchy ON category.category_id = category_hierarchy.category_parent_id
WHERE category.type = '1'
ORDER BY category.sort_order ASC";
// Prepare.
$stmt = $dbh->prepare($query);
// Execute.
$stmt->execute();
// Fetch results.
$categories = $stmt->fetchAll();
$countedRows = count($categories);
foreach($categories as $category) {
$parent_arr = '';
if(!empty($category['subcategories'])) {
$parent_arr = array(display_children($category['subcategories']));
}
$arr[] = array(
'category_id' => $category['category_id'],
'title' => $category['title'],
'slug' => $category['slug'],
'url' => $category['url'],
'type' => $category['type'],
'sort_order' => $category['sort_order'],
'categories' => $parent_arr
);
}
You have a aggregate function group_concat and without group by clause it will always return one row, you may need to add a group by at the end.
SELECT
category.*,
GROUP_CONCAT('category_hierarchy.category_id' SEPARATOR ',') AS subcategories
FROM category
LEFT JOIN category_hierarchy ON category.category_id = category_hierarchy.category_parent_id
WHERE category.type = '1'
group by category.category_id
ORDER BY category.sort_order ASC

Combine mysql query with sub query results into one PHP array

I have a table that contains events, to list these events I loop through the event table, check the event type and look up it's value in it's specific table with it's eventId.
At the moment this uses one query to get the 20 events and then up to 3 queries to get the data on those events. I currently have it coded procedurally but need to change it so that at the end it just returns the data in array form.
Here's a Pseduo code example of what I need to achieve:
while(eventQuery):
if commentQueryResult;
$array .= commentQueryResult;
if forumPostQueryResult;
$array .= forumPostQueryResult;
if uploadItemQueryResult;
$array .= uploadItemQueryResult;
endwhile;
return $array; // Combined returned results as one array
I will then be able to access the data and just foreach loop through it.
I'm just not sure the best way to combine multiple result sets into an array?
OR you could try and combine them into one query ...
$eventResult = mysql_query(
'SELECT userevent.event, userevent.eventId, userevent.friendId
FROM userevent
WHERE userevent.userId = 1 OR userevent.friendId = 1
ORDER BY userevent.id DESC
LIMIT 20'
);
while ($eventRow = mysql_fetch_row($eventResult)){
if($eventRow[0] == 1){
$result = mysql_fetch_array(mysql_query("
SELECT forumRooms.id, forumRooms.title
FROM forumPosts
INNER JOIN forumRooms ON forumPosts.`forumTopic` = forumRooms.`id`
WHERE forumPosts.id = '$eventRow[1]'"));
}
elseif($eventRow[0] == 2){
$result = mysql_fetch_array(mysql_query("
SELECT game.id, game.uriTitle, game.title
FROM usergamecomment
INNER JOIN game ON usergamecomment.`gameId` = game.id
WHERE usergamecomment.id = $eventRow[1]"));
}
elseif($eventRow[0] == 4){
$result = mysql_fetch_array(mysql_query("
SELECT usercomment.comment, UNIX_TIMESTAMP(usercomment.TIME), `user`.id, `user`.username, `user`.activate
FROM usercomment
INNER JOIN `user` ON usercomment.`userId` = `user`.id
WHERE usercomment.id = $eventRow[1]
AND `user`.activate = 1"));
}
elseif($eventRow[0] == 5){
$result = mysql_fetch_array(mysql_query("
SELECT game.id, game.title, game.uriTitle
FROM game
WHERE game.id = $eventRow[1]"));
}
// Combined Results as array
}
I'm in the process of converting all of these to PDO, that's the next step after working out the best way to minimise this.
Challenge accepted. ;)
Since you are actually only interested in the results inside the while loop, you could try this single query. Due to the LEFT JOINS it might not be faster, pretty much depends on your database. The final $result contains all elements with their respective fields.
$result = array();
$q = 'SELECT userevent.event AS userevent_event,
userevent.eventId AS userevent_eventId,
userevent.friendId AS userevent_friendId,
forumRooms.id AS forumRooms_id,
forumRooms.title AS forumRooms_title,
game.id AS game_id,
game.uriTitle AS game_uriTitle,
game.title AS game_title,
usercomment.comment AS usercomment_comment,
UNIX_TIMESTAMP(usercomment.TIME) AS usercomment_time,
user.id AS user_id,
user.username AS user_username,
user.activate AS user_activate,
g2.id AS game2_id,
g2.uriTitle AS game2_uriTitle,
g2.title AS game2_title
FROM userevent
LEFT JOIN forumPosts ON forumPosts.id = userevent.eventId
LEFT JOIN forumRooms ON forumPosts.forumTopic = forumRooms.id
LEFT JOIN usergamecomment ON usergamecomment.id = userevent.eventId
LEFT JOIN game ON usergamecomment.gameId = game.id
LEFT JOIN usercomment ON usercomment.id = userevent.eventId
LEFT JOIN user ON usercomment.userId = user.id
LEFT JOIN game g2 ON userevent.eventId = g2.id
WHERE (userevent.userId = 1 OR userevent.friendId = 1)
AND userevent.eventId >= (SELECT userevent.eventId
WHERE userevent.userId = 1 OR userevent.friendId = 1
ORDER BY userevent.id DESC LIMIT 1,20);';
$r = mysql_query($q);
while ( $o = mysql_fetch_row($r) ) {
switch($o['userevent_event']) {
case 1:
$result[] = array(
'id' => $o['forumsRooms_id'],
'title' => $o['forumsRooms_title'],
);
break;
case 2:
$result[] = array(
'id' => $o['game_id'],
'uriTitle' => $o['game_uriTitle'],
'title' => $o['game_title'],
);
break;
case 4:
$result[] = array(
'comment' => $o['usercomment_comment'],
'time' => $o['usercomment_time'],
'id' => $o['user_id'],
'username' => $o['user_username'],
'activate' => $o['user_activate'],
);
break;
case 5:
$result[] = array(
'id' => $o['game2_id'],
'uriTitle' => $o['game2_uriTitle'],
'title' => $o['game2_title'],
);
break;
}
}
Note: Eventually, the query has to be edited slightly, I just wrote that out of my head w/o testing. Optimization can surely be done if I'd knew more about the db structure.
Also, this is merely a proof of concept that it can indeed be done with a single query. ;)

Categories