4

I have a table called 'threads' in my laravel app, to which i want to add a new column named key with random strings of length 5. I ran the php artisan migrate:make add_key_to_threads on the CLL to create the migration file. In my generated migration file, the up function, i then used

Schema::table('threads', function($table){
            $table->str_random('key', 5);
        })

to add the new column, after which i ran php artisan migrate on my CLL. Obviously, The method str_random() does not exist (found it in a resource while googling around) as i keep getting the error "[BadMethodCallException] Method str_random does not exist". Can someone kindly guide me in doing this correctly.

Marcin Nabiałek
  • 109,655
  • 42
  • 258
  • 291
banky
  • 838
  • 2
  • 13
  • 26

4 Answers4

4

Schema::table is only to update you Database table, without using to coresponding SQL statments. In SQL there is no column type that automatically fills your table with a random string. Therefor the column type hast to be string. See all types that exit in Laravel 5.5 https://laravel.com/docs/5.5/migrations#columns

$table->string('key', 5);

The next question is if the key must be unique, if so then you can append the unique() function:

$table->string('key', 5)->unique();

If you have already data than you have to to this in a secound step, after, you fild in all your keys.

You have two options to fill random data in your column, with PHP (Laravel) or with SQL. With SQL you have to write a function and the function can be different depending on your SQL Server Type e.g.: Default random 10 character string value for SQL Server column

With Laravel Eloqent you don't have to think about what SQL Server you have.

$threads      = new Threads();
$threads->key = str_random(5);
$threads->save();

If you used the ->unique() function this could throw an exception if your APP create a key twice. Then you have to catch the Exception and try it again.

/* only if you need a unique Key */
$threads     = new Threads();

while (true)
{
    try
    {
        $threads->key = str_random(5);
        $threads->save();
        //  We could save now we break out of the loop
        break;
    }
    catch (Exception $e)
    {
        //  Could not save, try it again
    }
}

For you existing rows you can change the migration as follows:

public function up()
{
    Schema::table('threads', function (Blueprint $table)
    {
        $table->string('key', 5);
    });

    $existing_threads = Threads::where('key', '')->get();

    foreach($existing_threads as $threads)
    {
        $threads->key = str_random(5);
        $threads->save();                
    }
}

The Model Threads must exit before you do the migration

Webdesigner
  • 1,954
  • 1
  • 9
  • 16
0

As far as I know it's possible. In migration you need to create ordinary string column like this:

$table->string('key', 5);

and then when creating new model, you should run:

$model->key = str_random(5);

Assuming you use Eloquent when creating your model you can use the following syntax:

$thread = new Thread();
// below are sample fields you normally save for this thread
$thread->field_1 = 'value1';
$thread->field_2 = 'value2';
// this is the random key you set
$thread->key = str_random(5);
// now you save thread in database
$thread->save();
Marcin Nabiałek
  • 109,655
  • 42
  • 258
  • 291
  • Thanks for your assistance @Marcin Nabialek..Please can you explain the process of creating new model in this instance?Am i not suppose to run php artisan migrate after creating the string in my migration file? – banky Oct 08 '17 at 15:17
  • @banky I've updated my answer and showed here how you can create model. No, you create normal migration and set this key to string inm this migration and run it. And later when creating any models you need to make sure you set the key. – Marcin Nabiałek Oct 08 '17 at 15:31
0

Here you go:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddRandomStringKeyToThreadsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('threads', function (Blueprint $table) {
            $table->string('key');
        });

    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('threads', function (Blueprint $table) {
            $table->dropColumn('key');
        });
    }
}

update

I have changed the $table->string('key')->default(str_random(5)); to this $table->string('key');

And then make this route just to update the existing rows... This worked for me...maybe you will need to modify this for your table. But at least you will get an idea how to do it.

Route::get('/update-table', function () {
    $count = DB::table('threads')->count();

    while ($count > 1){
        DB::table('threads')
            ->where('id', '=', $count)
            ->update(['key' => str_random(5)]);
        $count--;
    }

    return 'DB updated';
});

And then you need to create a random string for every new thread.

lewis4u
  • 14,256
  • 18
  • 107
  • 148
  • This will automatically create that "key" for all existing rows in "threads" table and you don't have to worry about creating it somewhere else...(in controller on store() method for example) it will be automatically created with a every new thread – lewis4u Oct 08 '17 at 16:34
  • Sorry this will not work as expected... `str_random(5)` will generate a random string, and set it as default for the column during the migration. Now this it the **same default value** for all new rows... – Webdesigner Oct 08 '17 at 16:55
  • You can put it outside of Schema::table .... and new it up so it can be different....and then for new creations you will need to make a new str_random(5) – lewis4u Oct 08 '17 at 17:03
  • Have in mind that this migration to add a new key to existing table is only for fixing the existing rows in table....and for new submits you need to generate new random strings – lewis4u Oct 08 '17 at 17:04
  • But than all existing key columns would have the same key even if this one is random but having 100 old rows with key `we45r` or `gtjuz` is not a got way to do it. And new columns if they do not get a key from the APP will get the same string. – Webdesigner Oct 08 '17 at 17:08
  • Updated answer! – lewis4u Oct 08 '17 at 18:38
  • If this route is not protected somehow, than the hole table will change all the keys every time if someone only enter http://www.example.com/update-table, and not only the empty ones! – Webdesigner Oct 08 '17 at 18:45
  • You need to run this route only ONCE to update the existing rows and then delete it...don't keep it in your web.php file!!! – lewis4u Oct 08 '17 at 18:46
0

Simplely use $this to use your random string generator in migration. Example:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class Projects extends Migration
{

    public function up()
    {
        Schema::create('example', function (Blueprint $table) {
            $table->bigIncrements('id');
            // use $this to access class function
            $table->string($this->randString(5));
        });

    }

    // just a random string generator
    public function randString($length){
        $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $charactersLength = strlen($characters);
        $randomString = '';
        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[rand(0, $charactersLength - 1)];
        }
        return $randomString;

    }


}
黃皓哲
  • 3,422
  • 2
  • 7
  • 13