I have the following results returned via a mysql query:
tag_id article_id (asc) name tag_slug
56 69487 Exploration exploration
10 69488 Events events
32 69488 Military military
28 69489 Arts arts
3 69489 Religion churches
36 69490 Forestry forestry
8 69490 Industry industry
40 69490 Man-Made man-made
42 69490 Politics politics
I need to loop through the results and create a string that would include the tag_slugs associated with each set of article id's. The name and tag_id columns aren't necessary for this part of the code.
For example ...
69487 would have a string that is: '<exploration>'
69488 would have a string that is: '<events><military>'
69489 would have a string that is: '<arts><churches>'
69490 would have a string that is: '<forestry><industry><man-made><politics>'
...and a column named tags would be updated in the db with these strings and the respective article_id.
My attempt below works, kind of, but always leaves off the last db update. I'm sure there has to be a better more logical way but right now I cannot.
$previous_article_id = 0;
while( $row = $result->fetch_assoc() )
{
if ( $row['article_id '] != $previous_article_id && $previous_article_id != 0 )
{
$sql = "
UPDATE
".ARTICLE_TABLE."
SET
tags = '".$tags."'
WHERE
id = ".$previous_article_id."
";
$db->query($sql) OR sql_error($db->error.$sql);
$tags = '';
}
if ( $row['article_id '] == $previous_article_id || $previous_article_id == 0 )
{
$tags .= '<'.$row['tag_slug'].'>';
}
$previous_article_id = $row['article_id '];
}
Yes, I know it should be PDO and the codes a bit crazy, but I'm working on a friends brothers website so not much authority to change it.
First of all, my compliments for how you have explained your problem and what you have tried. Your code is also well formatted. I often see some crap posts here. So my compliments.
I should change the whole stuff to the following:
$article_tags = array();
while( $row = $result->fetch_assoc() )
{
$article_tags[$row['article_id']][] = $row['tag_slug'];
}
foreach( $article_tags as $article_id => $tag_array )
{
$tags = '';
foreach( $tag_array as $tag )
{
$tags .= '<' . $tag . '>';
}
$sql = "
UPDATE
".ARTICLE_TABLE."
SET
tags = '".$tags."'
WHERE
id = ".$article_id."
";
$db->query($sql) OR sql_error($db->error.$sql);
}
Maybe the cleaner way to do that with PHP is to create an associative array having your article_ids as keys and the string containing the related tags as values.
Eg.
$myArray[$row['article_id ']].='<'.$row['tag_slug'].'>';
Then, at the end you just iterate with a foreach loop over it and insert the data in your database.
You can more easily, in my opinion,fetch this results from the database by using GROUP_CONCAT function
SELECT article_id, CONCAT('<',GROUP_CONCAT(tag_slug separator '><'),'>')
from table GROUP BY article_id
ORDER BY article_id DESC
In case you have duplicates on your tag_slug you can use DISTINCT operator inside GROUP_CONCAT function
Related
I am trying to get the cost of all the items by the IDs and then add them up for a shopping cart. I have two issues:
1.) The users may have more than one item with the same ID. Using the method below mySql will not select the item twice. In the example below I have THREE items with the ID of '01' and TWO items with the ID of '07'. However my select statement will only select each ID once.
2.) The method I am using below adds a comma at the end of the last ID so my statement will not work. I need the comma's in between each item for the select but not on the last one. How can I resolve this issue?
if($_SESSION[InCart])
{
foreach($_SESSION[InCart] as $result)
{
$Items .= $result . ',';
}
}
include('../connect.php');
// EXAMPLE RESULT MAY LOOK LIKE THIS
// echo $Items (would print: 01,09,07,01,01,23,07,)
$sql = "SELECT Cost FROM cartItems WHERE itemNumber IN ('$Items')";
$result = $conn->query($sql);
$Cost = 0;
if ($result->num_rows > 0)
{
// output data of each row
while($row = $result->fetch_assoc()) {
$Cost = $Cost + $row["Cost"];
}
}
else {
echo "0 results";
}
$conn->close();
You should keep track of how many items of each type in your cart.
so in the cart you have now like
$items = [1,1,1,2,6];
you should have them like this
$items = [
'item-1' => 3,
'item-2' => 1,
'item-6' => 1
];
I never use integers for associate keys, if you sort ( or some other array functions ) an array like that it will mess it up by reordering or resetting the numeric keys, [1=>3,2=>1,6=>1] could become [0=>3,1=>1,2=>1] for example. You can make it more readable and protect them by prefixing them with a string like item-, So then you just do something like:
$itemIDs = [];
foreach( $items as $itemID => $Quantity ){
//replace item- with empty, you could also use substr, or even str_replace.
$itemIDs[] = (int)preg_replace('/^item-/', '', $itemID);
}
That will give you a list of ids like you have ( minus the duplicates )
$itemIDs = [1,2,6];
Do your search and in the while loop
while($row = $result->fetch_assoc()) {
//correlate it back to original $items using the key [item-{ID}] to get the quantity
$Cost += ( $row["Cost"] * $items['item-'.$row['ID']] ); //cost + ( item cost * quantity )
}
Multiply the items cost by the quantity in the cart, add that to your sum....
For this bit in your sql:
$sql = "SELECT Cost FROM cartItems WHERE itemNumber IN ('$Items')";
instead do this
$sql = 'SELECT Cost FROM cartItems WHERE itemNumber IN ('.implode(',', $itemIDs) .')';
You wont need to quote them because we cast them to int in the foreach loop. This is a regular expression that matches ^ starts with item-, so it matches the item- and removes it. ( see it in action here https://regex101.com/r/pLqDWw/1 )
(int)preg_replace('/^item-/', '', $itemID);
So that will come out like ( I think, just doing it in my head )
$sql = 'SELECT Cost FROM cartItems WHERE itemNumber IN (1,2,6)';
Which is fine because generally integers don't need to be quoted in MySql. This also has the benefit of sanitizing them to prevent any Sql Injection, because casting them wont allow any alphabet or special characters though.
I changed the " to single quotes ', it just makes it look better when used as a query without variable interpolation "$var" for example.
UPDATE: to add items
$items = isset($_SESSION['items']) ? $_SESSION['items'] : []; //check if items array is saved in the session.
$key = 'item-'.$_POST['itemID'];
if( !isset( $items[$key] ) ){
$items[$key] = $_POST['quantity'];
}else{
$items[$key] += $_POST['quantity'];
}
$_SESSION['items'] = $items;
Assuming $_POST['itemID'] is the items id and $_POST['quantity'] is the number to add. Same goes for subtracting except for that I would add
if( $items[$key] <= 0 ){
unset( $items[$key] ); //remove item from list.
}
You can use a join for this calculation -- because you explicit want duplicates. However, the query is a bit more challenging to generate:
SELECT SUM(cost) as cost
FROM cartItems c JOIN
(SELECT 1 as itemNumber UNION ALL
SELECT 1 UNION ALL
SELECT 2
) i
ON i.itemNumber = c.itemNumber;
Note: You should use aggregation in the query to add all the values together. That is the best way to use SQL for this purpose.
I have the following code (SQL-query tested in phpMyAdmin and seems to work) that fetch data from selected columns:
$ids = isset($_REQUEST['id']) ? $_REQUEST['id'] : array();
if (is_array($ids)) $ids = implode(',', $ids);
if (!empty($ids)) {
$sql = "SELECT upload, upload2, upload3, upload4, upload5 FROM wp_site_table WHERE cid IN($ids) ORDER BY FIELD(cid, $ids)";
$results = $wpdb->get_results($sql) or die(mysql_error());
foreach( $results as $result ) { ... } }
The problem I have is that I want to output all fields from selected cid.
This code will only display the first cid result.
echo $result->upload;
Let's say $ids contain cid 1, 4 and 7 I'd like to output upload, upload2, upload4, upload5 from all those specific rows. How? Use an array in some way?
kind regards
Johan
If you want to echo all the fields, just do it:
echo $result->upload . '<br>' . $result->upload2 . '<br>' . $result->upload3 .
'<br>' . $result->upload4 . '<br>' . $result->upload5;
You should perhaps consider redesigning your database so you don't have repeated fields in each row. It would be better to have another table where each upload is in a row of its own, so there's no hard-coded limit on the number of these values. Then you can use a JOIN query to get all the uploads, and they'll be in an array.
With '$wpdb->get_results' output defaults to OBJECT type. Try using pre defined constant : ARRAY_A , it will result the output as an associative array.
$sql = "SELECT upload, upload2, upload3, upload4, upload5 FROM wp_site_table WHERE cid IN($ids) ORDER BY FIELD(cid, $ids)";
$results = $wpdb->get_results($sql, ARRAY_A) or die(mysql_error());
To access, simply use :
foreach( $results as $result ){
echo $result['upload'];
}
UPDATE: Still can't seem to figure it out. If anyone can lend a hand, it would be appreciated ^^.
I am having a problem and I'm not sure where my code is breaking down. I have a 'follow' function where you can follow different registered users. Their userID's of who you followed are stored in an array (follower_array). It's retrieving each member from the array, but of each member that's followed instead of displaying all the content, it's only displaying the 1 latest one from each member.
$broadcastList= "";
if ( $follower_array != "" ) {
$followArray = explode(",", $follower_array);
$followCount = count($followArray);
$i = 0;
$broadcastList .= "<table>";
foreach( $followArray as $key => $value ) {
$i++;
$sqlName = mysql_query("
SELECT username, fullname
FROM members
WHERE id='$value'
LIMIT 1
") or die ("error!");
while ( $row = mysql_fetch_array($sqlName) ) {
$friendUserName = substr($row["username"],0,12);
}
$sqlBroadcast = mysql_query("
SELECT mem_id, bc, bc_date, device
FROM broadcast
WHERE mem_id='$value'
ORDER BY bc_date ASC
LIMIT 50
") or die ("error!");
while ( $bc_row = mysql_fetch_array($sqlBroadcast) ) {
$mem_id = $bc_row['mem_id'];
$bc = $bc_row['bc'];
$dev = $bc_row['device'];
$bc_date = $bc_row['bc_date'];
$broadcastList .= "<td><a href='member.php?id=' .$value. ''>
$friendUserName</a> • $when_bc • Via: $dev <b>$bc</b></td></tr>";
}
broadcastList .= "</table>";
}
}
So, it's retrieving the members entirely, but not more than their single latest "broadcast." Any insight would be appreciated.. thank you!
Here's what's happening:
while( $row = some_next_result_function() ){
$result = $row["thing"];
}
Is going to overwrite $result every time, so you'll only end up with the last result.
You need to make a list and append to it instead.
I'm gonna take a shot in the dark:
$broadcastList .= "<td><a href='member.php?id=' .$value. ''>
$friendUserName</a> • $when_bc • Via: $dev <b>$bc</b></td></tr>";
That line pretty much misses a starting "tr" element. Are you sure the content isn't shown simply because of the faulty html markup?
Also:
broadcastList .= "</table>";
You're missing the $ there ;).
I don't know if this fixes it or even helps you; but I sure hope so :). Alternatively; you can also check the HTML source to see if the entries really aren't there (see first remark).
The title could be confusing, but I think my doubt is clear.
I'll explain. I'm doing this query:
$sql = 'SELECT * FROM calendar WHERE day = "'.$day.'" AND month = "'.$month.'" AND year = "'.$year.'" AND realizada = 0 AND colaborador = "What to put here?!"';
But the field "colaborador" is a serialized array.
One example, when I print_rthe value of my array after unserialize it's something like this:
Array ( [0] => l3gion [1] => Someone [2] => teste )
Imagine that I want to search for "l3gion" in the previous Query, how can I do this?
Thank you.
l3gion
If you need to query individual elements from your array, don't store the serialized array. Store each element on an individual row in a child table, and associate it with the primary key value of your calendar table:
CREATE TABLE colaboradoras (
calendar_id INT NOT NULL,
colaborador VARCHAR(20) NOT NULL,
FOREIGN KEY (calendar_id) REFERENCES calendar(calendar_id)
);
INSERT INTO colaboradoras VALUES
(1234, 'l3gion'),
(1234, 'Someone'),
(1234, 'teste');
$sql = "SELECT * FROM calendar AS c JOIN colaboradoras AS o
ON c.calendar_id = o.calendar_id
WHERE c.day = $day AND c.month = $month AND c.year = $year
AND c.realizada = 0 AND o.colaborador = 'l3gion'";
This is the normalized approach.
If you must store the serialized array, you might check out How FriendFeed Uses MySQL to index "schemaless" data. But that also involves creating new tables for the indexes.
If you really can't create any new tables or indexes, you can try to use LIKE or REGEXP but both of these solutions will be very inefficient and error-prone.
SELECT ... WHERE ... AND colaborador REGEXP '[[:<:]]s:6:\"l3gion\";'
You're screwed.
The short answer: Don't serialize data into a database field. That's what normalization is for...
The long answer is it's going to be VERY difficult to do. You could do a search for colaborador LIKE '%"13gion"%'... You could write a regex to deal with it as well (And use MySQL's REGEXP expression)...
But the best solution is to not store serialized data in the database...
colaborador = "What to put here?!"
This code will deal with serialized and raw data, as well as array and plain string values.
if (is_string($colaborador)) {
// prevent errors from showing
ob_start();
$data = unserialize($colaborador);
// $data is now false or output buffer is non-empty if $colaborador was
// not serialized
$data = ob_get_length() > 0 || false === $data ? $colaborador : $data;
// lose the possible error
ob_end_clean();
} else {
$data = $colaborador;
}
// use the first value as colaborador if an array
$sql = 'SELECT * FROM calendar WHERE day = "'.$day.'" AND month = "'.$month.'"
AND year = "'.$year.'" AND realizada = 0 AND colaborador = \'' .
(is_array($data) ? (empty($data) ? '' : $data[0]) : $data) . '\'';`
Or if you want to search all of them:
$sql = 'SELECT * FROM calendar WHERE day = "'.$day.'" AND month = "'.$month.'"
AND year = "'.$year.'" AND realizada = 0 AND colaborador = IN (\'' .
(is_array($data) ? (empty($data) ? '' : implode("', '", $data[0])) : $data) . '\')';`
I'm trying to generate a tree structure from a table in a database. The table is stored flat, with each record either having a parent_id or 0. The ultimate goal is to have a select box generated, and an array of nodes.
The code I have so far is :
function init($table, $parent_id = 0)
{
$sql = "SELECT id, {$this->parent_id_field}, {$this->name_field} FROM $table WHERE {$this->parent_id_field}=$parent_id ORDER BY display_order";
$result = mysql_query($sql);
$this->get_tree($result, 0);
print_r($this->nodes);
print_r($this->select);
exit;
}
function get_tree($query, $depth = 0, $parent_obj = null)
{
while($row = mysql_fetch_object($query))
{
/* Get node */
$this->nodes[$row->parent_category_id][$row->id] = $row;
/* Get select item */
$text = "";
if($row->parent_category_id != 0) {
$text .= " ";
}
$text .= "$row->name";
$this->select[$row->id] = $text;
echo "$depth $text\n";
$sql = "SELECT id, parent_category_id, name FROM product_categories WHERE parent_category_id=".$row->id." ORDER BY display_order";
$nextQuery = mysql_query($sql);
$rows = mysql_num_rows($nextQuery);
if($rows > 0) {
$this->get_tree($nextQuery, ++$depth, $row);
}
}
}
It's almost working, but not quite. Can anybody help me finish it off?
You almost certainly, should not continue down your current path. The recursive method you are trying to use will almost certainly kill your performance if your tree ever gets even slightly larger. You probably should be looking at a nested set structure instead of an adjacency list if you plan on reading the tree frequently.
With a nested set, you can easily retrieve the entire tree nested properly with a single query.
Please see these questions for a a discussion of trees.
Is it possible to query a tree structure table in MySQL in a single query, to any depth?
Implementing a hierarchical data structure in a database
What is the most efficient/elegant way to parse a flat table into a tree?
$this->nodes[$row->parent_category_id][$row->id] = $row;
This line is destroying your ORDER BY display_order. Change it to
$this->nodes[$row->parent_category_id][] = $row;
My next issue is the $row->parent_category_id part of that. Shouldn't it just be $row->parent_id?
EDIT: Oh, I didn't read your source closely enough. Get rid of the WHERE clause. Read the whole table at once. You need to post process the tree a second time. First you read the database into a list of arrays. Then you process the array recursively to do your output.
Your array should look like this:
Array(0 => Array(1 => $obj, 5 => $obj),
1 => Array(2 => $obj),
2 => Array(3 => $obj, 4 => $obj),
5 => Array(6 => $obj) );
function display_tree() {
// all the stuff above
output_tree($this->nodes[0], 0); // pass all the parent_id = 0 arrays.
}
function output_tree($nodes, $depth = 0) {
foreach($nodes as $k => $v) {
echo str_repeat(' ', $depth*2) . $v->print_me();
// print my sub trees
output_tree($this->nodes[$k], $depth + 1);
}
}
output:
object 1
object 2
object 3
object 4
object 5
object 6
I think it's this line here:
if($row->parent_category_id != 0) {
$text .= " ";
}
should be:
while ($depth-- > 0) {
$text .= " ";
}
You are only indenting it once, not the number of times it should be indented.
And this line:
$this->get_tree($nextQuery, ++$depth, $row);
should be:
$this->get_tree($nextQuery, $depth + 1, $row);
Note that you should probably follow the advice in the other answer though, and grab the entire table at once, and then process it at once, because in general you want to minimize round-trips to the database (there are a few use cases where the way you are doing it is more optimal, such as if you have a very large tree, and are selecting a small portion of it, but I doubt that is the case here)