Wczoraj pisałem moduł obsługujący kody rabatowe w sklepie internetowym. Wedle specyfikacji kody mogą być jedno- lub wielokrotnego użytku. Te pierwsze mogą być hurtowo generowane w panelu administracyjnym. Na chłopski rozum taki jednorazowy kod powinien być:
- losowy, żeby pomysłowi klienci nie mogli zgadywać kodów
- w miarę krótki, żeby wpisywanie go nie było kłopotliwe
- pozbawiony podobnych do siebie znaków takich jak O i 0, ze względów wspomnianych w poprzednim punkcie
Mój pomysł na generowanie takich kodów wygląda następująco:
public static function generateRandom()
{
return str_pad(strtr(strtoupper(base_convert(rand(1000, 2176782335), 10, 36)), '0O', '1A'), 6, 'X');
}
Powyższa metoda generuje losowe kody złożone z 6 alfanumerycznych znaków (wielkie litery i cyfry) z pominięciem zera i litery O. Przykładowy kod: 3LFPHP. Tytułem wyjaśnienia: 2176782335 to ZZZZZZ w systemie trzydziestoszóstkowym.
Nie robiłem żadnych testów, ale przeczucie mówi mi, że takie rozwiązanie jest szybsze niż losowanie po jednym znaku z tablicy dozwolonych znaków.
Kody powinny być także unikalne, a powyższa metoda tego nie zapewnia. Gdyby tych kodów trzeba było generować tysiące na minutę pewnie pokusiłbym się o jakiś bardziej wyszukany algorytm, np. w jednej transakcji wygenerować tablicę z liczbą kodów ciut większą niż zadana, jednym zapytaniem sprawdzić które z nich już występują w bazie, odfiltrować je z tablicy, a resztę wstawić drugim zapytaniem. W tym konkretnym sklepie nie ma takiej potrzeby, więc wybrałem prostsze rozwiązanie.
public static function generateCodes(Voucher $voucher, $number)
{
for ($i = 1; $i <= $number; $i++)
{
do
{
try
{
$code = new VoucherCode();
$code->setVoucher($voucher)
->setCode(VoucherCode::generateRandom())
->save();
}
catch (Doctrine_Connection_Mysql_Exception $e)
{
}
} while ($code->isNew())
}
}
Kolumna code w tabeli ma założony unikalny indeks (przydaje się do wyszukiwania), więc próba wstawienia takiej samej wartości powoduje wyjątek, który łapię i generuję nowe wartości dopóki wstawianie rekordu do tablicy nie zakończy się powodzeniem.