如何为 laravel 中的每个用户生成唯一的随机值并将其添加到数据库

How to generate unique random value for each user in laravel and add it to database

我正在开发一个活动组织网站。在这里,当用户注册参加活动时,他将获得一个唯一的随机数(10 位数字),我们用它来生成条形码并将其邮寄给他。现在,

  1. 我想为每个注册的活动设置唯一的编号。
  2. 也是随机的

一种解决方案是获取数组中的所有随机数并使用 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();
}

遍历数组不会那么有效。如果您的数据库变得太大,那么它会减慢整个过程,并且可能会出现一种罕见的情况,当 2 个线程循环遍历数组以获得相同的随机数时,它会被发现可用并且 return 相同的数字到两者门票。

因此,您可以将 10 位注册 ID 设置为主键,而不是遍历数组,而不是循环遍历数组,您可以插入注册详细信息以及随机生成的号码,如果数据库插入操作成功,您可以 return 注册 ID 但如果不能则重新生成随机数并插入。

更有效的替代解决方案 您可以使用时间戳代替 10 位随机数来生成 10 位唯一注册号并使其随机化,您可以随机化时间戳的前 2 位或 3 位数字

我做了这样的东西

/**
 * 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;
}

您可以使用php的uniqid()函数根据微时间(当前时间以微秒为单位)生成唯一ID

示例:

<?php
echo uniqid();
?>

输出:

56c3096338cdb

为了避免每次创建新代码时都必须检查匹配代码是否存在的问题,我只捕获了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);
}

如果进行了最大次数的尝试,您还可以添加一些逻辑来逃避函数循环,但我从来没有费心,因为不太可能发生冲突。

这个不错:

do {
   $refrence_id = mt_rand( 1000000000, 9999999999 );
} while ( DB::table( 'transations' )->where( 'RefrenceID', $refrence_id )->exists() );
<?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');

一个解决方案可能是这样的:

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 之间的随机数。之后,它根据 table 验证该数字。如果为真,则它 returns 数字,否则再次运行该函数。

助手 (app/Helpers/Constants.php)

<?php
namespace App\Helper;

class Constants
{
    public static function getUniqueId($model){
        $id =  mt_rand(1000000000, 9999999999);
        if($model->find($id))
            return self::getUniqueId($model);
        return $id;
    }
}
?>

型号 (app/Models/User.php)

<?php

namespace App;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    public static function boot(){
        parent::boot();
        $creationCallback = function ($model) {
            if (empty($model->{$model->getKeyName()}))
                $model->{$model->getKeyName()} = Constants::getUniqueId(new self());
        };
        static::creating($creationCallback);
    }
}
?>

说明:而不是在每个控制器中调用getUniqueId方法。你可以把它写在模型里面。