PHP SQL update checkboxes in db table - php

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.

Related

Select mysql column (which is array) where other column is defined,:- and insert it into php variable

I have a two columns in database:- copy (which is array) and bookid. My code is;
$query = mysqli_query($db, "select copy from book_outward WHERE bookid like 'B1'");
while ($row = mysqli_fetch_array($query)) {
$copyid = $row['copy'];
}
Database shows like this
+----------------+
| id copy bookid |
+----------------+
| 1 1 B1 |
| 2 2,3 B1 |
| 3 4 B1 |
| 4 2 B2 |
+----------------+
but it stores only last values which was entered in 'B1'. I also tried
$copyid[] = $row['copy'];
but in this case I have to change array keys manually every time.
My aim is to insert copy into column bookid='B1' and before it has to make sure that only UNIQUE values can be stored in database for B1.
HTML :-
<input type="text" name="bookid" />
<input type="text" name="copies[]" />
PHP code for inserting:-
$book_id = $_POST['bookid'];
$copies = implode(',',$_POST['copies']);
$result = mysqli_query($db, "insert into book_outward(bookid,copy) values ('$book_id','$copies')");
As some of the comments mention it it not the best way to to it but it is possible.
You can obtain all the copyid data by:
$query = mysqli_query($db, "select copy from book_outward WHERE bookid like 'B1'");
$copyid = "";
while ($row = mysqli_fetch_array($query)) {
$copyid .= $row['copy'] . ",";
}
$copyidsFromDB = explode(",",rtrim($copyid , ','));
After that you can check if what you got in the request are in there using array_intersect:
$copies = $_POST['copies']
// if not an array use: $copies = explode(",", $_POST['copies'])
if (count(array_intersect($copies, $copyidsFromDB) == 0)
// insert to DB
Solved by myself by adding one line only
`$copies2 = explode(",",rtrim($copies , ','));`
before array_intersect Thanks code helps greatly.
So, sounds to me like the issue is the original data DB schema has some issues.
A sql-like DB is not a great place to put 'array' style value. It already has a mechanism for doing this: a link table.
That'd save you the trouble of having to manually shuffle around your primary keys in this table. It's way easier to check and validate.
If you HAVE to do this via php code, for example you don't have SQL access, like in a controlled build environment, you should take advantage of 'string keys' to on your $copyid, (hint rename to $copyidmap) to group together like pieces of data and preserve key relationships.
so the following would be unique:
map->{bookId}->{copyid}->{id} // Where id is that primary table id. Obviously, validate and do your undefined array assignments.

Prevent duplicate records from submitting; PHP & MySQL

Good day everyone, I'm trying to prevent duplicate records from being injected into a database of mine, I'm using the following code to prevent the data:
$con=mysqli_connect("localhost","root","","dbPos");
$first=trim($_POST['first']);
$last=trim($_POST['last']);
$emailaddress=trim($_POST['emailaddress']);
$queryF = "SELECT firstname FROM tbData WHERE firstname=='$first'";
$queryL = "SELECT lastname FROM tbData WHERE lastname=='$last";
$queryE = "SELECT emailaddress FROM tbData WHERE emailaddress=='$emailaddress'";
$resultF = mysqli_query($queryF);
$resultL = mysqli_query($queryL);
$resultE = mysqli_query($queryE);
if(mysqli_num_rows($con,$resultF) > 0 && mysqli_num_rows($con,$resultL) > 0 && mysqli_num_rows($con,$resultE) > 0)
{
header("Location: https://www.youtube.com"); //Just to check if it ignores the input.
}else{
mysqli_query($con,"INSERT INTO tbData VALUES (
'$first',
'$last',
'$emailaddress')");
header("Location: HOME.php");}
mysqli_close($con);
So I need it to check the first, and last name and the email address if there's a duplicate of it on the database, before it goes to the part of the code where it injects the data. Unfortunately, the code block where I check if there's a duplicate returns '0', so it proceeds on injecting the code in.
*Basically, the App should ignore the data inputted if it has the same first name, last name, and email address.
Any help would be highly appreciated!
You shouldn't be doing 3 separate queries, since the found values might be in different rows. If someone enters John Jones, you would match John Smith and Fred Jones, so it would think there's a duplicate when there isn't.
You should do a single query for all 3 columns:
SELECT 1 FROM tbData
WHERE firstname = '$first' AND lastname = '$last' AND emailaddress = '$emailaddress'
As mentioned in the comments, you should also learn how to use prepared queries, or at least use mysqli_real_escape_string() to sanitize user input.
You're also calling mysqli_num_rows() wrong. It should just be:
mysqli_num_rows($result)
without the $con argument.
Used the following logic with code below:
$resultFLE = mysqli_query($con,"SELECT * FROM tbData WHERE firstname='$first' AND lastname='$last' AND emailaddress='$emailaddress'");
$countFLE = mysqli_num_rows($resultFLE);
if($countFLE != null ){
header("Location: HOME.php");
}else{
mysqli_query($con,"INSERT INTO tbData VALUES (
...
header("Location: HOME.php");
}
I tried to simplified things, and think of it again; tried to locate affected row first by comparing it to the inputted data one by one, counted it just to see if it hits my counter, and then routed the page to wherever I want to based on the result.

checkbox inputs and DB search

I'm tagging Movies and store this into a Database.
a tag_id could be a car, train, boat and details could be color or specific types.
DB
movie_id | tag_id | tag_detailsid
2612 | 75 | 1
2612 | 10 | 3
2612 | 12 | 2
The tags are submitted via a form with checkboxes (all checkboxes checked are added to the db)
Now..
How do I keep track with checkboxes I uncheck..at a later stage
So looking at the above example.. for movie_id 2612, I uncheck tag 12 with id 2 as details.
So $_POST holds only 75-1 and 10-3....12-2 should be deleted from dB.
So I thought.. I simply go through the dB with a while loop and compare the db value with the values I get from the checkboxes (via the $_Post method)..
I count the values in the $_Post and it shows 2 (checkboxes checked).
Database however holds 3 entries ($numberofrecords) for that specific Movie.
My script so far...
$sql_query = "Select tag_id, tag_details_id FROM movie_tags WHERE movie_id = '2612";
$report = MYSQL_QUERY($sql_query);
$numberofrecords = mysql_num_rows ($report);
while ($report_row = mysql_fetch_array($report))
{
$thistag_id = $report_row['tag_id'];
$tag_details_id = $report_row['tag_details_id'];
foreach($_POST as $checkbox_id => $value)
{
if ($thistag_id == $checkbox_id) // check if DB tag equals checkbox value
{
echo "checkbox value is checked equals value in DB";
}
}
}
How do I track the record I need to delete from the database?
Sometimes the easiest solution is the way to go.
Unless you have a compelling reason NOT to, you can simply delete all of the tag records before saving, then insert only those that were checked:
sample code using OP's selected db driver
$sql = 'DELETE FROM movie_tags WHERE movie_id=2612';
mysql_query($sql);
foreach($_POST as $checkbox_id => $value) {
// save your records here
// To show you the code I'd need to see the HTML for the checkboxes.
}
NOTE: You should not be using mysql_. Use PDO / mysqli instead: Why shouldn't I use mysql_* functions in PHP?
A simple solution would be to delete every rows based on your primary key such as movie_id in your case and do insert in loop for every checked checkboxes. That is:
Delete rows based on movie_id
Loop through POST data and do insert

track selecting and unselecting an option When updating a table

I have three tables (I simplified it in this question) :
Table 1
id | Name
-----------
1 | John
2 | Smith
Table 2
id | Title
-----------
1 | Developer
2 | Web Developer
3 | A New Title
Table 3 (links between 1 and 2)
idName | idTitle
-----------
1 | 1
1 | 2
2 | 1
My problem is with the update page. My HTML is <select multiple> ... options ... </select> and I am using chosen to select and deselect options. I am trying to do this with PHP only.
Lets say that we have the following scenario:
The administrator wanted to remove the 'Developer' Title for 'John' and add 'New Title' for 'John'.
He/She will deselect Title id 1 and select id 3. When He/She submits the form I will get two select values: id 1 (the one that he selected) and id 2 (the one that was already there). But I will not get id 3 because he deselected it.
What I am struggling with is : when the user posted the new selected values the ones that were deselected were not submitted with the form. There is no way for me to track the ones that were deselected so I can delete them from my table. How do you update your table with the new changes then? Do you delete what is already existed in that table and add the ids again? Is there a better option?
UPDATE :
It seems #wander answer is less destructive than the other ones. However, #wonder did not explain how to compute step 4 in his answer. to distinguesh between new selected options, already existing selected options, and deselected options.
There's no need to submit the deselected one. e.g.
John has two titles(Developer and Web Developer) saved in database.
The administrator opens the page, selects 'New Title', deselects 'Developer' and clicks submit.
Now on server side, we can get
the title list of John stored in database: Developer and Web Developer
title list submitted from the users: Web Developer and New Title
We compare the two title lists and figure out: we shall delete Developer title and add New Title on John.
You could add hidden fields before the select menu which will cause 0/falsey values to also be sent to PHP.
<input type="hidden" name="stuff[]" value="0" />
<input type="hidden" name="stuff[]" value="0" />
<input type="hidden" name="stuff[]" value="0" />
<select multiple name="stuff[]">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
This is similar to how you would send unchecked check boxes to the server:
https://stackoverflow.com/a/1992745/268074 (note the answer without JS)
You can dump your old values in a hidden select, so you'll get them when handling your form.
<!-- Invisible select field -->
<select multiple name="oldData[]" style="display:none;" aria-hidden="true">
<option value="1">1</option>
<option value="2" selected>2</option>
<option value="3" selected>3</option>
</select>
<!-- Real select field -->
<select multiple name="data[]">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
Note: I was inspired by #Petah's answer, don't forget to upvote him :-).
Let's say we have $old_titles list with titles from db, and $new_titles titles from submit.
$add = array_diff($new_titles, $old_titles);
$delete = array_diff($old_titles, $new_titles);
$add - list of titles to add, $delete - list of titles to delete.
Add action:
$dbh->prepare('INSERT INTO table3(idName, idTitle) VALUES (:idName, :idTitle)');
foreach($add as $titleId) {
$dbh->execute(['idName' => $userId, 'idTitle' => $titleId]);
}
Delete action:
$dbh->prepare('DELETE FROM table3 WHERE idName = :idName AND idTitle = :idTitle');
foreach($delete as $titleId) {
$dbh->execute(['idName' => $userId, 'idTitle' => $titleId]);
}
Not sure why everybody keeps using hidden input fields to track the existing data. When you POST your form, you can just retrieve the original values from the database. That's a lot safer then depending on user input, because even hidden fields can be altered. I know it won't matter too much in this particular situation, but I think it's best to always use the best practise.
Now in PHP you have two options. The first would be to delete all relations and then add the new relations. The second would be to delete all that wasn't posted and add/update the relations that where.
Although the second options might seem to be the "least destructive" as you call it, it is the easiest and least error prone way. The only reason I would abandon this tactic is when I would store extra data in the relations table. EG: date added (when did somebody get into a function) or added by user.
How would you go about it (method 2)?
Keep in mind that I use a method that tries to continue whenever possible. Another approach would be to halt when false data is posted.
<?php
if (array_key_exists("relations",$_POST) && is_array($_POST["relations"])) {
//no duplicates, you dont want to store the same function twice
//only integers
$list = array_unique(array_filter($_POST["relations"], "is_int"));
$csv = implode(',', $list);
//get the userid hoewever you normally would
$userId = 1
//Delete existing
$query = "DELETE FROM Table3 WHERE idname=".$userId
//insert all posted functions
//I select the id's from the actual table so it will never inserted a none existing ID.
//this approach never has duplicates, so the duplicate check in the beginning is redundant now, but I leave it incase I change this.
$query = "INSERT INTO Table3 (idName, idTitle) SELECT ".$userId.", Id FROM Table2 WHERE id IN (".$csv.")".
//execute queries
} else {
//nothing posted, delete all relations
}
?>
Now if you only want to insert new data and remove none-existing
<?php
if (array_key_exists("relations",$_POST) && is_array($_POST["relations"])) {
//no duplicates, you dont want to store the same function twice
//only integers
$list = array_unique(array_filter($_POST["relations"], "is_int"));
$csv = implode(',', $list);
//get the userid hoewever you normally would
$userId = 1
//Delete existing
$query = "DELETE FROM Table3 WHERE idname=".$userId." AND idTitle NOT IN (".$csv.")"
//only insert new functions
//I select the id's from the actual table so it will never inserted a none existing ID.
//this approach never has duplicates, so the duplicate check in the beginning is redundant now, but I leave it incase I change this.
$query = "INSERT INTO Table3 (idName, idTitle) SELECT ".$userId.", Id FROM Table2 WHERE id IN (".$csv.") AND id NOT IN (SELECT idTitle FROM table3 WHERE idName=".$userId.")".
//execute queries
} else {
//nothing posted, delete all relations
}
?>
The only better way is the simplest one, remember KIS
on update you just need to do following
DELETE FROM table3 WHERE idName=??
and then insert all selected idTitles again in table3
This can be done in two SQL statement without knowing which value has been de-selected. Following these 2 steps
1) In your PHP, create a comma separated list of all selected option. For example: 2, 3.
2) Then execute the following statements:
DELETE FROM tblLink WHERE idName = $id AND idTitle NOT IN ($list);
INSERT IGNORE tblLink (idName, idTitle)
SELECT $id, id FROM tblTitle WHERE id IN ($list);
Note: for shake of simplification, I use $id and $list in my example. When you intent to use it, you should properly prepare and bind parameter properly. Make sure that idName and idTitle are primary key to make it work.
To my opinion you have to check what actions should be performed first. This is a small example that you can run here http://sandbox.onlinephpfunctions.com/code/0fa11b3c9f846e344c912f5a3e994eea5e9cac34.
First run a select on Table 3 for idName=1 the output would be an array of idTitle's (1,2). In the following example the $array1 is the output of such a select statement and $array2 is the array of new idTitle's for the same idName that will have to be updated or deleted or inserted. If for example the new idTitles for a user are more or less than the previous ones then there you should have some sort of control over it in order to decide what queries you will run to the database.
$array1 = array(1,7,3,5);// initial idTitles try it on the above link (php sandbox) with more or less values
$array2 = array(1,4,3,6);//new idTitles try it on the above link (php sandbox) with more or less values
$count1=count($array1);
$count2=count($array2);
$diff=$count1-$count2;
if ($diff==0) {
$result1=array_values(array_diff_assoc($array2,$array1));
$result2=array_values(array_diff_assoc($array1,$array2));
$countr=count($result1);// or $result2 it is the same since they have the same length
$cr=0;
while($cr < $countr) {
print_r("update tblname set val=$result1[$cr] where id=theid and val=$result2[$cr]\n");
$cr++;
}
}
if ($diff!=0) {
$result1=array_diff($array2,$array1);
$result2=array_diff($array1,$array2);
if(count($result2)>0) {
foreach($result2 as $r2) {
print_r("delete from tblname where id=theid and val=$r2\n");
}
}
foreach($result1 as $r1) {
print_r("insert into tblname where id=theid and val=$r1\n");
}
}
How I would do it would extend the 1st table
id | Name | Title IDs
1 | John | 1,2
2 | Smith | 1,3
Each time the row is updated I would simply just replace the title_ids fields and be done with it.

mysql prepared statement parameters and ordering queries

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.

Categories