I need to generate an invoice number from an integer of a table with an auto incrementing ID of the database where the user purchases saved.
Example of the table invoice database:
The invoice number format floor do one of two ways.
Example 1: of the number of invoices without prefix:
0000001 |
0000002 |
0000003 |
0000004 |
0000005
Example 2: the number of invoices with prefixes:
F-0000001 |
F-0000002 |
F-0000003 |
F-0000004 |
F-0000005
Question:
1) ¿What is the best way to do this, you can do directly from MySQL or PHP?
2) ¿What is the most appropriate format Example 1 or Example 2?
I appreciate your support as always!
Thanks to Gordon Linoff, I could get a way to solve this.
I will share an example, perhaps someone may be interested.
SQL - Invoice without prefix: SELECT id, LPAD(id,7,'0') FROM invoice WHERE id = 1;
Result: 0000001
SQL - Invoice with prefix: SELECT id, CONCAT( 'F-', LPAD(id,7,'0') ) FROM invoice;
Result: F-0000001
You can write a good helper function in PHP to use it wherever you want in your application to return an invoice number. The following helper function can simplify your process.
function invoice_num ($input, $pad_len = 7, $prefix = null) {
if ($pad_len <= strlen($input))
trigger_error('<strong>$pad_len</strong> cannot be less than or equal to the length of <strong>$input</strong> to generate invoice number', E_USER_ERROR);
if (is_string($prefix))
return sprintf("%s%s", $prefix, str_pad($input, $pad_len, "0", STR_PAD_LEFT));
return str_pad($input, $pad_len, "0", STR_PAD_LEFT);
}
// Returns input with 7 zeros padded on the left
echo invoice_num(1); // Output: 0000001
// Returns input with 10 zeros padded
echo invoice_num(1, 10); // Output: 0000000001
// Returns input with prefixed F- along with 7 zeros padded
echo invoice_num(1, 7, "F-"); // Output: F-0000001
// Returns input with prefixed F- along with 10 zeros padded
echo invoice_num(1, 10, "F-"); // Output: F-0000000001
Once you are done writing the helper function, you don't need to use LPAD or CONCAT MySQL functions every time in your query to return ID with padding zeros or zeros with prefix. If you have global access to the helper function in the entire application, you only need to invoke it wherever you want to generate an invoice number.
1 - 0000001 | 0000002 | 0000003 | 0000004 | 0000005
$dbValue = 1;
echo $dbValue = str_pad($dbValue, 7, "0", STR_PAD_LEFT); // it will give 0000001;
2 - F-0000001 | F-0000002 | F-0000003 | F-0000004 | F-0000005
$dbValue = 1;
echo $dbValue = "F-".str_pad($dbValue, 7, "0", STR_PAD_LEFT); // it will produce F-0000001;
Fetch last ID from database and store it in a PHP variable.
For example, if last record is 100, then increment it by 1.
$last = 100; // This is fetched from database
$last++;
$invoice_number = sprintf('%07d', $last);
Finally, the answer for second question is,
$number = "F-". $number;
Ans 1):
You can do this with PHP(directly by concat or use str-pad ) as well as with MySQL( LPAD ) also
But as per my view you should do this by PHP, so that you can change it according to your requirements e.g. extend zeroes as per number of id's in DB.So that not to change SQL queries and make it heavy.
Ans 2):
You can use both formats but if you want to be more specific about particular user or any thing else, then use second format.
I think second format can give you more information about data
Related
I generate an unique security code with this every time user login:
$code = substr(str_shuffle(str_repeat("0123456789", 4)), 0, 4);
it seems works but sometimes it generate 3 number instead of 4. also this problem occurred with rand() in past, then i decide to use str_shuffle + str_repeat.
also i insert this code in db with integer data type and length is 6.
what did i wrong or missed?
or is it a bug?
While I can't immediately say why your code sometimes returns only 3 digits, I find myself wondering why you don't create this 4-digit (call it a PIN?) code through the more numerically appropriate rand? For example, since you are going for a 4-digit PIN (between 0000 and 9999), I might write it like:
$code = rand(0, 9999);
$code = substr("000$code", -4);
That is much clearer as to its purpose (generate a random number, guarantee it's 4 digits), and less esoteric than str_repeat/str_shuffle.
EDIT (after learning $code is inserted into an integer DB field)
Why is your random string of 4 digits sometimes turning into 3 digits? Because you are inserting the value into an integer column. Either the DB or the DB Driver will attempt the moral equivalent of:
$code_to_insert = (int)$code;
at which point, if the number is less than 1000, you would get three digits.
Further, if you run your code enough times as it currently stands, you should get PIN lengths of 2 and 1 as well:
0 - 9 = ( 10 / 10000) -> 0.1% of the time
10 - 99 = ( 90 / 10000) -> 0.9% of the time
100 - 999 = ( 900 / 10000) -> 9.0% of the time
1000 - 9999 = (9000 / 10000) -> 90.0% of the time
A possible fix, given the current setup of your code and DB, might be to ensure the PIN length when you pull it out of the DB. You could use the same trick as above:
$sql = "SELECT code FROM ...";
...
$code = $row['code'];
$code = substr("000$code", -4);
Since you're storing the result in an integer field, it's not being stored as separate digits, just as a number. So it doesn't know anything about leading zeroes.
When you later retrieve the value, you can convert it to a string with leading zeroes using the str_pad function:
$code = str_pad($num, 4, '0', STR_PAD_LEFT);
The other option would be to change the datatype in the database to CHAR(4) instead of INT.
Try this:
$code = str_pad($num, 4, '0', STR_PAD_LEFT);
I am working in php and I am trying to create 1000 tickets in a database. Each ticket needs it's own unique code that consists of letters and numbers about 6 characters long.
EXP.
Tbl_Tickets
ID code
1 3F2jk7
2 2HGUF1
3 9FJDNJ
4 MFJEY9
5 23988D
I was wondering is there a simple way of doing this with php, or excel, or any other way for that matter. I know that i can use a random number generator, but the check for the Unique would have a large BigO notation and the check would get messy.
Unique is not compatible with random, but the following might suit:
=CHOOSE(RANDBETWEEN(1,2),RANDBETWEEN(0,9),CHAR(RANDBETWEEN(65,90)))
copied across to populate six columns (say A to F) with, in G:
=A1&B1&C1&D1&E1&F1
and both copied down to say row 1100. Then select G, copy Paste Special Values, and Remove Duplicates on ColumnG and select first 1000 entries.
You could easily create an array of strings in php and write it to a database:
function generateRandomString($length = 6, $letters = '1234567890QWERTYUIOPASDFGHJKLZXCVBNM'){
$s = '';
$lettersLength = strlen($letters)-1;
for($i = 0 ; $i < $length ; $i++){
$s .= $letters[rand(0,$lettersLength)];
}
return $s;
}
// Create an array with random strings
for ($i=0; $i<1000; $i++){
$ticket_numbers = array();
$ticket_number = generateRandomString();
while (in_array($ticket_number,$ticket_numbers))
$ticket_number = generateRandomString();
$ticket_numbers[] = $ticket_number;
}
// Write the array to a database
$con = mysqli_connect("myhost","myuser","mypassw","mybd") or die("Error");
foreach ($ticket_numbers as $number){
mysqli_query($con,"Your insert query using the value $number");
}
mysqli_close($con);
This should help you in the right direction though there are probably better ways to do this.
The function generateRandomString() was taken from How to generate random numbers/letters with PHP/Javascript
And another option. Encryption is guaranteed to be unique, so encrypting the numbers 0, 1, 2, ... will give you guaranteed unique random-seeming output. Six characters is 30 bits using Base32, or 36 bits using Base64. You will need a 30 (or 36 bit) cypher. Unless you have a library that includes Hasty Pudding cypher (unlikely) then just implement a simple four round Feistel cypher with the appropriate block size. It will not be completely secure, but it will be enough to defeat casual attacks.
This will produce random strings in column B with no repeats from B1 thru B1001
Sub Lottery()
Dim i As Long, j As Long, c As Collection
Set c = New Collection
v = Split("0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z", ",")
For i = 1 To 5000
can = ""
For j = 1 To 6
can = can & v(Application.RandBetween(0, 35))
Next j
On Error Resume Next
c.Add can, CStr(can)
On Error GoTo 0
If c.Count = 1000 Then Exit For
Next i
For i = 1 To 1000
Cells(i + 1, 2).Value = c(i)
Next i
End Sub
I need to generate a ten digits product code (for bar code generation) for a fashion store, which is a combination of:
collectionYear (this or next year), two digits, e.g. 14 or 15
collectionSeason (spring to winter) one digit, e.g. 1 for spring
productId (from 1 to 99.999 with zeros filled), e.g. 12 -> 00012
productVariant (from 1 to 99 with zero filled), e.g. 2 -> 02
The result should be something like this: 1510001202
Using php I would do this like this:
$year = str_pad($year, 2 ,'0', STR_PAD_LEFT); // for future safety, e.g. 2102 :-)
$prodId = str_pad($prodId, 5 ,'0', STR_PAD_LEFT);
$prodVar = str_pad($prodVar, 2 ,'0', STR_PAD_LEFT);
$finalCode = $year.$season.$prodId.$prodVar;
Is there a mysql syntax equiv for this like
SELECT CONCAT(collectionYear,collectionSeason,productIdproductVariant) AS prodCode
with the zero fill functions included ??
Thanks for your engagement.
I wonder if there is a way in PHP to generate a unique alphanumeric(case sensitive) tokens that can be unique forever without any collision. If we derive them from the time stamp string which is 10 characters like: 1394452319, that might be possible but I am not sure if we can make the token short up to 4 characters? If not possible then 5, 6, 7 and max is 8 characters. Because I want to generate short tokens to be readable by users.
Tokens should look like: 1aYc, ZoXq, 3iU9, etc.
I don't want to show the users any sequence.
One more thing, my application will be used by more than one user, so in case two users clicked at same time to generate the token, will the PHP application generate the same token (I assume we use the timestamp to generate the token)? How can we prevent from this problem?
Thank you for your help!
this is the another function that you can use also
<?php
function generateRandomString($length = 8) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, strlen($characters) - 1)];
}
return $randomString;
}
echo generateRandomString();
?>
One approach is to have an incremental (i.e. auto_update) id that you keep hidden internally. From that, you generate a hash, representing the id to hide the sequence. The incremented id gets rid of collision problems (i.e. MySQL has an integrated solution for this).
The trick you need to use now is a random hash table consinsting of two columns, both having the values n to m but with the second column being randomized. i.e.
col1 | col2
1 | 2
2 | 4
3 | 5
4 | 1
5 | 3
if you have the randomly sorted number for your incremented number, it is easy to create a hash from that. Just think about your possible chars as numbers. You get it righgt?
Assuming you have a good algorithm for random numbers, you can make a pretty good hash table. However, there also is a way to find an algorithm, providing you with the numbers as they increase. So in this example it would give you col2 = fn(col1) so i.e. 4 = fn(2).
All you have to do is take the result and re-enginer it into a formular :D
Otherwise you have to fill the table initially.
To give you a glimpse insight into the math of it, think of a function that uses odd/even characteristics of the number and combines it with addition.
With n digits using a range of 62 possibilitys (case sensitive letters and numbers) per char you have 62^n possibilities.
For 4 digits that makes 14776336 possibilities (62^4).
Thou that might sound just wonderfull, you can imagine that having a table, prefilled with 14776336 id's is not the cleanest solution.
Still, i hope this at least leads into the right direction.
EDIT:
We started a discussion on math.stackexchange.com. IT has some additional information on how to create a function for our needs.
You can use something like following
<?php
// chars
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!##$%^&*()-+';
// convert to array
$arr = str_split($chars, 1);
// shuffle the array
shuffle($arr);
// array to chars with 8 chars
echo substr(implode('', $arr), 0, 8);
?>
You can use this function :
// RETRUN 24 digit of UNIX ID :
public function getComplexIDTicket(){ // duplicate method on Rest.php
$arrAZ1 = range('A','Z');
$arrAZ2 = range('A','Z');
$arrAZ3 = range('A','Z');
$arrs1 = range('A','Z');
$arrs2 = range('A','Z');
$arrs3 = range('A','Z');
$a1 = $arrAZ1[rand(0,25)];
$a2 = $arrAZ2[rand(0,25)];
$a3 = $arrAZ3[rand(0,25)];
$s1 = $arrs1[rand(0,25)];
$s2 = $arrs2[rand(0,25)];
$s3 = $arrs3[rand(0,25)];
$s = $s1.$s2.$s3;
$t = microtime(true);
$micro = sprintf("%07d",($t - floor($t)) * 10000000);
$id = date('ymdHis').strtoupper(dechex(substr($micro,0,7)));
$id = str_pad($id, 24, $a3.$a2.$a1.$s, STR_PAD_RIGHT);
// 151106214010 3DDBF0 L D C SM4
return $id;
}
Basically on my website there are checkboxes for each day of the week. If a user checks them, those are the days to be saved (in a field) in their user account.
How would I go about saving these dates? For example, if someone checks Saturday and Wednesday, another saves Monday and Tuesday, what's a good pattern to keep this info?
Someone suggested saving it as a bit string like 0001001 the 1's representing the days of the week check marked, out of the 7 bits representing each day. Any ideas?
The table would look like this:
user_id | days
10 | 0010010 (Wednesday and Saturday, week starts on Monday)
The only issue with is how would I output this info from php?
(I'm trying to do this in the most optimal way both on the mysql and php side)
The suggestion to use a bit field type in MySQL with a length of seven sounds fair to keep storage requirements low (BIT(7)).
The only issue with is how would I output this info from php?
The first step would be to define a mapping of each bit to a weekday-name:
$days = array(
'Monday',
'Tuesday',
...
'Sunday'
);
Then you first need to select the binary value from the database, e.g. as a decimal (integer) value:
SELECT days+0 FROM table;
In PHP you can convert this to a fixed-width binary number string using decbin and substr:
$binary = substr('000000'.decbin($integer), -7);
or probably more convenient sprintf:
$binary = sprintf('%07b', $integer);
As each of those characters is either 0 or 1 you can iterate over them and create an array for all set days:
$array = array();
foreach (range(0, 7) as $day)
{
if (! $set = (int) $binary[$day])
continue;
$array[] = $days[$day];
}
The resulting $array contains now all weekday names that the user checked. You can then output them:
$output = '(none)';
if (1 === $count = count($array)
{
$output = $array[0]; # one day
}
elseif ($count)
{
$last = array_pop($array); # last day
$output = implode(', ', $array) . ' and ' . $last;
}
That was the output of the database value. Hope this is helpful.
You can store the days in a serialized array, and that way, get it back easily when you want it.
$days = array('wednesday','saturday');
$store = serialize($days);
and then store $store for that user_id
I think it would be easier to store like this:
user_id | sun | mon | tues | wed |...
01 | 1 | 0 | 0 | 1 |
And leave the day columns as tinyint(1)'s. That way, in PHP, you could just go like this:
if($row['sun'] == 1)
Do sunday stuff
I would suggest a table like this:
id user_id day
1 1 1
2 1 4
3 2 1
4 3 7