Laravel – Seeding-Beziehungen

Lesezeit: 7 Minuten

In Laravel wird das Seeding von Datenbanken im Allgemeinen durch Modellfabriken erreicht. Sie definieren also mithilfe von Faker-Daten einen Entwurf für Ihr Modell und sagen, wie viele Instanzen Sie benötigen:

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});

$user = factory(App\User::class, 50)->create();

Nehmen wir jedoch an, Ihr Benutzermodell hat a hasMany Beziehung zu vielen anderen Models, wie z Post Modell zum Beispiel:

Post:
   id
   name
   body
   user_id

In dieser Situation möchten Sie also Ihre Posts-Tabelle mit Seeden versehen tatsächlich Benutzer, die in Ihre Benutzertabelle gesetzt wurden. Dies scheint nicht explizit diskutiert zu werden, aber ich habe Folgendes in den Laravel-Dokumenten gefunden:

$users = factory(App\User::class, 3)
    ->create()
    ->each(function($u) {
         $u->posts()->save(factory(App\Post::class)->make());
    });

In Ihrer Benutzerfabrik erstellen Sie also X Posts für jeden von Ihnen erstellten Benutzer. In einer großen Anwendung, in der vielleicht 50 bis 75 Modelle Beziehungen mit dem Benutzermodell teilen, würde Ihr Benutzer-Seeder im Wesentlichen die gesamte Datenbank mit all ihren Beziehungen besetzen.

Meine Frage ist: Ist das der beste Weg, damit umzugehen? Die einzige andere Sache, die mir einfällt, ist, zuerst die Benutzer zu Seeden (ohne Beziehungen zu setzen) und dann nach Bedarf zufällige Benutzer aus der DB zu ziehen, während Sie andere Modelle Seeden. In Fällen, in denen sie jedoch eindeutig sein müssen, müssen Sie nachverfolgen, welche Benutzer verwendet wurden. Außerdem scheint dies dem Seeding-Prozess eine Menge zusätzlicher Abfragemengen hinzuzufügen.

Benutzer-Avatar
Benutzer6392101

Sie können auch saveMany verwenden. Zum Beispiel:

factory(User::class, 10)->create()->each(function ($user) {
    $user->posts()->saveMany(factory(Posts::class, 5)->make());
});

  • Ich wusste nichts davon saveMany() bis ich diese Antwort sah. Danke. 🙂

    – Vaughany

    10. Januar 2018 um 11:20 Uhr

  • Was ist der Unterschied/Vorteil in saveMany im Vergleich zu create?

    – Begründung

    1. April 2020 um 8:30 Uhr

  • Wie wird die neue $user->id zu den neuen Beiträgen hinzugefügt werden user_id?

    – PeterPan

    6. Mai um 6:51 Uhr

Benutzer-Avatar
TimmyG

Sie können dies wie besprochen mithilfe von Closures innerhalb der ModelFactory tun hier.

Auch mit Sämaschinen funktioniert diese Lösung sauber und elegant.

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});

$factory->define(App\Post::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'body' => $faker->paragraph(1),
        'user_id' => function() {
            return factory(App\User::class)->create()->id;
        },
    ];
});

Verwenden Sie für Ihre Sämaschine etwas Einfaches wie das Folgende:

//create 10 users
factory(User::class, 10)->create()->each(function ($user) {
    //create 5 posts for each user
    factory(Post::class, 5)->create(['user_id'=>$user->id]);
});

HINWEIS: Diese Methode erstellt keine unnötigen Einträge in der Datenbank, stattdessen werden die übergebenen Attribute VOR der Erstellung zugehöriger Datensätze zugewiesen.

  • Sollte die beste Antwort sein 🙂 Danke! Deine Antwort hat mir viel Zeit gespart.

    – alexey-novikov

    9. August 2019 um 16:07 Uhr

  • Eine der besten Antworten. Auch in der Postfabrik kommt man heutzutage mit etwas einfacheren Herangehensweisen davon 'user_id' => factory(User::class)keine Notwendigkeit mehr für die Clojure.

    – FAB

    19. Februar 2020 um 21:52 Uhr

  • @FAB Wie wirst du das konsumieren user_id in der Postfabrik?

    – PeterPan

    6. Mai um 6:55 Uhr

Benutzer-Avatar
Entwickeln123

Persönlich denke ich, dass eine Seeder-Klasse zur Verwaltung dieser Beziehungen besser ist als getrennte Seeder-Klassen, weil Sie die gesamte Logik an einem Ort haben, sodass Sie auf einen Blick sehen können, was vor sich geht. (Wer einen besseren Ansatz kennt: bitte teilen) 🙂

Eine Lösung könnte sein: ein DatabaseSeeder und private Methoden innerhalb der Klasse, um die ‘Run’-Methode etwas sauberer zu halten. Ich habe dieses Beispiel unten, das einen Benutzer, Link, LinkUser (viele-zu-viele) und eine Notiz (viele-zu-eins) hat.

Für die Viele-zu-Viele-Beziehungen erstelle ich zuerst alle Links und erhalte die eingefügten IDs. (da die ids auto-inc sind, denke ich, dass die ids einfacher abgerufen werden könnten (get max), spielt aber in diesem beispiel keine rolle). Erstellen Sie dann die Benutzer und fügen Sie jedem Benutzer einige zufällige Links hinzu (many-to-many). Es erstellt auch zufällige Notizen für jeden Benutzer (viele-zu-eins-Beispiel). Es verwendet die ‘Fabrik’-Methoden.

Wenn Sie den ‘Link’ für Ihren ‘Post’ ersetzen, sollte dies funktionieren. (Dann können Sie den Abschnitt “Notiz” entfernen …)

(Es gibt auch eine Methode, um sicherzustellen, dass Sie 1 gültigen Benutzer mit Ihren eigenen Anmeldeinformationen haben.)

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // Create random links
        factory(App\Link::class, 100)->create();

        // Fetch the link ids
        $link_ids = App\Link::all('id')->pluck('id')->toArray();

        // Create random users
        factory(App\User::class, 50)->create()->each(function ($user) use ($link_ids) {

            // Example: Many-to-many relations
            $this->attachRandomLinksToUser($user->id, $link_ids);

            // Example: Many-to-one relations
            $this->createNotesForUserId( $user->id );
        });

        // Make sure you have a user to login with (your own email, name and password)
        $this->updateCredentialsForTestLogin('[email protected]', 'John Doe', 'my-password');
    }

    /**
     * @param $user_id
     * @param $link_ids
     * @return void
     */
    private function attachRandomLinksToUser($user_id, $link_ids)
    {
        $amount = random_int( 0, count($link_ids) ); // The amount of links for this user
        echo "Attach " . $amount . " link(s) to user " . $user_id . "\n";

        if($amount > 0) {
            $keys = (array)array_rand($link_ids, $amount); // Random links

            foreach($keys as $key) {
                DB::table('link_user')->insert([
                    'link_id' => $link_ids[$key],
                    'user_id' => $user_id,
                ]);
            }
        }
    }

    /**
     * @param $user_id
     * @return void
     */
    private function createNotesForUserId($user_id)
    {
        $amount = random_int(10, 50);
        factory(App\Note::class, $amount)->create([
            'user_id' => $user_id
        ]);
    }

    /**
     * @param $email
     * @param $name
     * @param $password
     * @return void
     */
    private function updateCredentialsForTestLogin($email, $name, $password)
    {
        $user = App\User::where('email', $email)->first();
        if(!$user) {
            $user = App\User::find(1);
        }
        $user->name = $name;
        $user->email = $email;
        $user->password = bcrypt($password); // Or whatever you use for password encryption
        $user->save();
    }
}

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});

$factory->define(App\Post::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'body' => $faker->paragraph(1),
        'user_id' => factory(App\User::class)->create()->id,
    ];
});

Also jetzt, wenn Sie dies tun factory(App\Post::class, 4)->create() Es werden 4 verschiedene Beiträge erstellt und dabei auch 4 verschiedene Benutzer erstellt.

Wenn Sie denselben Benutzer für alle Beiträge haben möchten, mache ich normalerweise Folgendes:

$user = factory(App\User::class)->create();
$posts = factory(App\Posts::class, 40)->create(['user_id' => $user->id]);

Ich möchte den Ansatz, den ich zum Einfügen vieler Beiträge gewählt habe, vielen Benutzern mitteilen:`

factory(App\User::class, 50)->create() 
                ->each( 
                    function ($u) {
                        factory(App\Post::class, 10)->create()
                                ->each(
                                    function($p) use (&$u) { 
                                        $u->posts()->save($p)->make();
                                    }
                                );
                    }
                );

`

Diese Problemumgehung hat für mich funktioniert, nachdem ich den ganzen Tag nach einer Möglichkeit gesucht hatte, die Beziehung zu säen

Benutzer-Avatar
Subramanya Rao

das hat bei mir in laravel v8 funktioniert

for ($i=0; $i<=2; $i++) {
    $user = \App\Models\User::factory(1)->create()->first();
    $product = \App\Models\Product::factory(1)->create(['user_id' => $user->id])->first();
}

Benutzer-Avatar
ignaciodev

Ich verwende eine Sonderanfertigung beziehenodererstellen Funktion, die einen zufälligen Eintrag dieses Modells in der Datenbank findet. Wenn keine vorhanden sind, wird eine neue erstellt:

function relateOrCreate($class) {
    $instances = $class::all();
    $instance;

    if (count($instances) > 0) {
        $randomIndex = rand(0, (count($instances) - 1));
        $instance = $instances[$randomIndex];
    }
    else {
        $instance = $class::factory()->create();
    }

    return $instance;
}

Dann benutze ich es so:

$relatedUser = relateOrCreate(User::class);

return [
    'user_id' => $relatedUser->id,
    // ...
];

  • Es wäre sinnvoller, eine zufällige Zeile aus der Datenbank auszuwählen, anstatt jede einzelne Zeile aus Ihrer Datenbank auszuwählen und dann eine zufällige auszuwählen. Scheint auch unnötig zu sein, da Sie beim Seeding einer Datenbank wissen, ob andere Datensätze vorhanden sind oder nicht.

    – Mike32

    31. Dezember 2021 um 15:57 Uhr


1011920cookie-checkLaravel – Seeding-Beziehungen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy