phplaravellaravel-sanctumseedinglaravel-seeding

How to generate Laravel Sanctum tokens for users while seeding?


I have a Laravel REST API that exclusively serves authenticated users. Authentication is done via Laravel Sanctum.

Now for local development on the API, I seed the database with three users.

User::factory()->count(2)->hasLocations(100)->create(); // two users with related data
User::factory()->create(); // another user

However, I cannot use those users to work on the API and test stuff out, because they don't have access tokens!

How to automatically create Sanctum tokens for my users while seeding and log them to a file?


Solution

  • This can easily be achieved as such:

    class DatabaseSeeder extends Seeder
    {
        public function run(): void
        {
            File::put(base_path('sanctum_tokens.txt'), '');
    
            $users = collect([
                User::factory()->count(2)->hasLocations(100)->create(),
                User::factory()->create()
            ])->flatten();
    
            $users->each(function (User $user) {
                $token = $user->createToken('basic-token')->plainTextToken;
                $line = "{$user->id} | {$user->name} | {$token}" . PHP_EOL;
                File::append(base_path('sanctum_tokens.txt'), $line);
            });
        }
    }
    

    * given your User model has the fields id and name, if that's not the case, change {$user->id}` and `{$user->name} to anything that identifies a user for you.

    Explanation

    1. Create an empty sanctum_tokens.txt file at the root of your project.

    2. Gather your created User models to a Laravel collection and flatten it because the line with ->count(2) creates a nested collection which we don't want. We hereby get a collection of plain User models.

    3. Iterate over the collection of users now, create a token for every one and append a new line to the sanctum_tokens.txt file with the desired information.

    Performance-optimized version

    Many small write operations are always far more expensive for the operating system than a single combined write operation.

    Thus, if you care about performance, consider this solution, which writes the file at once.

    class DatabaseSeeder extends Seeder
    {
        public function run(): void
        {
            $users = collect([
                User::factory()->count(2)->hasLocations(100)->create();
                User::factory()->create();
            ])->flatten();
    
            $lines = $users->map(function (User $user) {
                $token = $user->createToken('basic-token');
                return "{$user->id} | {$user->name} | {$token->plainTextToken}";
            });
    
            File::put(base_path('sanctum_tokens.txt'), $lines->implode("\n"));
        }
    }