我刚刚学习 Laravel,我正在尝试实现一本书和一篇评论之间的一对多关系,但是播种失败,不知道为什么

问题描述 投票:0回答:1

书籍模型

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Book extends Model
{
    use HasFactory;
    public function reviews(){
        // a book can have many reviews
        return $this->hasMany(Review::class);
        
        // i tried
            //return $this->hasMany(Review::class, 'book_id');
        //but it didn't solve the problem
    }
}

审查模型

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Review extends Model
{
    use HasFactory;

    public function books(){
        // a review is tied to one book
        return $this->belongsTo(Book::class);
        // I tried
            //return $this->belongsTo(Book::class,'book_id');
        //but it didn't work
    }
}

Book 模型的迁移文件

public function up() : void
    {
        Schema::create('books', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('author');
            $table->timestamps();
        });
    }

Review模型的迁移文件

public function up() : void
    {
        Schema::create('reviews', function (Blueprint $table) {
            $table->id();


            $table->text('review');
            $table->unsignedTinyInteger('rating');
            $table->timestamps();

            //?defining the foreign key
            // $table->unsignedBigInteger( 'book_id' );
            // $table->foreign( 'book_id' )
               ->references( 'id' )->on( 'books' )->onDelete( 'cascade' );

            //?a shorter syntax
            $table->foreignId('book_id')->constrained()->cascadeOnDelete();
        });
    }

BookFactory 文件

public function definition() : array
    {
        return [
            'title'      => fake()->sentence(3),
            'author'     => fake()->name,
            'created_at' => fake()->dateTimeBetween('-2 years'),
            'updated_at' => fake()->dateTimeBetween('created_at', 'now')
        ];
    }

评论工厂

class ReviewFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition() : array
    {
        return [
            'book_id'    => null,
            'review'     => fake()->paragraph,
            'rating'     => fake()->numberBetween(1, 5),
            'created_at' => fake()->dateTimeBetween('-2 years'),
            'updated_at' => fake()->dateTimeBetween('created_at', 'now')
        ];
    }


    //? Create state methods to create more diversity in the generated data
    public function good(){
        return $this->state(function (array $attributes) {
            return [
                'rating' => fake() -> numberBetween(4,5)
            ];
        });
    }
    public function average(){
        return $this->state(function (array $attributes) {
            return [
                'rating' => fake() -> numberBetween(2,5)
            ];
        });
    }
    public function bad(){
        return $this->state(function (array $attributes) {
            return [
                'rating' => fake() -> numberBetween(1,3)
            ];
        });
    }
}

DatabaseSeeder.php 文件

public function run() : void
    {
        Book::factory(34)->create()->each(function ($book) {
            $numReviews = random_int(5, 30);
            Review::factory()
                ->count($numReviews) //the number of the reviews generated
                ->good() // using the state method to define the 'rating'
                ->for($book) // associating the records to the current generated book
                ->create();
        });
        Book::factory(33)->create()->each(function ($book) {
            $numReviews = random_int(5, 30);
            Review::factory()
                ->count($numReviews)
                ->average()
                ->for($book)
                ->create();
        });
        Book::factory(33)->create()->each(function ($book) {
            $numReviews = random_int(5, 30);
            Review::factory()
                ->count($numReviews)
                ->bad()
                ->for($book)
                ->create();
        });
    }

运行“php artisan migrate:refresh --seed”时收到的错误消息

   INFO  Rolling back migrations.

  2023_08_17_222856_create_reviews_table ...................................... 18ms DONE
  2023_08_17_222848_create_books_table ......................................... 7ms DONE
  2019_12_14_000001_create_personal_access_tokens_table ........................ 8ms DONE
  2019_08_19_000000_create_failed_jobs_table ................................... 7ms DONE
  2014_10_12_100000_create_password_reset_tokens_table ......................... 7ms DONE
  2014_10_12_000000_create_users_table ......................................... 7ms DONE


   INFO  Running migrations.

  2014_10_12_000000_create_users_table ........................................ 33ms DONE
  2014_10_12_100000_create_password_reset_tokens_table ........................ 36ms DONE
  2019_08_19_000000_create_failed_jobs_table .................................. 24ms DONE
  2019_12_14_000001_create_personal_access_tokens_table ....................... 34ms DONE
  2023_08_17_222848_create_books_table ......................................... 9ms DONE
  2023_08_17_222856_create_reviews_table ...................................... 45ms DONE


   INFO  Seeding database.


   Illuminate\Database\QueryException

  SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'book_id' cannot be null (Connection: mysql, SQL: insert into `reviews` (`book_id`, `review`, `rating`, `created_at`, `updated_at`) values (?, Officiis consequatur temporibus maxime sequi laudantium et. Nam non voluptas est ea. Id fugit et amet deserunt ullam laborum eveniet. Harum eos ratione voluptate debitis qui sequi., 5, 2022-11-02 05:18:02, 2022-12-06 12:41:24))

  at vendor\laravel\framework\src\Illuminate\Database\Connection.php:795
    791▕         // If an exception occurs when attempting to run a query, we'll format the error
    792▕         // message to include the bindings with SQL, which will make this exception a
    793▕         // lot more helpful to the developer instead of just the database's errors.
    794▕         catch (Exception $e) {
  ➜ 795▕             throw new QueryException(
    796▕                 $this->getName(), $query, $this->prepareBindings($bindings), $e
    797▕             );
    798▕         }
    799▕     }

  1   vendor\laravel\framework\src\Illuminate\Database\Connection.php:580
      PDOException::("SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'book_id' cannot be null")

  2   vendor\laravel\framework\src\Illuminate\Database\Connection.php:580
      PDOStatement::execute()

我原本希望找到 100 本书,每本书有 5 到 30 条评论,但当我检查 phpMyAdmin 时,我发现只有 34 本书被种子化,而且根本没有评论被种子化,但我发现外键列已添加到评论表中。

php one-to-many data-migration laravel-10 laravel-seeding
1个回答
0
投票

好吧,这里有很多东西要解压。

  1. 在您的审查模型中,所属函数的名称应该是单数。所以它是

    book()
    不是
    books()

  2. 您忘记注释掉审核迁移文件中的

    ->references( 'id' )->on( 'books' )->onDelete( 'cascade' );
    行。

  3. 在您的 Book- 和 ReviewFactory 中,您有

    faker()->name
    faker()->paragraph
    ,它们应该是函数,所以
    faker()->name()
    faker()->paragraph()

  4. 在您的 Book- 和 ReviewFactory 中,您有

    fake()->dateTimeBetween('-2 years')
    。我不是 100% 舒尔,但我认为它应该采用 0 或 2 个参数,例如
    fake()->dateTimeBetween('-2 years', 'now')

  5. ...这就是您的错误消息的来源。在您的 ReviewFactory 中您有

    'book_id' => null
    。只需删除这一行即可。

在 DatabaseSeeder 中,您的代码可能如下所示:

public function run(): void
{
    $book = Book::factory(34)
        ->has(Review::factory()->count(random_int(5, 30)), 'reviews')
        ->create();
}
© www.soinside.com 2019 - 2024. All rights reserved.