Filtering PDO SQL Queries - php

I'm using PDO method in my PHP page to implode two strings (in order to create a graph... long story!), but I don't know how to filter them. The table structure in MySQL is like this:
Column Name:
item (the name of the item being voted on);
nvote (the number of votes this item has received);
date (the dates of the votes)
This is what I have so far:
$stmt1 = $db->prepare("SELECT date FROM vote");
$stmt1->execute();
$stmt2 = $db->prepare("SELECT nvote FROM vote");
$stmt2->execute();
$line1 = implode( ',', $stmt1->fetchAll (PDO::FETCH_COLUMN) );
$line2 = implode( ',', $stmt2->fetchAll (PDO::FETCH_COLUMN) );
(Line1 creates the x-axis of my graph, line2 creates the y-axis)
This works very well to generate the strings for my graph.
How can I filter these so that name of the item being voted on (in the "item" column") is filtered by a var (in this case, $nameofitem)? I can't seem to get it to work.

$params = array(':value' => 'Hello World');
$stmt1 = $db->prepare("SELECT date FROM vote WHERE item = :value ORDER BY date");
$stmt1->execute($params);
$stmt2 = $db->prepare("SELECT nvote FROM vote WHERE item = :value ORDER BY date");
$stmt2->execute($params);
$line1 = implode( ',', $stmt1->fetchAll (PDO::FETCH_COLUMN) );
$line2 = implode( ',', $stmt2->fetchAll (PDO::FETCH_COLUMN) );
The key is adding a WHERE condition in the SQL query with a parameter that is bound by execute when passed an array.
Note that I also added an order statement for you as there is no guarantee of the result ordering without it, meaning the points you saw on the graph could have been associated with the wrong point. Date might not be the best choice, use a unique key if you can. Alternatively, you could fetch everything in a single query and build your lists with post-processing.

Related

I need to nest 2 arrays so that I can echo order header as well as order item details

I have updated my original post based on what I learned from your comments below. It is a much simpler process than I originally thought.
require '../database.php';
$pdo = Database::connect();
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "SELECT * FROM Orders WHERE id = 430";
$q = $pdo->prepare($sql);
$q->execute(array($id));
$data = $q->fetch(PDO::FETCH_ASSOC);
echo 'Order Num: ' . $data['id'] . '<br>';
$sql = "SELECT * FROM Order_items
JOIN Parts ON Parts.id = Order_Items.part_id
WHERE Order_Items.orders_id = 430";
$q = $pdo->prepare($sql);
$q->execute(array($line_item_id));
$data = $q->fetch(PDO::FETCH_ASSOC);
while ($data = $q->fetch(PDO::FETCH_ASSOC))
{
echo '- ' . $data['part_num'] . $data['qty'] . "<br>";
}
Database::disconnect();
Unfortunately, only my first query is producing results. The second query is producing the following ERROR LOG: "Base table or view not found: 1146 Table 'Order_items' doesn't exist" but I am expecting the following results.
Expected Results from Query 1:
Order Num: 430
Expected Results from Query 2:
- Screws 400
- Plates 35
- Clips 37
- Poles 7
- Zip ties 45
Now that I understand where you are coming from, let's explain a couple of things.
1.PDO and mysqli are two ways of accessing the database; they essentially do the same things, but the notation is different.
2.Arrays are variables with multiple "compartments". Most typical array has the compartments identified by a numerical index, like:
$array[0] = 'OR12345'; //order number
$array[1] = '2017-03-15'; //order date
$array[2] = 23; //id of a person/customer placing the order
etc. But this would require us to remember which number index means what. So in PHP there are associative arrays, which allow using text strings as indexes, and are used for fetching SQL query results.
3.The statement
$data = $q->fetch(PDO::FETCH_ASSOC)
or
$row = $result->fetch_assoc()
do exactly the same thing: put a record (row) from a query into an array, using field names as indexes. This way it's easy to use the data, because you can use field names (with a little bit around them) for displaying or manipulating the field values.
4.The
while ($row = $result->fetch_assoc())
does two things. It checks if there is a row still to fetch from the query results. and while there is one - it puts it into the array $row for you to use, and repeats (all the stuff between { and }).
So you fetch the row, display the results in whatever form you want, and then loop to fetch another row. If there are no more rows to fetch - the loop ends.
5.You should avoid using commas in the FROM clause in a query. This notation can be used only if the fields joining the tables are obvious (named the same), but it is bad practice anyway. The joins between tables should be specified explicitly. In the first query you want the header only, and there is no additional table needed in your example, so you should have just
SELECT *
FROM Orders
WHERE Orders.Order_ID = 12345
whereas in the second query I understand you have a table Parts, which contains descriptions of various parts that can be ordered? If so, then the second query should have:
SELECT *
FROM Order_items
JOIN Parts ON Parts.ID = Order_Items.Part_ID
WHEERE Order_Items.Order_ID = 12345
If in your Orders table you had a field for the ID of the supplier Supplier_ID, pointing to a Suppliers table, and an ID of the person placing the order Customer_ID, pointing to a Customers table, then the first query would look like this:
SELECT *
FROM Orders
JOIN Suppliers ON Suppliers.ID = Orders.Supplier_ID
JOIN Customers ON Customers.ID = Orders.Customer_ID
WHERE Orders.Order_ID = 12345
Hope this is enough for you to learn further on your own :).

PHP SQLSRV Sorting with Parameter of Prepared Statement

I can't figure out why sorting will work as long as I'm not using $sort as a passed in parameter. Example below will work for sorting:
$sort = "quantity desc";
$sql = " with items as (
SELECT i.[item_id]
,i.[name]
,i.[value]
,i.[quantity]
,i.[available]
,isnull(r.awarded, 0) as awarded
, ROW_NUMBER() OVER(
ORDER BY $sort
) rowNumber
FROM [Intranet].[dbo].[Goodwell_Item] i
LEFT JOIN (
SELECT r.item_id
, COUNT(1) awarded
from [Intranet].[dbo].[Goodwell_Reward] r
group by r.item_id
) as r
ON i.item_id = r.item_id
)
SELECT *
FROM items
WHERE rowNumber BETWEEN (?) and (?)
and ( (?) = '' OR (available = (?)))
";
$params = array( $pagify['startFrom'], $end, $available, $available );
$stmt = sqlsrv_query( $conn, $sql, $params );
However if I change the line with ORDER BY to:
ORDER BY (?)
and add it to my $params like so:
$params = array($sort, $pagify['startFrom'], $end, $available, $available );
then the sort for some reason is being ignored.
Please tell me how to get the sort working in a way that doesn't allow SQL injection.
I am dealing with this exact issue right now, and cannot find anything online to help.
I have tried:
$query = "SELECT * FROM {$this->view} WHERE SeriesID = ? ORDER BY ? ";
$result = $conn->getData($query, array($seriesID,$sortBy));
and
$query = "SELECT * FROM {$this->view} WHERE SeriesID = ? ORDER BY ? ?";
$result = $conn->getData($query, array($seriesID,$sortBy,$sortOrder));
In both cases, I get no error, and no results.
I think the only way to solve this safely is to use a switch statement before the query to manually validate the acceptable values. However, unless you're only ever dealing with one table, you can't know what the possible values are for the SortBy column.
However, if you just go with the assumption that the values at this point have already been cleaned, you can go with the non-parameterized version like this:
$query = "SELECT * FROM {$this->view} WHERE SeriesID = ? ORDER BY " . $sortBy . " " . $sortOrder;
$result = $conn->getData($query, array($seriesID));
What I plan to do is make sure to validate sortBy and sortOrder before I pass them to the method that contains this code. By doing it this way, each place I call the code becomes responsible for validating the data before sending it. The calling code would know the valid possible values for the table (or view in this case) that it is calling. (I'm the author of both pieces of code in this case, so I know it's safe.)
So, in short, just make sure that the values at this point in the code are already cleaned and safe, and push that responsibility up one level the code that calls this code.

PHP MySQL multiple id insert and set name

I've got this POST form in my website designed to make groups out of an unspecified number users. It works by while looping out all the users in my database with a check box next to their names. You tick the boxes of the users you want, give the group a name then submit the form.
To accomplish the above I need to do two queries due to the way the database has been set up. The table is called Participant/Group and it contains 3 columns: idParticipant, idGroup and GroupName. Here's what I have so far but it has its flaws:
$aParticipants = array_map('mysql_real_escape_string', $_POST['check']);
$pIn = implode("),(", $aParticipants);
$gIn = implode("', '", $aParticipants);
$query1 = mysql_query("INSERT INTO `Participant/Group` (`idParticipant`) VALUES ($pIn);");
$query2 = mysql_query("UPDATE `Participant/Group` SET GroupName = '".$_POST['group']."' WHERE idParticipant IN ('$gIn')");
So the first query inserts the id's of the users into the database and the second query adds the group name to each of those newly inserted id's. If you could combine these two queries I think it could solve all my problems but I'm pretty sure the syntax doesn't allow it.
Anyway the problem with the above code is that it overwrites the database with any subsequent overlapping queries. The important thing to remember here is users are not restricted to one group. A user with an id of 7 can be in a Group called Group A and also be in a group called Group C. There will need to be two separate rows to record this. With my code above it is creating the separate rows but both of them will have the group name of whatever was last submitted in the form.
Anyone know how my code could be tweaked (re-written if you want) to fix this?
Add group name in implode
$aParticipants = array_map('mysql_real_escape_string', $_POST['check']);
$group = mysql_real_escape_string($_POST['group']);
$pIn = implode(", {$group}),(", $aParticipants);
$query1 = mysql_query("INSERT INTO `Participant/Group` (`idParticipant`, `GroupName`) VALUES ($pIn);");
It could be done using only one query. Also, the group field must also be sanitized using mysql_real_escape_string to avoid SQL injection attacks.
$aParticipants = array_map('mysql_real_escape_string', $_POST['check']);
$group = mysql_real_escape_string($_POST['group']);
$sqlvalues = array();
foreach ($aParticipants as $p) {
$sqlvalues[] = "('$p', '$group')";
}
$values = implode(',', $sqlvalues);
mysql_query("INSERT INTO `Participant/Group` (`idParticipant`, GroupName)
VALUES $values");
For the sake of completeness, the best would be to use MySQLi or PDO to take advantage of prepared statements for the sake of performance and security.
$aParticipants = array_map('mysql_real_escape_string', $_POST['check']);
$participants = array();
foreach(aParticipants as $p) {
$participants[] = $p . ', ' . mysql_real_escape_string($_POST['group']);
}
$pIn = implode("),(", $participants);
$query1 = mysql_query("INSERT INTO `Participant/Group` (`idParticipant`, `GroupName`) VALUES ({$pIn})");
EDIT the problem here is that the $iPn string will contain ,( at the end that causes SQL error (which it did not when inserting just one value into one column).
Therefore a line
$pIn = implode("),(", $participants);
has to be replaced by
$pIn = substr(implode("),(", $participants), 0, -3);
Now it should work without any SQL errors...

PHP PDO: Array in Update SQL WHERE IN () clause

I'm trying to take an array of ID numbers and update every row with that ID number. PHP PDO code follows:
private function markAsDelivered($ids) {
$update = $this->dbh->prepare("
UPDATE notifications
SET notified = 1
WHERE notification_id IN (
:ids
)
");
$ids = join(',', $ids);
Logger::log("Marking the following as delivered: " . $ids, $this->dbh);
$update->bindParam(":ids", $ids, PDO::PARAM_STR);
$update->execute();
}
However, when this is run, only the first item in the list is getting updated, although multiple ID numbers are being logged. How do I modify this to update more than one row?
A placeholder can only represent a single, atomic value. The reason it kinda works is because the value mysql sees is of the form '123,456' which it interprets as an integer, but discards the rest of the string once it encounters the non numeric part(the comma).
Instead, do something like
$list = join(',', array_fill(0, count($ids), '?'));
echo $sql = "...where notification_id IN ($list)";
$this->dbh->prepare($sql)->execute(array_values($ids));

How do i select multiple rows which match the value of an item in an array?

I have written a php script that returns an arbitrary number of specific ids (which are in the format of numbers) in an array. I would like to make a new query that selects each row from a table that belongs to each id. I know i can do 1 query to get one row with the matching id. But i would like to do this all in one query. Here is my code:
$id = array(1,4,7,3,11,120); // The array containing the ids
$query = mysql_query("SELECT *
FROM posts
WHERE id = '$id[0]'");
// I would like to Do this for each id in the array and return it as one group of rows.`
I think you want the IN clause:
$idList = implode(",", $id);
SELECT *
FROM posts
WHERE id IN ( $idList );
The implode() function will turn your array of numbers into a comma-separated string of those same values. When you use it as part of an IN clause, it tells the database to use those values as a lookup table to match id against.
Standard Disclaimer/Warning:
As with any SQL query, you really shouldn't be directly concatenating variables into the query string. You're just opening yourself up to SQL injection. Use prepared/parameterized statements instead.
Use PHP's implode function to convert the array into a comma separated value string.
Then, you can use the SQL IN clause to run a single SQL statement containing the values associated with the ids you captured from PHP:
$id = array(1,4,7,3,11,120);
$csv = implode(',', $id);
$query = sprintf("SELECT *
FROM posts
WHERE id IN (%s)",
mysql_real_escape_string($csv));
$result = mysql_query($query)
I omitted the single quotes because they aren't necessary when dealing with numeric values in SQL. If the id values were strings, each would have to be encapsulated inside of single quotes.
What you want is SQL's IN clause.
SELECT * FROM posts WHERE id IN (1, 4, 7, 11, 120)
In PHP, you'll probably want something like:
$query = mysql_query(sprintf("SELECT * FROM posts WHERE id IN (%s)", implode(',', $id)));
Obviously, that's assuming you know you have integer values for $id, and that the values for $id didn't come from the user (that is, they should be sanitized). To be safe, you really ought to do something like:
$ids = implode(',', array_map('mysql_real_escape_string', $id));
$query = mysql_query("SELECT * FROM posts WHERE id IN ($ids)");
And if $id is dynamically generated, don't forget to put something in that IN clause, because SELECT * FROM foo WHERE bar IN () will give you an error. I generally make sure to set my IN-clause variables to 0, since IN (0) is good, and primary keys are pretty much never 0.

Categories