如何为 laravel 中的每个用户生成唯一的随机值并将其添加到数据库
How to generate unique random value for each user in laravel and add it to database
我正在开发一个活动组织网站。在这里,当用户注册参加活动时,他将获得一个唯一的随机数(10 位数字),我们用它来生成条形码并将其邮寄给他。现在,
- 我想为每个注册的活动设置唯一的编号。
- 也是随机的
一种解决方案是获取数组中的所有随机数并使用 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
方法。你可以把它写在模型里面。
我正在开发一个活动组织网站。在这里,当用户注册参加活动时,他将获得一个唯一的随机数(10 位数字),我们用它来生成条形码并将其邮寄给他。现在,
- 我想为每个注册的活动设置唯一的编号。
- 也是随机的
一种解决方案是获取数组中的所有随机数并使用 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
方法。你可以把它写在模型里面。