For loop stopping before get to number needed - php

My idea is to make tooltip for new users on website. They will go through the tooltips and each time they complete a tooltip it inserts into DB.
However i've got a button which is skip all. So my idea is to insert all the reminder tooltips which they've not completed into the DB with for loop.
So it works if there has been no tooltips clicked already. So the tooltip would be equal to 1 because coming through the $data is equal to 0. However if the tooltip is equal to 1 when passed through $data it gets a +1 and the for loop doesn't seem to post anything into database
$NumberOfTooltips = 2;
(int)$data->tooltipLastID = ((int)$data->tooltipLastID === 0) ? 1 : (int)$data->tooltipLastID + 1;
for ($x = (int)$data->tooltipLastID; $x <= $NumberOfTooltips; $x++) {
$query = "";
$query = "INSERT INTO tooltips ";
$query .= "(tooltip_id, inserted) VALUES ($x, NOW())";
database::query($query);
$this->id = database::getInsertID();
}
On the broken loop the value of (int)$data->tooltipLastID is 2
Is it because the (int)$data->tooltipLastID is already equal to $NumberOfTooltips?

General improvements
These do not directly solve the question asked but they do give you a helping hand in clarifying your data and steaming out any secondary bugs and bloopers. Also pointing out some best (or at least, better) practise.
$x is a lazy and poorly defined counter. Prefer using descriptve veraibles such as $tooltipCounter
$data->tooltipLastID should not start at 1; use the same syntax as every other integer number system in PHP/programming and start at zero. If you need a one then add +1 only when it's needed (VALUES (".$x+1.")).
$NumberOfTooltips = 2; The number 2 is probably not high enough for adequate testing.
var_dump($data->tooltipLastID) and var_dump($NumberOfTooltips) to check both values are what you expect.
Rewrite the test code to take the variables out of the code so that you can check your Database connction works correctly (such as if you're trying to insert into a string field-type by mistake)
$query = ""; is redundant.
You should not need to type cast (int) your object variables ($data->whatever) all the time but type cast them when they're set.
Also by adding +1 to a variable PHP automatically recasts the variable as an int anyway.
Check that your $data->tooltipLastID is publicly accessible/writable.
You use $this ; so which class are you in? Are you self referencing the data class?
A bank holiday is just one day.
It is better the inserted Database column is set by the database automatically upon insert. You can use this SQL to alter your current table:
ALTER TABLE <yourTable> MODIFY COLUMN inserted timestamp DEFAULT CURRENT_TIMESTAMP

Check the type of $data->tooltipLastID? And plz use var_dump($data->tooltipLastID) and var_dump((int)$data->tooltipLastID) before the for loop to see what indeed the original value and the $x is.
Strange type casts will result in strange bugs...

Related

Mysql INSERT SET type

Sorry for my English but it is not my native language.
I have created a user interface to insert data to MySQL. Everything except one thing is ok but when I want to read data from multiple checkboxes and write them to SET type in MySQL it just doesn't work. I have tried to find the answer but after 4 hours I can't find it or I don't understand it.
http://jyxo.info/uploads/21/21b104df77f6ca723bb708d8d0549af5430e8e91.jpg
dobaVyskytu is SET type and there are in with month you can find mushroom(my tema is online atlas of mushrooms)
in user interfacei have 12 checkbox for 12 month.
http://jyxo.info/uploads/FD/fd548760b155307dfa677ada7c4be4996abf7b93.png
In dobavyskytu i need to have multiple select and that is reason why i use $doba +=
if(isset($_POST["Leden"]))
{
$doba += "Leden";
}
if(isset($_POST["Únor"]))
{
$doba += "Únor";
}
if(isset($_POST["Březen"]))
{
$doba += "Březen";
}
Db::query("INSERT INTO houby(nazev,dobaVyskytu,mistoVyskytu,popis,jedovatost,img)VALUES(?,?,?,?,?,?)",$nazev,$doba,$misto,$popis,$jedovatost,$foto);
Thank you all for reading and for help because it works now.
For strings in PHP, it uses . as concatanation not +, so
$doba .= "Leden";
Edit:
For a better way of doing this, you should try something like...
$options = [];
if(isset($_POST["Leden"]))
{
$options[] = "Leden";
}
if(isset($_POST["Únor"]))
{
$options[] = "Únor";
}
...
$doba = implode(',', $options);
As this will give you something like Leden,Únor
My hypotheses are:
$doba is the variable you want to insert in your SET type column (I translated and it seems the values you put as example in your question is Slovak for "January", "February", "March" -- I suppose there could be more).
I suppose that your SET type column is "dobaVyskytu" and that you created it correctly in MySQL by including all the possible values in the column definition.
(Your question update seem to confirm my hypotheses!)
First, when you want to insert multiple values in a SET type column in MySQL, the string value has to be separated with commas.
With the code I see, you can end up with that string "LedenÚnorBřezen" (I suppose you use += for string concatenation, but you should really use .= like Nigel Ren mentionned). You really want to end up with a string like "Leden,Únor,Březen" if all the 3 values you show are checked in your form.
See here for how to handle SET type in MySQL:
https://dev.mysql.com/doc/refman/5.7/en/set.html
Since you do not know if you will end up with 0 or multiple values for that column, I would suggest to make $doba an array.
$doba = array(); // depending on your PHP version, you can also write $doba = [];
After, you can add your values this way (the syntax $array[] = 'value' will apprend a value to the array):
$doba[] = "Leden";
$doba[] = "Únor";
$doba[] = "Březen";
Then, before inserting it, you can convert the array to a string with the values separated by commas that way:
$csvDoba = implode(',', $doba);
Then use $csvDoba instead of $doba in your Db::query() line.
After you get this working, here are more things you can look for to improve your code:
You can also take advantage PHP magic by naming your form checkbox with a special name to avoid repeating yourself.
For example, you can name all your checkboxes with the name "doba[]", and if (isset($_POST["doba"]), it will already be an array with all the checked values! But beware, if no value is checked, it won't be set. That way, you will avoid doing an if condition for each of your checkbox.
You can do something like this in your code to retrieve the value:
$doba = isset($_POST['doba']) ? (array) $_POST['doba'] : array();
What this do?
If any checkboxes named "doba[]" is checked, then you will retrieve them and make sure the value you retrieve is of type array, the "(array)" part for the value to be an array even if it was not (e.g., an error or someone trying to hack your form). Else you will return an empty array (as no choices has been put).
If you are not familar with this syntax, do a searcch for "ternary operator".
You will of course want to do some validation of your values if not already done
You might look to put the values in another table instead of using the "SET type", but that is up to you and at this stade you probably still have a couple stuff to learn, so I don't want to flood you with too much info. ;-)

Lock a Select on the Postgres Database and updating the Column of "value+1" when necessary

I am updating this question, please do not mind the comments below as, instead of deleting this question, I reworked it to give it a sense.
A form on a php page let me create a csv file, to name this file I need to run a SELECT on the database, it the name does not exists, my query must create it; if the name exist, it must update it.
The problem is, there is a chance where 2 or more users can push the submit button at the same time.
This will cause the query to reurn the same value to all of them, therefore creating or updating the file in a non-controlled way.
I need to create a system, that will LOCK the table for INSERT/UPDATE and, if in the meantime another connection appear, the column on the database that will name the file must be incremented of +1.
$date = date("Ymd");
$csv = fopen("/tmp/$user_$date_$id_$reference.csv", 'w+');
Where "reference" is a progressive number in the format of "Axxxx". x's are numbers.
The SELECT would be:
$sql = pg_query($conn, " SELECT user, identification, reference, FROM orders WHERE identification = '$_POST[id_order]' ORDER BY date DESC LIMIT 1");
while ($row = pg_fetch_row($sql)) {
$user = $row[0];
$id = $row[1];
$reference = $row[2];
}
I need to create a function, like the one below, where users can both INSERT and UPDATE, and in the case of concurrent connection, the ones that are not the first will have "reference" incremented of 1.
CREATE OR REPLACE FUNCTION upsert_identification( in_identification TEXT, in_user TEXT ) RETURNS void as $$
BEGIN
UPDATE table SET identification=in_identification, user=in_user, reference=in_reference WHERE identification = in_identification;
IF FOUND THEN
RETURN;
END IF;
BEGIN
INSERT INTO table ( identification, user, reference ) VALUES (in_identification, in_user, in_reference );
EXCEPTION WHEN OTHERS THEN
-- Should the increment be here?
END;
RETURN;
END;
$$ language plpgsql;
I hope what I'm asking is clear, re-read and I do understand it. Please comment below for any question you might have.
I really hope someone can help me!
I was looking for some clues in the postgres manual, I found this link about locking but I am not so sure this is what I need: LINK

PHP function for auto increment with changed value

I have a requirement where I need to insert user_id in following format
13310_userid_1
13310_userid_2
13310_userid_3
where
13310 = $_GET['userid'] //user id from session
userid = constant //constant text defined
1/2/3 = autoincrement value
Here the variation is when user_id is changed, the auto increment value will be inserted from beginning which would look like
13311_userid_1
13311_userid_2
13311_userid_2
and not
13311_userid_4
13311_userid_5
13311_userid_6
How can I check if the user_id is changed and insert auto increment value from 1 ?
Thanks
I'm not sure it's that you need, but you can use an array to store increment of each cont :
$const = 'userid';
$user_id = '13310';
$array_increment[$user_id] =1;
foreach(array('Franck','Robert','You','Me') as $index=>$test){
if($index==2)$user_id = '13311';
if(!isset($array_increment[$user_id])){
$array_increment[$user_id]=1;
}
$increment = $array_increment[$user_id];
echo $user_id.'_'.$const.'_'.$increment.'<br />';
$array_increment[$user_id]++;
}
Will show :
13310_userid_1
13310_userid_2
13311_userid_1
13311_userid_2
you could store the incremental value of each userid in a temporary session:
For each request check if $_GET['userid'] already exists in a session if not create it with value zero.
Increment by one and use this value to create your string for inserting into DB
//set up session if it is not already set
if(!isset($_SESSION['users'][$_GET['userid'])){
$_SESSION['users'][$_GET['userid']=0;
}
$_SESSION['users'][$_GET['userid']=$_SESSION['users'][$_GET['userid']+1;
$user=$_GET['userid'].'_userid_'.$_SESSION['users'][$_GET['userid'];
//insert $user into your DB or permanent storage.
$db->insert($user);
(this should be a comment but its a bit verbose)
Your only makes sense if a database figures in here somewhere - but you never explicitly stated that this is the case nor which database it is.
Relational database design (and to quite a large extent, non-relational database design) is subject to the rules of normalization. These are methods for describing the structure of your data and prevent you doing stupid things. This breaks the first rule.
Assuming you were designing the system properly, then you would keep the three attributes as seperate fields. But that does not answer the question of whether the id is nominal, cardinal or ordinal (and in the case of ordinal numbers whether there is a requirement for them to be consecutive).

While loop for mysql database with php?

I am developing a mysql database.
I "need" a unique id for each user but it must not auto increment! It is vital it is not auto increment.
So I was thinking of inserting a random number something like mt_rand(5000, 1000000) into my mysql table when a user signs up for my web site to be. This is where I am stuck?!
The id is a unique key on my mysql table specific to each user, as I can not 100% guarantee that inserting mt_rand(5000, 1000000) for the user id will not incoherently clash with another user's id.
Is there a way in which I can use mt_rand(5000, 1000000) and scan the mysql database, and if it returns true that it is unique, then insert it as the user's new ID, upon returning false (somebody already has that id) generate a new id until it becomes unique and then insert it into the mysql database.
I know this is possible I have seen it many times, I have tried with while loops and all sorts, so this place is my last resort.
Thanks
You're better off using this: http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_uuid
Or using this: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
But if you actually want to do what you are saying, you can just do something like:
$x;
do {
$x = random_number();
"SELECT count(*) FROM table WHERE id = $x"
} while (count != 0);
// $x is now a value that's not in the db
You could use a guid. That's what I've seen done when you can't use an auto number.
http://php.net/manual/en/function.com-create-guid.php
Doesn't this function do what you want (without verification): http://www.php.net/manual/en/function.uniqid.php?
I think you need to approach the problem from a different direction, specifically why a sequence of incrementing numbers is not desired.
If it needs to be an 'opaque' identifier, you can do something like start with a simple incrementing number and then add something around it to make it look like it's not, such as three random numbers on the end. You could go further than that and put some generated letters in front (either random or based on some other algorithm, such as the day of the month they first registered, or which server they hit), then do a simple checksuming algorithm to make another letter for the end. Now someone can't easily guess an ID and you have a way of rejecting one sort of ID before it hits the database. You will need to store the additional data around the ID somewhere, too.
If it needs to be a number that is random and unique, then you need to check the database with the generated ID before you tell the new user. This is where you will run into problems of scale as too small a number space and you will get too many collisions before the check lucks upon an unallocated one. If that is likely, then you will need to divide your ID generation into two parts: the first part is going to be used to find all IDs with that prefix, then you can generate a new one that doesn't exist in the set you got from the DB.
Random string generation... letters, numbers, there are 218 340 105 584 896 combinations for 8 chars.
function randr($j = 8){
$string = "";
for($i=0;$i < $j;$i++){
srand((double)microtime()*1234567);
$x = mt_rand(0,2);
switch($x){
case 0:$string.= chr(mt_rand(97,122));break;
case 1:$string.= chr(mt_rand(65,90));break;
case 2:$string.= chr(mt_rand(48,57));break;
}
}
return $string;
}
Loop...
do{
$id = randr();
$sql = mysql_query("SELECT COUNT(0) FROM table WHERE id = '$id'");
$sql = mysql_fetch_array($sql);
$count = $sql[0];
}while($count != 0);
For starters I always prefer to do all the randomization in php.
function gencode(){
$tempid=mt_rand(5000, 1000000);
$check=mysql_fetch_assoc(mysql_query("SELECT FROM users WHERE id =$tempid",$link));
if($check)gencode();
$reg=mysql_query("INSERT INTO users id VALUES ('$tempid')",$link);
//of course u can check for if $reg then insert successfull

get max value in php (instead of mysql)

I have two msyql tables, Badges and Events. I use a join to find all the events and return the badge info for that event (title & description) using the following code:
SELECT COUNT(Badges.badge_ID) AS
badge_count,title,Badges.description
FROM Badges JOIN Events ON
Badges.badge_id=Events.badge_id GROUP
BY title ASC
In addition to the counts, I need to know the value of the event with the most entries. I thought I'd do this in php with the max() function, but I had trouble getting that to work correctly. So, I decided I could get the same result by modifying the above query by using "ORDER BY badgecount DESC LIMIT 1," which returns an array of a single element, whose value is the highest count total of all the events.
While this solution works well for me, I'm curious if it is taking more resources to make 2 calls to the server (b/c I'm now using two queries) instead of working it out in php. If I did do it in php, how could I get the max value of a particular item in an associative array (it would be nice to be able to return the key and the value, if possible)?
EDIT:
OK, it's amazing what a few hours of rest will do for the mind. I opened up my code this morning, and made a simple modification to the code, which worked out for me. I simply created a variable on the count field and, if the new one was greater than the old one, changed it to the new value (see the "if" statement in the following code):
if ( $c > $highestCount ) {
$highestCount = $c; }
This might again lead to a "religious war", but I would go with the two queries version. To me it is cleaner to have data handling in the database as much as possible. In the long run, query caching, etc.. would even out the overhead caused by the extra query.
Anyway, to get the max in PHP, you simply need to iterate over your $results array:
getMax($results) {
if (count($results) == 0) {
return NULL;
}
$max = reset($results);
for($results as $elem) {
if ($max < $elem) { // need to do specific comparison here
$max = $elem;
}
}
return $max;
}

Categories