Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ehsanquddusi's avatar

Generate large number of unique coupons (~6 characters)

I need to create promo codes which should be short in length (~ 6 characters). The promo codes have to be unique, so I need to check their uniqueness in database as well. They need to be generated in batches of thousands, so a check in db with every coupon generation is not feasible. I have created a method which first generates the required number of coupons and then check for duplicates using where in(). Having duplicate count of greater than zero, makes it generate the count again.

    public function generateCoupons($count, $length = 6)
    {
        $coupons = [];
        while(count($coupons) < $count) {
            do {
                $coupon = strtoupper(str_random($length));
            } while (in_array($coupon, $coupons));
            $coupons[] = $coupon;
        }

        $existing = Offer::whereIn('coupon', $coupons)->count();
        if ($existing > 0)
            $coupons += $this->generateCoupons($existing, $length);

        return (count($coupons) == 1) ? $coupons[0] : $coupons;
    }

Need suggestions how to improve upon this? Or if I can have some other way to achieve the same.

0 likes
11 replies
jimmy.puckett's avatar

How many coupons & what characters are you using? Let say that you need 50 & are using ABCDEF (base 6), so that gives you 46,656 combinations. For this example, you could do something like this...

  1. divide the max number by needed (46,565 / 50 = 933.12)
  2. take the floor of that number 933.
  3. now loop 50 time to get a random number between 1 & 933 & add it to the loop index times the 933
  4. convert that number to base 6

This would give you a randomly selected value every 933 spaces, so you know that you would have unique coupons & they would not be guessable.

Tavicu's avatar

Hei,

I would make a query first to get all existing codes and save them into an array!

Then run while and check if the code is in array, if it's not there add it in the db and push to the array!

ehsanquddusi's avatar

@Tavicu - That is practically not possible. It will lead to memory leaks. What if we have ~1000000 promo codes in db?

shez1983's avatar

memory leaks? how so.. please explain?

I don't think it will..

robgeorgeuk's avatar

I can think of two ways to try

First way

  1. Create a temporary db table.
  2. Add all the new coupon codes to this table.
  3. Use SQL query to check that all codes in temporary table are unique.
  4. Use SQL query to compare live table to temporary table.
  5. Remove duplicates and merge.

Second way

  1. Set coupon code column in live db to UNIQUE.
  2. Generate code and insert to db.
  3. DB will reject if not unique.

I suspect the first way will give better performance but I haven't tested it.

Tavicu's avatar

@ehsanquddusi Any other method than checking the current database it will not work. It will be a very small (but it will be) chance to get the same code twice.

Making a single query and save ~1000000 promo codes in an array (or laravel collection) is better than making a query for every code you generate.

@robgeorgeuk About the first way, what if he want's to create 100 codes and 10 of them will be duplicates? It will have only 90 codes instead of 10. The only way to create the 100 codes it's with a while.

The second way it's actually a good solution.

shez1983's avatar

of course what you could do is, make the code unique in the database, and then use laravel's firstorcreate() to make the coupon.. this way new coupons will be created... and duplicate ones will be ignored.. all this without getting the codes from db first.. you would however need some sort of a counter to see how many codes have been added...

cedamorim's avatar

How often will create these coupons? I think it's no problem if it takes a while, the important thing is to ensure that will not be repeated coupons.

I think the idea of putting the coupon field as UK interesting only because you can go creating and if it fails, try to generate a new coupon, to complete the desired amount.

To avoid run 10,000 SQL's, you can check for example, 1000 coupons at a time if they exist (may even be more or less, it depends on your database)

Anyway, there are other ways this could be done and also one MAYBE would be up to the creation of procedure (leaves your database take care of it, in my opinion )

If I can think of something better, I post again

ohffs's avatar

I'm full of the cold, so ignore me if this is rubbish - but if it's only 6 chars and assuming a-zA-Z0-9 then can't you have a background task that keeps a pool of, say, 10000 definitely unique codes and then when you're creating a new coupon just drain the pool of 'known good' codes? The background task can be a bit slow/ugly - but the coupon creation code wouldn't have to worry about all the logic & db stuff?

As I say - I'm a bit ill and full of some rather suspicious 'cold remedy' just now, so I'm probably talking rubbish :-) Took me about 20 minutes to make a cup of coffee this morning - fun times! ;-)

mikebarwick's avatar

What about just storing the data in a table, setting an auto-increment and using that ID to hash out a 6-digit random code (which will be unique). Maybe using this library? https://github.com/vinkla/hashids

Not sure this is a better solution, but throwing it out there as another option. No checks, one query, and will always be unique. Or you could create a random hash (based off the ID and store that in a "hash" or "coupon" field.

1 like

Please or to participate in this conversation.