the Book model
<?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
}
}
the Review model
<?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
}
}
the migration file for the Book model
public function up() : void
{
Schema::create('books', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('author');
$table->timestamps();
});
}
the migration file of the Review model
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();
});
}
the BookFactory file
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')
];
}
the ReviewFactory
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)
];
});
}
}
the DatabaseSeeder.php file
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();
});
}
the error message i get when I run "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()
I was expecting to find a 100 books and between 5 to 30 reviews for each book seed to the database, but when I checked phpMyAdmin I found that only 34 books have been seed and that no reviews at all were seed, but I found that the foreign key column has been added to the reviews table.