web-dev-qa-db-fra.com

Comment générer une valeur aléatoire unique pour chaque utilisateur dans laravel et l'ajouter à la base de données

Je développe un site Internet d'organisation d'événements. Ici, lorsque l'utilisateur s'inscrit à un événement, il recevra un numéro aléatoire unique (10 chiffres), que nous utilisons pour générer un code-barres et le lui envoyer. Maintenant,

  1. Je veux rendre le numéro unique pour chaque événement enregistré.
  2. Et aussi aléatoire

Une solution consiste à saisir tous les nombres aléatoires dans un tableau et à générer un nombre aléatoire à l'aide de Php Rand (1000000000, 9999999999), de parcourir et de vérifier toutes les valeurs. Saisissez la première valeur qui n'est égale à aucune des valeurs du tableau et ajoutez-la à la base de données.

Mais je pense qu'il pourrait y avoir une meilleure solution à cela. Toute suggestion?

13
Nasif Md. Tanjim

Votre logique n'est pas techniquement défectueuse. Cependant, si votre application attire de nombreux utilisateurs, la récupération de tous les nombres aléatoires risque de devenir inutilement coûteuse en termes de ressources et de temps de calcul.

Je suggère une autre approche, où vous générez un nombre aléatoire, puis le comparez à la base de données.

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();
}
9
Joel Hinz

Vous pouvez utiliser la fonction niqid () de php pour générer un ID unique basé sur le microtime (heure actuelle en microsecondes)

Exemple:

<?php
echo uniqid();
?>

Production:

56c3096338cdb
21

La boucle à travers le tableau ne sera pas aussi efficace. Si votre base de données devient trop volumineuse, cela ralentit tout le processus et il peut également y avoir une situation rare lorsque 2 threads font une boucle dans le tableau pour le même nombre aléatoire et il sera trouvé disponible et renverra le même nombre aux deux tickets.

Ainsi, au lieu de parcourir le tableau, vous pouvez définir l'ID d'enregistrement à 10 chiffres comme clé primaire et au lieu de parcourir le tableau, vous pouvez insérer les détails d'enregistrement ainsi que le numéro généré de manière aléatoire, si l'opération d'insertion de la base de données réussit, vous pouvez renvoyer l'ID d'enregistrement mais sinon, régénérez le nombre aléatoire et insérez.

Solution alternative qui sera plus efficace Au lieu de 10 chiffres aléatoires, vous pouvez utiliser l'horodatage pour générer un numéro d'enregistrement unique à 10 chiffres et pour le rendre aléatoire, vous pouvez randomiser les 2 ou 3 premiers chiffres de l'horodatage

2
Pavan Jiwnani

Pour éviter le problème d'avoir à vérifier si un code correspondant existe à chaque fois qu'un nouveau est créé, j'attrape simplement l'exception d'enregistrement en double de MySQL (code d'erreur 1062). Si cette erreur est détectée, j'appelle à nouveau la fonction jusqu'à ce que la sauvegarde réussisse. De cette façon, il ne doit générer un nouveau code que s'il entre en collision avec un code existant. Fonctionne beaucoup plus rapidement - mais devient évidemment un peu plus lent lorsque votre nombre d'utilisateurs se rapproche du nombre de codes-barres possibles.

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);
        }

    }
}

Il vous suffit donc de parcourir tous les utilisateurs auxquels vous souhaitez attribuer un code:

foreach(User::all() as $user) {
    generateBarcode($user->id);
}

Vous pouvez également ajouter un peu de logique pour échapper à la boucle de fonction si un nombre maximum de tentatives est effectué, mais je n'ai jamais dérangé car les collisions sont peu probables.

1
possemedia
<?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;
    }
}

exemple d'utilisation

$card->number = GeneratorHelper::generateID(Card::class, 'number');
1
Takamura

C'est bon:

do {
   $refrence_id = mt_Rand( 1000000000, 9999999999 );
} while ( DB::table( 'transations' )->where( 'RefrenceID', $refrence_id )->exists() );
1
Hadi Note

Une solution pourrait être comme ceci:

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();
}

Il génère un nombre aléatoire entre 1000000000 et 9999999999. Après cela, il valide le nombre par rapport à la table. Si vrai, il renvoie le nombre, sinon exécute à nouveau la fonction.

0
Naqib Faiyaz

J'ai fait quelque chose comme ça

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