イベント組織のウェブサイトを開発しています。ここで、ユーザーがイベントに登録すると、一意の乱数(10桁)が与えられます。これを使用して、バーコードを生成し、ユーザーにメールで送信します。さて、
1つの解決策は、配列内のすべての乱数を取得し、Php Rand(1000000000、9999999999)を使用して乱数を生成し、ループしてすべての値を確認することです。配列内のどの値とも等しくない最初の値を取得して、データベースに追加します。
しかし、これにはもっと良い解決策があるかもしれないと私は考えています。なにか提案を?
ロジックに技術的な欠陥はありません。ただし、アプリケーションが多くのユーザーを引き付ける場合、すべての乱数のフェッチは、リソースと計算時間の点で、不必要に高価になる可能性があります。
乱数を生成してデータベースと照合する別のアプローチをお勧めします。
function generateBarcodeNumber() {
$number = mt_Rand(1000000000, 9999999999); // better than Rand()
// call the same function if the barcode exists already
if (barcodeNumberExists($number)) {
return generateBarcodeNumber();
}
// otherwise, it's valid and can be used
return $number;
}
function barcodeNumberExists($number) {
// query the database and return a boolean
// for instance, it might look like this in Laravel
return User::whereBarcodeNumber($number)->exists();
}
PHPの niqid() 関数を使用して、マイクロ時間(マイクロ秒単位の現在の時間)に基づいて一意のIDを生成できます。
例:
<?php
echo uniqid();
?>
出力:
56c3096338cdb
配列のループはそれほど効率的ではありません。データベースが大きくなりすぎると、プロセス全体の速度が低下します。また、2つのスレッドが同じ乱数の配列をループしていて、それが利用可能で、両方のチケットに同じ番号を返すというまれな状況が発生する可能性があります。
したがって、配列をループする代わりに、10桁の登録IDを主キーとして設定できます。配列をループする代わりに、ランダムに生成された番号とともに登録の詳細を挿入できます。データベースの挿入操作が成功した場合は、登録IDを返すことができます。そうでない場合は、乱数を再生成して挿入します。
より効果的な代替ソリューション10桁の乱数の代わりに、タイムスタンプを使用して10桁の一意の登録番号を生成し、ランダムにすることで、タイムスタンプの最初の2桁または3桁をランダム化できます。
新しいコードが作成されるたびに一致するコードが存在するかどうかを確認する必要があるという問題を回避するために、MySQLの重複レコード例外(エラーコード1062)をキャッチします。そのエラーがキャッチされた場合、保存が成功するまで関数をもう一度呼び出します。そうすれば、既存のコードと衝突した場合にのみ、新しいコードを生成する必要があります。実行速度はかなり速くなりますが、ユーザー数が可能なバーコードの数に近づくにつれて明らかに少し遅くなります。
function generateBarcode($user_id) {
try {
$user = User::find($user_id);
$user->barcode = mt_Rand(1000000000, 9999999999);
$user->save();
} catch (Exception $e) {
$error_info = $e->errorInfo;
if($error_info[1] == 1062) {
generateBarcode($user_id);
} else {
// Only logs when an error other than duplicate happens
Log::error($e);
}
}
}
したがって、コードを割り当てたいすべてのユーザーをループするだけです。
foreach(User::all() as $user) {
generateBarcode($user->id);
}
最大数の試行が行われた場合に関数ループをエスケープするロジックを追加することもできますが、衝突が発生する可能性が低いので、私は気にしたことはありません。
<?php
declare(strict_types=1);
namespace App\Helpers;
use App\Exceptions\GeneratorException;
class GeneratorHelper
{
public static $limitIterations = 100000;
/**
* @param string $column
* @param string $modelClass
* @return string
* @throws GeneratorException
*/
public static function generateID(string $modelClass, string $column): string
{
return self::run(
$modelClass,
$column,
self::IDGenerator(),
'Generation id is failed. The loop limit exceeds ' . self::$limitIterations
);
}
/**
* @param string $modelClass
* @param string $column
* @param \Generator $generator
* @param string $exceptionMessage
* @param array $whereParams
* @return string
* @throws GeneratorException
*/
protected static function run(string $modelClass, string $column, \Generator $generator, string $exceptionMessage, array $whereParams = []): string
{
try {
foreach ($generator as $id) {
$query = $modelClass::where([$column => $id]);
foreach ($whereParams as $param) {
$query->where(...$param);
}
if (!$query->first()) {
return $id;
}
}
} catch (\Throwable $e) {
$exceptionMessage = $e->getMessage();
}
throw new GeneratorException($exceptionMessage);
}
protected static function IDGenerator(): ?\Generator
{
for ($i = 1; $i <= self::$limitIterations; $i++) {
yield (string)random_int(1000000000, 9999999999);
}
return null;
}
}
使用例
$card->number = GeneratorHelper::generateID(Card::class, 'number');
これはいい:
do {
$refrence_id = mt_Rand( 1000000000, 9999999999 );
} while ( DB::table( 'transations' )->where( 'RefrenceID', $refrence_id )->exists() );
1つのソリューションは次のようになります。
use Illuminate\Support\Facades\Validator;
private function genUserCode(){
$this->user_code = [
'user_code' => mt_Rand(1000000000,9999999999)
];
$rules = ['user_code' => 'unique:users'];
$validate = Validator::make($this->user_code, $rules)->passes();
return $validate ? $this->user_code['user_code'] : $this->genUserCode();
}
1000000000から9999999999の間の乱数を生成します。その後、テーブルに対して番号を検証します。 trueの場合は数値を返し、そうでない場合は関数を再度実行します。
このようなものを作りました
/**
* Generate unique shipment ID
*
* @param int $length
*
* @return string
*/
function generateShipmentId($length)
{
$number = '';
do {
for ($i=$length; $i--; $i>0) {
$number .= mt_Rand(0,9);
}
} while ( !empty(DB::table('shipments')->where('id', $number)->first(['id'])) );
return $number;
}