mysql prepared statement parameters and ordering queries - php

I have a form, the purpose of which is to place the currently displayed record into a category. I am using the following html code via php to do so:
<form name="categoryForm">
<input name="radiobutton" type="radio" value="fakeapproved" />Fake (Approved)<p>
<input name="radiobutton" type="radio" value="fakesuspected" />Fake (Suspected)<p>
<input name="radiobutton" type="radio" value="keyword" />Forbidden Keywords<p>
<input name="radiobutton" type="radio" value="parallelimport" />Parallel Imports
<input name="Submit" type="submit" value="Update" onclick="handleClick(".$pk.");return false"/>
</form>
At the moment, I simply have an AUCTIONS table, with a category column, and this column is set to one of the categories defined in my form.
This approach is not effective for what I need to do with the data, so I am planning to change it to have a separate column for each category, which can be set to either true or false.
What I would like to know, is if it is possible to use the text defined in my form and obtained via my javascript function, in my sql query.
For example, update auctions set $textfromfrom = true
At the moment, I am using the following prepared statement:
if($cmd=="addcat"){
$alterQuery = "UPDATE auctions SET category = ? WHERE article_no= ?";
if ($altRecord = $con->prepare($alterQuery)) {
$altRecord->bind_param("ss", $subcat, $pk);
$altRecord->execute();
$altRecord->close();
echo "true";
} else {
echo "false";
}
}
Is there a way to replace
$alterQuery = "UPDATE auctions SET category = ? WHERE article_no= ?";
with
$alterQuery = "UPDATE auctions SET ? = true WHERE article_no= ?";
Would it also be possible to execute a separate query straight after, i.e.:
if($cmd=="addcat"){
$alterQuery = "UPDATE auctions SET ? = true WHERE article_no= ?";
$insertQuery = "INSERT into users (username, ?) values ?, true";
if ($altRecord = $con->prepare($alterQuery)) {
$altRecord->bind_param("ss", $category, $pk);
$altRecord->execute();
if ($insRecord = $con->prepare($insertQuery)) {
$insRecord->bind_param("ss", $category, $username);
$insRecord->execute();
$insRecord->close();
}
$altRecord->close();
echo "true";
} else {
echo "false";
}
My reasoning for using the above approach is as follows:
The auctions database is imported from another source, and I cannot change the structure at all, except to add categories on to the end. Primary keys and such must not be changed.
There are only 4 categories
An individual auction may belong to more than one category
The auctions table only deals with auctions. I will need a users table, which will consist of primarily new user input.
The users table must be able to show for each users, the categories they have had auctions in.
There must not be more than one record in the users table per user. The username will function as the primary key.

Yikes, classic database denormalization here about to happen. You don't want to do this. You want to keep it either a single column, or, as it would seem to need to be, create a one to many relationship between auctions and type. Your first sign that something is wrong is that there's no easy path to do what you're trying to do.
A better approach would be to create an auction table, and a categoryAuctionForm table...
It would look like:
auctions Table:
auction_id
---------
0
1
2
3
auctions Type:
auction_type_id auction_id auction_type
1 0 something
2 0 othertype
3 0 fake
4 1 othertype
5 2 fake
6 3 fake
You can also add more levels of normalization such as creating an auction_type column.
Just my thought.

$alterQuery = "UPDATE auctions SET ? = true WHERE article_no= ?";
No, you can't use query parameters for column names. Query parameters can be used only in the place of a literal value in an SQL expression.
You'll have to use dynamic SQL. That is, form an SQL statement as a string, and interpolate your application variables into this string as column names.

It is the point of preparing queries that nothing except data is going to change.
You could prepare different queries for each category you have (put them into array indexed by category name).
But I doubt preparing queries would actually make any difference here.

Related

Many to many relationships - Moving data from many tables to a single table

I have a table with users and one with labels
A label can have many users and a user can have many labels, so a Many to Many relationship
A joining table is needed, that's why I have label_user
Below you can see pictures of what they contain with example data:
Users:
https://i.stack.imgur.com/E5E6O.png
Labels:
https://i.stack.imgur.com/1NFjq.png
label_user:
https://i.stack.imgur.com/tW2Uo.png
Let's say I have 5000 users and I can sort them by gender. Let's say 2800 of them are males, how can I assign them all to a label?
Here's some things I tried:
public function add_users_to_label($label_id, $condition, $value)
{
$db = new Database();
$conn = $db->db_connect();
$label_id = escape_string($conn, $label_id);
$query = $conn->query("INSERT INTO `label_user`(`label_id`, `user_id`) SELECT :label_id, psid FROM `iris_messenger_users` WHERE $condition = $value");
$query->bind_param("iss", $label_id, $condition, $value);
if ($query->execute()) {
return true;
}
else {
return "Error inserting data: " . $conn->error . "\n";
}
}
On the user side I have a simple form with select that let's you select a label and then this code:
if(isset($_POST['label-select'])) {
if ($_GET['show_only_gender'] == 'male') {
$condition = 'gender';
$user->add_users_to_label($_POST['label-select'], $condition, $_GET['show_only_gender']);
}
}
Basically, I want to get all users that are male and assign them to a label and put that into label_user with respectively the label_id and the user_id(psid)
Even if this worked I'd still have to do it 2699 times more. What can I do here to optimize and make it to run with 1 query if possible?
I don't think using foreach and running it as much times as there are users is the best option, is it?
Is there any better approach I can take to make this possible?
Although what you are describing does not make sense to have a "label" associated with a person for this specific component, the gender is already on the user table you should be able to get all male based on
select * from user where gender = 'male'
no need to JOIN to a label table on this field. Similarly if you were trying to find people based on a name starting with something... you would not create a label for the name either. Query directly from the table that has that specific component association.
Now, to answer your question, how to insert into the label table for each instance in bulk, you could do something like... I am doing this based on some label ID = 123 as just an example in your labels table that represents gender.
I am doing a LEFT-JOIN in the select so we dont try to add for any user IDs that are already on file do not try to get re-added.
insert into label_user
( label_id,
user_id )
select
123 as label_id,
U.id as user_id
from
users U
left join label_user LU
on U.id = LU.user_id
AND LU.label_id = 123
where
U.gender = 'male'
AND LU.user_id IS NULL
You obviously need to adjust for php.

PHP SQL update checkboxes in db table

I have a update.php page which checks checkbox values and updates the data in the DB.
For example, the sport_table looks like this (primary key is a combination of MemberID-Sport):
MemberID | Sport
John | Football
John | Rugby
John | Cricket
Paul | Football
Paul | Rugby
Mike | Cricket
So what I want to do is get get the value from the checkboxes on my webform and update them as necesary in the db table.
I have tried the following code:
$sport = $_POST["sport"];
for ($i=0; $i < sizeof($sport); $i++) {
$sql = "UPDATE sport_table
SET sport='$sport[$i]', MemberID='$username'";
$result = mysqli_query($conn, $sql);
But receive an errors duplicate keys when checking/unchecking boxes and then clicking update.
Making a few assumptions, this is where you might need to be looking:
First, if using checkboxes, you need to allow for multiple answers in the submission with HTML that looks something like this:
<input type="checkbox" name="sport[]" value="Football">Football</input>
Note the [] in the field name. This ensures PHP will parse the field as an array which can have multiple values.
Second, PHP needs to process the array if it exists and always validate user input. Learn about SQL Injection, Never take content direct from the request and use in an SQL query. It will save you a heap of bother one day.
$validSports=array('Football','Rugby','Cricket','Football');
// $_POST['sport'] will not be set if no boxes are checked
if(isset($_POST['sport']) &&
is_array($_POST['sport'])) {
// Remove previous entries from the table for this member.
$sql = "DELETE FROM sport_table WHERE MemberID='$username'";
$result = mysqli_query($conn, $sql);
foreach($_POST['sport'] as $sport) {
// See how we validate the user input here, there are other ways
if(in_array($sport, $validSports)) {
$sql = "INSERT INTO sport_table
SET sport='$sport[$i]', MemberID='$username'";
$result = mysqli_query($conn, $sql);
}
}
}
Thirdly, I've missed out the example code to keep it simple, but in practice, always check the $result value from mysqli_query and if $result === false then you need to handle the error - probably by writing to a log somewhere.
Finally, check the indexes on your sport_table to make it fast. An index on MemberID would be sufficient for the use cases above although a unique index on (MemberId,sport) may be more appropriate to mitigate against data duplication.

Joining multiple tables using PHP relating to a member id

I'm really hoping that you can clear things up for me regarding JOIN using php as I'm really struggling to come to terms with how it works.
Basically I have 2 tables, one for premium members and another for fans of these members. So I want to get certain information from the "premium" table where a member ($memberid) is a fan.
Premium = id, name, avatar, town, etc, etc
Fans = fan($memberid), fanee(Premium ID)
So I have this code at the moment:
<?php
$get_connections_sql = "SELECT id, name, avatar, town FROM premium LEFT JOIN fans ON fans.fanee=premium.id WHERE fans.fan=$memberid LIMIT 18";
$get_connections_res = mysqli_query($con, $get_connections_sql);
if(mysqli_affected_rows($con)>0){
while ($connections = mysqli_fetch_assoc($get_connections_res)){
$connectionid = $connections['id'];
$connectionname = $connections['name'];
$connectiontown = $connections['town'];
$connectionavatar = $connections['avatar'];
$connections .= "
<div class=\"connectionHolder\"><img class=\"connection\" src=\"uploads/avatars/pro/$connectionavatar\" /></div>
";
}
}else{
$connections = "
<div class=\"noContent\">There are no connections to be shown</div>
";
}
?>
This is returning the else condition so there must be something wrong with my JOIN row - Can anyone please point me in the right direction.
You've said that the column 'id' is ambiguous, this is because you have two tables referenced in your query both with 'id' columns and MySQL does not know which to choose. You can be specific by adding the table name like this:
SELECT premium.id, ...
Additionally, you'll want mysqli_num_rows() instead of mysqli_affected_rows(). Since your query does not affect any rows (like update, delete, insert), instead it's returning a set of rows.
mysql_num_rows() takes the result of a query as its parameter (in your case $get_connections_res) rather than the connection. To make sure that the result is valid first check the value of $get_connections_res before the if... if it's falsey then there's an error in the query. Use mysqli_error() to report the error.

Retrieve and display comments/queries from database

I'm developing in php/sql a web application where users will be able to post items that they'd like to sell ( kinda like ebay ). I want non-members to be able to comment on the items or ask queries about items.
My problem is I want to display each item as well as any comment/query made about that item, in a similar manner as the way Facebook wall works.
I want to "append comments"(if any) to each item. The comments table is linked to the items table via column item_id. And the items table is linked to users table via column user_id. I have left joined users table with items table to display item details, i tried to left join comments table as well so that there are 3 joined tables.
That fails because no comments are displayed and only one item is displayed, despite there being multiple entries in each table. Here is the code i,m using.
$database->query
('
SELECT sale.*, query.*, users.id AS userid, users.username as user
FROM sale
LEFT JOIN users ON sale.user_id = users.id
LEFT JOIN query on sale.id = query.item_id
where category = "$category" ORDER BY sale.id DESC
');
$show = " "; //variable to hold items and comments
if ($database->count() == 0) {
// Show this message if there are no items
$show .= "<li class='noitems'>There are currently no items to display.</li>" ;
} else {
$show .= "<li>";
while ( $items = $database->statement->fetch(PDO::FETCH_ASSOC) )
{
$show .= "
//show item details in html
";
while( $query = $database->statement->fetch(PDO::FETCH_ASSOC) )
{
$show .= "
//show queries below item details
";
}
$show .= "</li>" ;
}
Welcome to Stackoverflow!
I recommend you taking a look at pdo. If you are already using mysql_ functions, then I recommend you switch. More on that can be found here.
Now that your pointed to the direction of to what functions to use when connecting/running queries, you now should create your tables. I use phpmyadmin for managing my database, I find it very good, but it's up to you what you use. Once you've decided on the service you use to manage your database, you should then learn how to use it by doing some google searches.
Now you need to set up your table and structure it correctly. If you say you're having items, then you should make a table called items. Next create the columns to the properties of the items. Also I recommend reading about Database Normalization, which is a key aspect of setting up your SQL tables Etc.
Once you have everything set up, you've connected to your database successfully Etc. You now need to set up the "Dynamic Page". What I mean by this is, there's only one page, say called 'dynamic', then a variable is passed to the url. These are called GET HTTP requests. Here's an example of what one would look like: http://example.com/item?id=345.
If you've noticed, you'll see the ? then the id variable defined to 345. You can GRAB this variable from the url by accessing the built in PHP array called $_GET[]. You can then type in your variable name you want to fetch into the []'s. Here's an example.
<?php
$data = $_GET['varname']; // get varname from the url
if(isnumeric($data)){ // is it totally made out of numbers?
$query = "SELECT fieldname FROM table WHERE id=:paramname";
$statement = $pdo->prepare($query); // prepare's the query
$statement->execute(array(
'paramname'=>$data // binds the parameter 'paramname' to the $data variable.
));
$result = $statement->fetch(PDO::FETCH_ASSOC); // grabs the result of the query
$result = $result['fieldname'];
echo 'varname was found in the database with the id equal to:'.$data.', and the result being: '.$result;
}
?>
So now you can see how you can grab the variable from the url and dynamically change the content with-in the page from that!
Hope this helped :)

How can I include (user selected - dynamic) columns in select statement and where clause?

I would like the users to choose which fields they want to see and which they do not want to see.
Table: Companies(cid, cname, state, project_manager, site_supervisor, elec_engg, mech_engg, hydraulics, .....)
Note: All the columns from project_manager to the last column have the value 'Yes/No'
Lets say the user wants to find the companies that have Project managers and electrical engineers in NSW.
The Query will be:
Select cid, cname, project_manager, elec_engg
from companies
where state='NSW'
AND project_manager='Yes'
AND elec_engg ='Yes';
I was wondering how can I make this search dynamic. Displaying all job titles in a HTML form and having check boxes next to each job title and with search button. Something like below.
Query:
select cid, cname, (dynamic user input of columns)
from companies
where state="NSW"
AND Dynamic input column1 ='Yes'
and Dynamic input column2 ='Yes'
AND Dynamic input column3 ='Yes'.....
AND Dynamic input columnn ='Yes';
I'm assuming you're just ANDing all the parameters together. If you can set the name for each HTML the elements in the form exactly what you expect for the column name this should work. When you get the post back (assuming search submits the form as a post), you can loop through the post values. I'd create an array of valid columns to check against as a whitelist to avoid ever having a broken query.
Here's an example using oldschool mysql escaping or you to get the idea, but I'd really use PDO with prepared statements to populate the values of the query. The key is to protect both your where parameters and values when dynamically creating SQL. Shoot me a message if you're using PDO and would like to see that, but this will give you a place to start:
$sql = 'SELECT cid, cname, project_manager, elec_engg FROM companies ';
$whereClause = null;
//whitelist of valid where clause parameters
$validParams = array('site_supervisor', 'elec_engg', 'mech_engg', 'hydraulics');
foreach($_POST as $key=>$value){
if(array_search($key, $validParams)!==false){ //make sure you use !== and not !=
if(empty($whereClause)){
$whereClause = " WHERE ";
}else{
$whereClause .= " AND ";
}
//IMPORTANT: use whatever you're db needs to escape things or use prepare
//statement replacement
$whereClause .= "$key='".mysql_escape_string($value)."' ";
}
}
$sql = $sql.$whereClause;
echo $sql;
You can also add a loop check for just values of 'Yes' and exclude 'No' values...
As noted below in a comment mysql_escape_string($value) is bad... no doubt, to do this correctly and safely with prepared statements and pdo you would change the code to:
$sql = 'SELECT cid, cname, project_manager, elec_engg FROM companies ';
$whereClause = null;
//whitelist of valid where clause parameters
$validParams = array('site_supervisor', 'elec_engg', 'mech_engg', 'hydraulics');
foreach($_POST as $key=>$value){
if(array_search($key, $validParams)!==false){ //make sure you use !== and not !=
if(empty($whereClause)){
$whereClause = " WHERE ";
}else{
$whereClause .= " AND ";
}
//IMPORTANT: use whatever you're db needs to escape things or use prepare
//statement replacement
$whereClause .= " $key=:$key ";
}
}
$sql = $sql.$whereClause;
$db = new PDO($someDsnString);
$statement = $db->prepare($sql);
foreach($_POST as $key=>$value){
if(array_search($key, $validParams)!==false){ //make sure you use !== and not !=
$statement->bindValue(":$key", $_POST[$key]);
}
}
$statement->execute();
$result = $statement->fetchAll();
Both the prepared statements on the values and the whitelist on the parameter values will make this query safe.
just put the result of each field into a string, so something like
$builderworks = $POST_['builderworks'];
$hydrolics = $POST_['hydrolics'];
then just select from the database using all the string values, just make sure you sanitise the POST data above first using mysql_real_escape_string.
so something like
SELECT jobname FROM jobtable WHERE hydrolics='$hydrolics' AND $builderworks='$builderworks'
ect ect
so basically $builderworks etc is whatever has been POSTED in that field, it is stored inside everytime someone submits, so if fo example I went to the form, selected the field builderworks to yes, $builderworks would then equal yes when I hit submit, whatever is stored in each string is then compared using the database query then so if $builderworks has yes inside it, selecting all fields that are equal to $builderworks would select all fields equal to yes, or equal to no if I had selected no in the form.
Don't do this on your database layer. It's a really, really bad idea for security, stability and maintainability all at once. Separate your concerns. Your data access layer should not be dependent on your user's permissions and should definitely not be dependent on your view.
Extract your data from your database as you normally would (into a "model" or some other structure) and then selectively show the data of that structure in your rendering layer as needed (the "view").

Categories