multilevel menu item deletion recursion function - php

I want to delete category from multilevel menu with recursion function. To understand situation more carefully , lets take a look first at mysql table structure
CREATE TABLE IF NOT EXISTS menu(
id INT(5) NOT NULL AUTO_INCREMENT PRIMARY KEY,
p_id INT(5),
sort_id INT(5) NOT NULL,
title VARCHAR(50) CHARSET utf8 COLLATE 'utf8_unicode_ci' NOT NULL,
);
it looks like this when it is retrieved
id | p_id | sort_id | title |
1 | 0 | 1 | root1 |
2 | 1 | 1 | sub of root1
3 | 0 | 2 | root2 |
4 | 2 | 1 | sub of "sub of root1"
... | ... | ... | ....
etc ...
I've written php script for delete category, here it is =>
function del_cat($connection,$id){
if (!$connection->connect_errno){
if ($connection->set_charset("utf8")){
if ($r = $connection->query("SELECT id FROM menu WHERE p_id=" . $id . "")){
if ($r->num_rows>0){
while ($row = $r->fetch_assoc()){
del_cat($connection,$row['id']);
}
} else {
$connection->query("DELETE FROM menu WHERE id=" . $id . "");
}
$r->free();
}
}
}
}
$connection variable is a just mysql connection object, and $id is id in table.
It works just fine when I'm deleting one row ( I mean when category doesn't have a child , sub categories), for example 1 | 0 | 1 | root1 |, but when I want to delete for example 4 | 2 | 1 | sub of "sub of root1" it doesn't delete category with sub categories . Any idea how to solve this problem ? thanks

The delete is only in else so you never actually delete the parent category. I think you need something like:
del_cat($connection, $row['id']);
$connection->query("DELETE ...");
} else {
$connection->query("DELETE ...");
By the way you should escape the ID input.

Related

using one key to multiple values in php

I'm developing a webaplicattion in php where there will be an area called 'My Products'. This area you can see the products that you listed. Lets say you placed a car to sell. You will have something like:
Model: R8
Color: Yellow
Brand: Audi
Type: Diesel
Price: 90000
CarID: 1
My problem: How can I select the carid so that I have the cardid as the key and the other values (eg. model, color, etc...) as values?
I'm using mysql to store the listings. SQL TABLE:
+---------+------------------------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+------------------------------------------+------+-----+---------+----------------+
| carid | int(11) | NO | MUL | NULL | auto_increment |
| brand | enum('Alfa Romeo','Aston Martin','Audi') | NO | | NULL | |
| color | varchar(20) | NO | | NULL | |
| type | enum('gasoline','diesel','eletric') | YES | | NULL | |
| price | mediumint(8) unsigned | YES | | NULL | |
| mileage | mediumint(8) unsigned | YES | | NULL | |
| model | text | YES | | NULL | |
| year | year(4) | YES | | NULL | |
| user | varchar(30) | YES | | NULL | |
+---------+------------------------------------------+------+-----+---------+----------------+
My pseudo code is something like this:
Fetch ID of the cars that was listed by certain user;
Use that ID as a key;
Get every other options to be the values (diesel, yellow, etc...)
I have tried some codes since array_combine to foreach inside foreach.
This is the code I have ended up so far:
$qry_id = "select carid from cars where user='$login'";
if ($car_id = mysqli_query($link, $qry_id)){
while ($row = mysqli_fetch_assoc($car_id)){
//It will fetch the iD of the user in the DB
$user_id = $row["carid"];
$qry_model = "select model from cars where carid='$user_id'";
if($model_obj = mysqli_query($link, $qry_model)){
while ($row_2 = mysqli_fetch_assoc($model_obj)){
$model = $row_2['model'];
$final_array = array($user_id => $model);
}
}
}
}
I don't know if this is the right approach since it's my first webapp I'm building.
I'm just using the model value in this code for the simplicity sakes.
$q = "SELECT * FROM cars where user=?";
if ($stmt = $pdo->preapre($q)){
$result=[];
$stmt->execute([$login]);
while ($row = $stmt->fetchObject()){
$carId = $row->carid;
unset($row->carid);
$result[$carId]=$row;
}
}
now note that $pdo is object of pdo connecttion to the database not mysqli, you can do the same thing with mysqli but i am not uses to it
also i don't recommend that you use the user name on every column you should instead store the userId as foreign key to the primary key id in the table user
that will save alot more storage make the query faster ( it's easier to look for numbers than string ) and user can change it's name without having to change his name in all other tables ( userid won't be changing of course )
also carid should be unsigned int(10) not int(11) if wondering why 10 see that post MySQL datatype INT(11) whereas UNSIGNED INT(10)?
To avoid SQL injection, use prepared statements. You can use one query to fetch all attributes for the cars:
$qry_id = "select carid, model, price, color from cars where user=?";
$stmt = mysqli_prepare($link , $qry_d) or die("SQL statement error");
// Bind the login parameter to the statement
mysqli_stmt_bind_param($stmt, "s", $login);
mysqli_stmt_execute($stmt);
// bind every column in the SELECT
mysqli_stmt_bind_result($stmt, $user_id, $carid, $model, $price, $color);
while (mysqli_stmt_fetch($stmt)){
$final_array[] = array(
"model" => $model,
"price" => $price,
"color" => $color
);
}
You may do the following
Note this query is not safe and pron to SQL Injection, I would recommend to use prepared statements or PDO
The $carArray variable will finally have the array with carid as key in it
$query = "select * from cars where user='$login'";
$result = mysqli_query($query);
$carArray = array();
while ($row = mysqli_fetch_assoc($result)){
$carArray[$row['carid']] = $row;
}

Count descendants in hierarchical query

I was told that PostgreSQL is a better choice than MySQL for displaying hierarchical data, so I installed PostgreSQL and I'm ready to go.
This is the schema from my title (copied from pgAdmin):
CREATE TABLE public.gz_life_mammals (
id integer NOT NULL,
taxon text NOT NULL,
parent text NOT NULL,
parent_id smallint NOT NULL,
slug text,
name_common text,
plural text,
extinct smallint NOT NULL,
rank smallint NOT NULL,
key smallint NOT NULL,
CONSTRAINT "Primary Key" PRIMARY KEY (id)
);
This is my database connection and first query:
$dbh = pg_connect("host=localhost dbname=geozoo user=postgres");
if (!$dbh) {
die("Error in connection: " . pg_last_error());
}
$sql = "SELECT * FROM gz_life_mammals";
$result = pg_query($dbh, $sql);
while ($row = pg_fetch_array($result)) {
echo "ID: " . $row[0] . " | ";
echo "Taxon: " . $row[1] . " | ";
echo "ParentID: " . $row[3] . "<br>";
}
// free memory
pg_free_result($result);
// close connection
pg_close($dbh);
The most important table fields for this exercise are the first four (id, taxon, parent and parent_id. The data looks like this:
ID | TAXON | PARENT | PARENT_ID
1 | Mammalia | Chordata | 1
2 | Carnivora | Mammalia | 2
3 | Canidae | Carnivora | 3
4 | Canis | Canidae | 4
5 | Canis-lupus | Canis | 5
6 | Canis-latrans | Canis | 5
Where the last two rows represent the wolf (Canis lupus) and coyote (Canis latrans). Eventually, I'd like to be able to display the names of children, grandchildren, parents, great grandparents, etc. But right now I'm just trying to display the number of descendants. For example, if I navigated to MySite/life/mammalia, I might see the following display:
Orders: 19
Families: 58
Genera: 688
Species: 8,034
If I navigated to MySite/life/canidae, it might display something like this:
Genera: 6
Species: 37
Can anyone show me the best way to write that kind of query and display the results (with PHP)?
Given the table:
select * from gz_life_mammals;
id | taxon | parent | parent_id
----+---------------+-----------+-----------
1 | Mammalia | Chordata | 1
2 | Carnivora | Mammalia | 2
3 | Canidae | Carnivora | 3
4 | Canis | Canidae | 4
5 | Canis-lupus | Canis | 5
6 | Canis-latrans | Canis | 5
(6 rows)
and the function to translate parent_id into taxonomic rank name:
create function tax_rank(id integer) returns text as $$
select case id
when 1 then 'Classes'
when 2 then 'Orders'
when 3 then 'Families'
when 4 then 'Genera'
when 5 then 'Species'
end;
$$ language sql;
you can query number of descendants with the following recursive query:
with recursive hier(taxon,parent_id) as (
select m.taxon, null::integer
from gz_life_mammals m
where taxon='Mammalia' --<< substitute me
union all
select m.taxon, m.parent_id
from hier, gz_life_mammals m
where m.parent=hier.taxon
)
select tax_rank(parent_id),
count(*) num_of_desc
from hier
where parent_id is not null
group by parent_id
order by parent_id;
tax_rank | num_of_desc
----------+-------------
Orders | 1
Families | 1
Genera | 1
Species | 2
(4 rows)
The interesting part is inside with recursive. The first part of the query selects the root row(s) of hierarchy. The second part (after union all) is called recursively and each time adds direct descendants to the previous result set. Read this to understand how it works in details.
After hierarchy is constructed, it can be represented as you like. In the above example only number of descendants are shown. You can get names as well:
with recursive hier(taxon,parent_id) as (
...
)
select tax_rank(parent_id),
taxon as name
from hier
where parent_id is not null
order by parent_id;
tax_rank | name
----------+---------------
Orders | Carnivora
Families | Canidae
Genera | Canis
Species | Canis-lupus
Species | Canis-latrans
(5 rows)
The same on one line:
with recursive hier(taxon,parent_id) as (
...
)
select tax_rank(parent_id),
string_agg(taxon,', ') as names
from hier
where parent_id is not null
group by parent_id
order by parent_id;
tax_rank | names
----------+----------------------------
Orders | Carnivora
Families | Canidae
Genera | Canis
Species | Canis-lupus, Canis-latrans
(4 rows)
And so on...

Query returns one post late

So I have a query that gets all the posts in the database. Say I post a post, it won't show, till I post another post. So essentially there's a post delay, until you post another post. That's the best I can explain it. Here's my query
SELECT * FROM comments WHERE comment_post_id = :id
Then I create an array
//Get post comments
$cmt = $con->prepare("SELECT * FROM comments WHERE comment_post_id = :id");
$cmt->bindValue(':id', $id, PDO::PARAM_INT);
$cmt->execute();
$cmtc = $cmt->fetch(PDO::FETCH_ASSOC);
$comments = array();
while($row = $cmt->fetch(PDO::FETCH_ASSOC)){
$comments[] = array(
'comment_user' => $row['comment_user'],
'comment_ip' => $row['comment_ip'],
'comment_date' => $row['comment_date'],
'comment_content' => $row['comment_content'],
'comment_post_id' => $row['comment_post_id']
);
}
And finally display it
<?php foreach($comments as $comment){?>
<h1 class="message"><?php echo $comment['comment_content']?></h1>
<?php } ?>
I'm not getting any errors, but when I run the query in the command line I get one result. But when I visit the page I don't see any results.
Here's the structure
+-----------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------------+------+-----+---------+----------------+
| id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| comment_user | varchar(255) | YES | | NULL | |
| comment_ip | varchar(255) | YES | | NULL | |
| comment_date | varchar(255) | YES | | NULL | |
| comment_content | varchar(85) | YES | | NULL | |
| comment_post_id | varchar(255) | YES | | NULL | |
+-----------------+------------------+------+-----+---------+----------------+
Your first call to fetch automatically increments the internal pointer of the result set by one after fetching the row:
$cmtc = $cmt->fetch(PDO::FETCH_ASSOC);
The above returns the zeroth row and moves the pointer to the first row. So, when you get to your while loop, the pointer is offest by one, and nothing is found:
while($row = $cmt->fetch(PDO::FETCH_ASSOC)){
The reason is that line:
$cmtc = $cmt->fetch(PDO::FETCH_ASSOC);
You should simple remove it.
Otherwise you fetch first record and don't assign it to $comments variable and then in loop you try to get records from 2 to n
To fetch all results of your query try this:
$cmt = $con->prepare("SELECT * FROM comments WHERE id = :id");
$cmt->bindValue(':id', 1);
$cmt->execute();
$comments = $cmt->fetchAll(PDO::FETCH_ASSOC);
PS. your schema use id as primary key, but your query use comment_post_id.

recursive function for dynamic multilevel menu php

I've a mysql table structure like this:
CREATE TABLE IF NOT EXISTS menu(
id INT(5) NOT NULL AUTO_INCREMENT PRIMARY KEY,
p_id INT(5), -- parent id
sort_id INT(5) NOT NULL, -- for position
title VARCHAR(50) NOT NULL,
etc ...);
data structure would be something like this:
id | p_id | sort_id | title
1 | NULL | 1 | root_1
2 | 1 | 1 | sub1 of root_1
3 | 1 | 2 | sub2 of root_1
4 | NULL | 2 | root_2
5 | 2 | 1 | sub1 of root_2
6 | 2 | 2 | sub2 of root_2
7 | 3 | 1 | sub1 of `sub2 of root_1`
I've created a php script to show up one level sub menu, but I can't make up my mind how to get other levels. I think a recursive function is needed, for example, to get sub1 of sub2 of root_1 element in this task.
If anyone has any idea how to start creating a recursive function in this situation , please advise me, thanks :)
It might be best to first turn this into a tree type structure:
Menu Top
|
Nodes with NULL p_id
|
Children
You could do this by creating a MenuNode class that has an array of children. You don't have to do it that way, but it will make it much easier to create a recursive function to output the menu.
function generate_menu_list($parent_id)
{
$result = mysql_query("SELECT * FROM menu WHERE p_id ='$parent_id' order by sort_id ");
if (result)
{
while ($row = mysql_fetch_array($result))
{
$count = mysql_query("SELECT count(0) as cnt FROM menu_master WHERE parent_id='".$row['id']."'");
$countrow = mysql_fetch_array($count);
echo '<li><span class="fa fa-user"></span>'.$row['title '].' ';
if($countrow['cnt']>0)
{
echo '<ul>';
$this->generate_menu_list($row['id']);
echo '</ul>';
}
echo '</li>';
}
}
}

AddItem function does not seem to be working

I go this thing to sorta work but my AddItem function doesn't seem to work. I click the "add item" link on the products page and it takes me to the cart page. But nothing is on the cart page. I am hoping somoneone can see it and tell me what to do to correct it.
The AddItem function:
function AddItem($itemId, $qty) {
// Will check whether or not this item
// already exists in the cart table.
// If it does, the UpdateItem function
// will be called instead
// Check if this item already exists in the users cart table
$result = mysql_query("select count(*) from cs368_cart where cookieID = '" . GetCartID() . "' and itemId = $itemId");
$row = mysql_fetch_row($result);
$numRows = $row[0];
if($numRows == 0) {
// This item doesn't exist in the users cart,
// we will add it with an insert query
mysql_query("insert into cs368_cart(cookieID, itemId, qty) values('" . GetCartID() . "', $itemId, $qty)");
}
else {
// This item already exists in the users cart,
// we will update it instead
UpdateItem($itemId, $qty);
}
}
I just checked my cs368_cart tables of the database and it is empty.
mysql> select * from cs368_cart
-> ;
Empty set (0.00 sec)
So apparently nothing is being added. I am wondering if my query is correct?
My tables:
mysql> select * from cs368_products
-> ;
+--------+----------------+---------------------------------------------+-----------+
| itemId | itemName | itemDesc | itemPrice |
+--------+----------------+---------------------------------------------+-----------+
| 1 | French Vanilla | A medium blend with a hint vanilla | 9.79 |
| 2 | Hazelnut Cream | A light blend with a spicy note of Hazelnut | 9.69 |
| 3 | Columbian | A medium-dark blend straight up | 9.89 |
+--------+----------------+---------------------------------------------+-----------+
3 rows in set (0.00 sec)
and my cart tables;
mysql> show columns from cs368_cart;
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| cartId | int(11) | NO | PRI | NULL | auto_increment |
| cookieId | varchar(50) | NO | | | |
| itemId | int(11) | YES | | NULL | |
| qty | int(11) | YES | | NULL | |
+----------+-------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
This is my GetCartId I have in a seperate php file which is bieng called correctly by the php file with this AddItem function.
function GetCartId(){
if(isset($_COOKIE["cartId"])){
return $_COOKIE["cartId"];
}
else {
session_start();
setcookie("cartId", session_id(), time()+((3600*24)*30));
return session_id();
}
It looks like you are trying to insert cartId as a string when your table has cartId as an integer. Take a closer look at your SQL in the PHP strings.
You should change your insert query to something similar to below which will tell you what is wrong with your insert sataement (also it is good practice to handle any errors gracefully)
<?php
$queryResult = mysql_query("insert into cs368_cart(cookieID, itemId, qty) values('" . GetCartID() . "', $itemId, $qty)");
if (!$queryResult) {
die('Invalid query: ' . mysql_error());
}
?>
based on example in http://php.net/manual/en/function.mysql-query.php

Categories