I have this section of a form I need for my customer signup sheet I am creating.
My goal is check if the customer exists, and if they do, output the customerid into an input box. If the customer does not exist, a 6 digit number should be generated in the input box instead.
Here my code:
if(!empty($row['customerid'])) {
echo '<input class="input-xlarge focused" disabled id="focusedInput" name="customerID" value="'.$row['customerid'].'">';
} else {
$six_digit_random_number = mt_rand(100000, 999999);
$sql = "SELECT * FROM customers WHERE customerid='$six_digit_random_number'";
$loop = 0;
while($row = $result->fetch_assoc()){
if($loop == 10) {
echo "CANNOT GENERATE RANDOM NUMBER";
die();
}
$six_digit_random_number = mt_rand(100000, 999999);
$loop++;
}
echo '<input class="input-xlarge focused" disabled id="focusedInput" name="customerID" value="'.$six_digit_random_number.'">';
}
My question is, will the while loop actually stop duplicates from being made, or is there something I am overlooking?
--UPDATE
I switched out my original $six_digit_random_number with a number that already exists in the database, and the while loop did not change the number at all.
So I guess my updated question is, how can I check if the number has been used before?
Then you have to do something like this ( untested )
$sql = "SELECT customerid FROM customers WHERE customerid=:random_number";
$stmt = $DB->prepare($sql);
do{
//generate random number
$six_digit_random_number = mt_rand(100000, 999999);
//search for it
$stmt->excute([':random_number' => $six_digit_random_number]);
if( !$stmt->fetch() ){
//exit loop if it matches.
break;
}
}while(true);
//$six_digit_random_number save user with the unique number
Let me break the logic down for you.
When you create a new user, you need an infinite loop. On each iteration of that loop
Make a random number
Check against existing
Now when fetch returns false that means the number was never saved, so just use break to exit the loop. The last value of $six_digit_random_number is still set. So, after you exit the loop insert the new user with that number.
Next user cannot have the same number because, fetch will return a result, and the loop will continue and generate a new number. And so on, until such time as it creates one not in use.
The main drawback is if you have a lot of users you could make a bunch of queries against the DB, make sure to set that field to be an int and have a Unique index in the schema. That will improve the query performance, as will returning only one field ( that's all you need ) instead of *
Make sense.
-note- I like to use the do{ }while(true); syntax for this as it's less likely to be mistaken as a typo latter. It's just more readable then using while(true){ } other then that the only difference is with a do loop the loop happens before the evaluation so do{ }while(0) will run 1 time where while(0){} will not. In this case that doesn't matter though.
Related
I have a php script that runs every 20 minutes (I use screen on Debian - server side),
The php script selects the first row of the Database using LIMIT 1, like:
$q = mysqli_query($connection,"SELECT * FROM table_name LIMIT 1");
Once selected the first Row it will send the result to a telegram bot, delete the 1st row from the DB and the scripts ends.
It repeats this process every 20 minutes cause the screen.
Now, the question is, if the first row I got with LIMIT 1 doesn't meet the criteria (at the moment it is deleted and skip the actual process until the next 20 minutes), how can I make it to select the 2nd row (which is now the 1st row again) to get the new data using the same script and avoiding wait the next 20 minutes?
Is it possible with my actual script LIMIT 1?
Obviously I don't have access to your database, and I don't know what your criteria are, so this will all be pseudocode. I am assuming you will want to delete the first row if it doesn't match the criteria, but it would be just as possible to only skip it:
$finished = false;
while(!$finished) {
// you already have the code to get the first row. It goes here.
// I will assume those results are in $row
$finished = $row MEETS CRITERIA; // whatever these criteria are
// you also have the code to delete the current row. put that here
// whether this row satisfies the condition or not. The row you
// just selected will always be deleted
}
It's as simple as that. If the row met the criteria, then $finished is TRUE and the while loop will terminate*. If the row didn't meet the criteria, the $finished is FALSE and the while loop will run again. It will run until a row meets the criteria and $finished is TRUE.
* ! is the php NOT operator. It inverts the value. So basically what the while loop is saying is this:
while not finished { do this code }
Also, to avoid a non-terminating loop, you'll need to make sure that your result set has something in it, or else $finished will never be set to TRUE. I'm not sure how you're executing, so I'm not going to suggest an implementation. Just be aware that it is possible for a while to become a neverending loop and take measures to avoid it. For instance, you might:
$finished = false;
$retries = 5;
while(!$finished && $retries-->0) {
//the code
}
This decrements $retries every loop, and once it gets to 0 the while will stop no matter what. It's a good idea to put a failsafe in any loop that might accidentally (or unforeseenedly) become unterminated, especially during development, so you don't hang your code.
EDITED now that I have seen some of the sample code:
$finished = false;
$retries = 5;
while(!$finished && $retries-->0) {
$q = mysqli_query($connection, "SELECT * FROM table LIMIT 1");
$r = mysqli_fetch_assoc($q);
$e = $r['id'];
$asin_queue = $r['asin'];
$price_queue = $r['new_price'];
$r = file_get_contents("https://api.keepa.com/product?asin=$asin_queue&key=$api_keepa");
$gz = gzdecode($r);
$t = json_decode($gz);
$new_price = $t->products[0]->csv;
// put any other stuff you need to do here,
// like updating any db entries, etc
// you also have the code to delete the current row. put that here
// whether this row satisfies the condition or not. The row you
// just selected will always be deleted
$finished = $new_price <= $price_queue;
}
And that's pretty much it. It's your existing code, just wrapped in a while block so that it loops. The only thing I've added is a line to check for a condition inside the while block so that the code knows when to exit.
Also, let's look at this: $retries-->0, because that line may be a little confusing. So I said you needed some kind of failsafe to keep your code from accidentally looping forever if you make a mistake or oversight, and so I assign a variable $retries and make it equal to 5. -- is php shorthand for "subtract one from this", so $retries-- just subtracts one from whatever the value is currently*. >0 is just a value check, even though I wrote it so it all runs together, it's two separate things. It could have been written like this: $retries-- > 0.
In context of the while loop, there's a few things happening:
while( !$finished && $retries-->0 ) {}
and the whole thing has to evaluate to TRUE for the loop to keep running. && is the logical operator AND. ! is the logical operator NOT. So in plain language terms that condition would read:
"while not finished and the number of remaining retries is greater than 0, continue looping."
Hope all this helps.
* -- can go before or after the variable it's modifying. If it goes after ($retries--) then the decrement doesn't take effect immediately. This lets you use the variable in a logical test before the decrement is applied. 5-- > 4 is true. If you put the -- before the variable, the decrement happens first. --5 > 4 would be false, because the subtraction will happen before the comparison.
Thanks for replying back!
I think I can use your suggestion but I need to edit it, the check I need to perform is as follows:
$q = mysqli_query($connection,"SELECT * FROM table LIMIT 1");
$r = mysqli_fetch_assoc($q);
$e = $r['id'];
$asin_queue = $r['asin'];
$price_queue = $r['new_price'];
//perform the check to see if the price has changed
$r = file_get_contents("https://api.keepa.com/product?asin=$asin_queue&key=$api_keepa");
$gz = gzdecode($r);
$t = json_decode($gz);
//price updated
$new_price = $t->products[0]->csv;
//here is where I am stuck
I should do something like this:
while($new_price > $price_queue){
*// It will need to delete the first row on the DB and do the check again, until it get the right condition $new_price <= $price_queue and then it can quit the while loop.
//repeat the above to do the check again*
$q = mysqli_query($connection,"SELECT * FROM table LIMIT 1");
$r = mysqli_fetch_assoc($q);
$e = $r['id'];
$asin_queue = $r['asin'];
$price_queue = $r['new_price'];
//perform the check to see if the price has changed
$r = file_get_contents("https://api.keepa.com/product?asin=$asin_queue&key=$api_keepa");
$gz = gzdecode($r);
$t = json_decode($gz);
//price updated
$new_price = $t->products[0]->csv;
}
I'm having a little problem with the codes given below. When I'm using the name="staff_number[]" then it insert the record with everything ok even if it is already in the database table and when i use name="staff_number" it does check the record and also give me alert box but when insert the record if it is not in the database it stores only the first number of the staff number like the staff no is 12345 it stores only 1. can anyone help in this record i think there is only a minor issue what I'm not able to sort out.
PHP Code:
<select placeholder='Select' style="width:912px;" name="staff_number[]" multiple />
<?php
$query="SELECT * FROM staff";
$resulti=mysql_query($query);
while ($row=mysql_fetch_array($result)) { ?>
<option value="<?php echo $row['staff_no']?>"><?php echo $row['staff_name']?></option>
<?php } ?>
</select>
Mysql Code:
$prtCheck = $_POST['staff_number'];
$resultsa = mysql_query("SELECT * FROM staff where staff_no ='$prtCheck' ");
$num_rows = mysql_num_rows($resultsa);
if ($num_rows > 0) {
echo "<script>alert('Staff No $prtCheck Has Already Been Declared As CDP');</script>";
$msg=urlencode("Selected Staff ".$_POST['st_nona']." Already Been Declared As CDP");
echo'<script>location.href = "cdp_staff.php?msg='.$msg.'";</script>';
}
Insert Query
$st_nonas = $_POST['st_nona'];
$t_result = $_POST['st_date'];
$p_result = $_POST['remarks'];
$arrayResult = explode(',', $t_result[0]);
$prrayResult = explode(',', $p_result[0]); $arrayStnona = $st_nonas;
$countStnona = count($arrayStnona);
for ($i = 0; $i < $countStnona; $i++) {
$_stnona = $arrayStnona[$i];
$_result = $arrayResult[$i];
$_presult = $prrayResult[$i];
mysql_query("INSERT INTO staff(st_no,date,remarks)
VALUES ('".$_stnona."', '".$_result."', '".$_presult."')");
$msg=urlencode("CDP Staff Has Been Added Successfully");
echo'<script>location.href = "cdp_staff.php?msg='.$msg.'";</script>';
}
Your $_POST['staff_number'] is actually an array.
So you have to access it like $_POST['staff_number'][0] here, 0 is a index number.
If the name of select is staff_number[] then $prtCheck will be a array so your check query must be in a loop to make sure your check condition.
if the name is staff_number then the below code is fine.
The answer of amit is right but I will complete it.
Your HTML form give to your PHP an array due to the use of staff_number[] with [] that it seems legit with the "multiple" attribute.
So you have to loop on the given values, you do it with a for and a lot of useless variables without really checking it. From a long time, we have the FOREACH loop structure.
I could help you more if i know what is the 'st_nona', st_date' and 'remarks' values.
According to your question you are getting difficulty in storing the data. This question is related to $_POST array.
Like your question we have selected following ids from the select : 1,2,3,4
It is only storing 1.
This is due to you have not used the loop when inserting the data.
Like below:
<?php
foreach($_POST['staffnumber'] as $staffnumber){
$query=mysql_query("select * from staff where staff_number =".$staffnumber);
if(mysql_num_rows($query)>0){
//action you want to perform
}else{
//action you want to perform like entering records etc. as your wish
}
}
?>
And I would like to suggest you that use the unique keys in database for field and use PHP PDO for database, as it is secure and best for OOPs.
Let me know if you have any queries.
I've got a weird little problem.
I'm writing a script that inserts multiple rows into a MySQL DB, quirk is that their IDs are not necessarily a nice neat 1,2,3 set as they're being edited. The continue statement must be skipping every row as it times out in PHP. This has been doing my head in for the past few hours. Any ideas?
Thanks!
$items = $_POST['invItemQuantity'];
$i = 1;
while($i <= $items) {
if(!isset($_POST['item'.$i])) continue;
//assign posts to variables
$date = $_POST["item_date".$i];
$description = $_POST["description".$i];
$price = $_POST["price".$i];
$ID = $_POST["item".$i];
$que = "UPDATE invoice_items SET date='".$date."', description ='".$description."', price ='".$price."' WHERE item_ID=".$ID;
$test .= $que."<br>";
$i++;
}
if(!isset($_POST['item'.$i])) continue;
You forgot to increment i in that case. Fix it to :
if(!isset($_POST['item'.$i])) { $i++; continue; }
Since you need to iterate over all the item fields no matter what, a for loop might make it easier to not forget your increment action.
$items = $_POST['invItemQuantity'];
for($i=1; $i<=$items; $i++)
{
if(!isset($_POST['item'.$i])) continue;
// ...
}
You might also want to perform some validation on "$_POST['invItemQuantity']" before you use it in your code (e.g. verify it contains a number of expected range).
Your whole approach to this is very strange. I'm guessing in your form you have item1, item2, item3 etc. Instead you should have items[] for all of them to submit it as an array. Do the same for each item_date, description and price. Then simply run:
foreach($_POST['items'] as $i => $item) {
if(!empty($item)) {
$date = mysql_real_escape_string(trim($_POST['item_date'][$i]));
$description = mysql_real_escape_string(trim($_POST['description'][$i]));
$price = mysql_real_escape_string(trim($_POST['price'][$i]));
$ID = (int)$_POST['item'][$i];
//UPDATE QUERY...
}
}
The other thing is you should never take user input and directly input it into the database as that leaves you wide open to SQL injections. You should always escape it first using mysql_real_escape_string (for mysql). Even better would be to learn MySQLi or PDO.
You may also wish to look at filter_input, a good way to make sure that your inputs are clean. You should never trust user input and should always test it against a white list of suitable variables if possible.
I wrote a function which makes a random id makeid(); Just to ensure the id is unique I have a SQL statement which checks if the id already exists.
$does_id_exist = mysql_query("SELECT COUNT(*) AS count FROM signups WHERE affid='$affid'");
if(mysql_num_rows($does_id_exist) == 1)
{
#loop function and perform query again
}
else
{
#insert record
}
So I'm having trouble with looping the function. How do I loop my function makeid() and perform the $does_id_exist check to ensure that each ID is unique.
--UPDATE-- Just to clarify- My code makes an id like YES#281E But before I INSERT this id into the users record. I just need to verify IF any other user already has this id. IF another user has this id that event must trigger my function to create a new id e.g. WOW!29E3 and again check the sql/query to ensure no other user has that id. Continue to loop if fails or end and INSERT if the id is available.
You can either just use a primary key on your database table, or something like this:
<?php
// the id to insert
$newId = null;
// populate with results from a SELECT `aff_id` FROM `table`
$currentIds = array();
// prepopulate
for( $i=0; $i<100000; $i++ )
{
$currentIds[] = "STRING_" + rand();
}
// generate at least one id
do
{
$newId = "STRING_" + rand();
}
// while the id is taken (cached in $currentIds)
while( in_array($newId, $currentIds) );
// when we get here, we have an id that's not taken.
echo $newId;
?>
Output:
STRING_905649971 (run time 95ms);
I'd definitely not recommend running the query repeatedly. Perhaps a final check before you insert, if your traffic volume is high enough.
Do not do COUNT(*), because you do not need to know how many rows is there (it should be 0 or 1 as you need Id unique), so even DB finds your row it will still be checking for the whole table to count. You really care if you got 1 row, so just select for row with that ID and this sufficient. You should also avoid using rand() - this does not help as you see and you cannot predict how many loops you can do before you find "free slot". use something predictable, like date prefix, or prefix incremented each day. anything that would help you narrow the data set. But for now (pseudocode!):
$id = null;
while( $id == null ) {
$newId = 'prefix' . rand();
mysql_query("SELECT `affid` FROM `signups` WHERE `affid`='${newId}'");
if( mysql_num_rows() == 0) {
$id = newId;
break;
}
}
Ensure you got DB indexed, to speed things up.
EDIT: I do agree that any cache would be useful to speed things up (you can add it easily yourself based on #Josh example), still, I think this is fixing at wrong place. If possible rethink the way you generate your ID. It does not really need to be auto increment, but something more predictable than rand() would help you. If your ID does not need to be easily memorable and it is not any security concern to have them sequential, maybe use numbers with other base than 10 (i.e. using 26 would use all digits + letters so you'd end with PREFIX-AX3TK, so string as you want, and at the same time you would easily be able to quickly generate next Id
I am trying to implement a check in my PHP code, that checks if there is a duplicate uid in the database, and if so, to assign a new uid, and check again, but I am having trouble nailing the logic, here is what I have thus far,
function check($uid){
$sql = mysql_query("SELECT * FROM table WHERE uid='$uid'");
$pre = mysql_num_rows($sql);
if($pre >= 1){
return false;
}else{
return true;
}
}
And then using that function I thought of using a while loop to continue looping through until it evaluates to true
$pre_check = check($uid);
while($pre_check == false){
//having trouble figuring out what should go here
}
So basically, once I have a usable uid, write everything to the database, else keep generating new ones and checking them till it finds one that is not already in use.
It is probably really simple, but for some reason I am having trouble with it.
Thanx in advance!
$uid = 100; // pick some other value you want to start with or have stored based on the last successful insert.
while($pre_check == false){
$pre_check = check(++$uid);
}
Of course ths is exactly what 'auto incrementing' primary keys are useful for. Are you aware of 'auto incrementing' primary keys in mysql?
EDIT
In light of your comment regarding maintaining someone else's code that uses the random function like that (ewwwww)... I would use the method I suggest above and store the last inserted id somewhere you can read it again for the next user. This will allow you to "fill-in-the-blanks" for the uids that are missing. So, if for example you have uids 1, 2, 5, 9, 40, 100... you can start with $uid = 1; Your while loop will return once you get to 3. Now you store the 3 and create the new record. Next time, you start with $uid = 3; and so on. Eventually you will have all numbers filled in.
It is also important to realize that you will need to do the inserts by either locking the tables for WRITES. You don't want to get into a race condition where two different users are given the same uid because they are both searching for an available uid at the same time.
Indeed the best is to use autoincrement ids, but if you don't have the choice, you can do a reccursive function like that:
function find_uid() {
$new_uid = rand(1000000000, 9999999999);
$sql = mysql_query("SELECT COUNT(*) AS 'nb' WHERE uid=".$new_uid.";");
$row = mysql_fetch_assoc();
$pre = $row['nb'];
return ($pre >= 1 ? find_uid() : $new_uid);
}
COUNT(*) should be more performant because the count is made by MySQL and not php.
By the way, if you need a new uid shouldn't the condition be ($pre > 0) instead of ($pre > 1) ?