Search code examples
modellaravel-5.6factories

factory building returns company_id and package_id the same


in my code each company belongs to a package and has a package usage class Package extends Model. package Model:

protected $fillable =[
    'name',
    'daily_cv_view',
    'monthly_cv_view',
    'per_job_post_cv_view',
    'active_job_post_limit',
    'question_per_job_post_limit',
    'package_lifetime',
    'job_post_lifetime_limit',
    'price'
    ];
public function companies()
{
    return $this->hasMany('App\Models\Company');
}

public function packageUsages()
{
    return $this->hasMany('App\Models\package_usage');
}

Company Model

 public function package()
    {
        return $this->belongsTo('App\Models\Package', 'package_id');
    }

packageUsage Model

public function company()
    {
        return $this->belongsTo('App\Models\Company');
    }
    public function package()
    {
        return $this->belongsTo('App\Models\Package');
    }

in my database seeder i am trying to build a package sage for each company created. so its look like this:

 $companies=factory(App\Models\Company::class, 20)->create()->each(function ($company) {
            $company->users()->save(factory(App\Models\User::class)->make());
            $company->events()->save(factory(\App\Models\Event::class)->make());
        });
        foreach ($companies as $company)
            $company->packageUsages()->save(factory(\App\Models\PackageUsage::class)->make([
                'company_id'=>$company->id,
                'package_id'=>$company->first()->package_id
            ]));

and finally my pacage usage fctory loos like this:

$factory->define(App\Models\PackageUsage::class, function (Faker $faker) {
    $companyId= $faker->randomElement(\App\Models\Company::all()->pluck('id')->toArray());
    $company= App\Models\Company::find($companyId);
    $package=$company->package;
    $dailyCvView=$package->first()->daily_cv_view;
    return [
        'company_id'=>$company,
        'package_id'=>$package,
        'daily_cv_view'=>$faker->numberBetween($min=0 , $max=$dailyCvView ),
        'monthly_cv_view'=>$faker->numberBetween($min=$dailyCvView , $max=$package->monthly_cv_view ),
        'active_job_post_count'=>$company->jobPosts->count(),
        'expiration_date'=>$faker->dateTimeBetween($min='now', $max='+ 30 days')
    ];
});

company factory:

$factory->define(\App\Models\Company::class, function (Faker $faker) {
        return [
        'name'=>$faker->company,
        'company_size'=>$faker->randomElement(['10','50','100','200']),
        'slogan'=>'باما کار کنید',
        'website'=>$faker->domainName,
        'logo'=>'/images/companies/avatar',
        'message_title'=>'ما در اینجا.....',
        'message_content'=>$faker->paragraph('10'),
        'main_photo'=>'/images/companies/mainphotos/avatar',
        'about_us'=>$faker->paragraph('10'),
        'why_us'=>$faker->paragraph('10'),
        'recruiting_steps'=>$faker->paragraph('10'),
        'address'=>$faker->address,
        'email'=>$faker->safeEmail,
        'phone_number'=>$faker->numberBetween(10,100),
        'location'=>$faker->city,
            'package_id'=>$faker->randomElement(\App\Models\Package::all()->pluck('id')->toArray())
    ];
});

when i run this it returns error:

   Illuminate\Database\QueryException  : SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`balatar`.`package_usages`, CONSTRAINT `package_usages_package_id_foreign` FOREIGN KEY (`package_id`
) REFERENCES `packages` (`id`)) (SQL: insert into `package_usages` (`company_id`, `package_id`, `daily_cv_view`, `monthly_cv_view`, `active_job_post_count`, `expiration_date`, `updated_at`, `created_at`) values (4, 4, 12, 484, 0, 2018-09-16 07:26:11, 2018-08-30
 03:45:22, 2018-08-30 03:45:22))

when i check database i see that it has created 3 packages for each the company id and the package id are the same. since i think for the forth one when it tries to build it with both ids of 4 and i only inserted 3 packages a foreign key fails. whats the problem in my code?


Solution

  • I think your first mistake is in your database design. Your companies will have more than one packages, as I understand there is only one active package at the time. So it needs to create a many-to-many relation between your companies and packages tables as the following:

    Schema::create('rel_company_package', function (Blueprint $table) {
        $table->increments('id');
        $table->timestamps();
        $table->unsignedInteger('package_id');
        $table->unsignedInteger('company_id');
        $table->timestamp('activated_at')->nullable();
        $table->timestamp('expires_at')->nullable();
        $table->boolean('is_active')->nullable();
        $table->unsignedInteger('nubmer_of_remaining_job_posts');
    });
    
    1. actived_at: The date which your company activates its package.
    2. nubmer_of_remaining_job_posts: Firstly comes from your package data and will be decreased by one as soon as your company posts a job.
    3. expires_at: when your package gets expired
    4. is_active: This field is redundant but will help you to spend more memory instead of calculation resource. Will determines if the currently assigned package is active or not

    Note: you should fill in activated_at, expires_at and is_active with proper values as soon as decide to activate the package.

    Also you should fill in nubmer_of_remaining_job_posts at package assignment time.

    What you are decided to seed is log data which on production time you may not rely on, because it's easily getting huge and takes a lot of resources to calculate what you want. BTW that log data (PackageUsage) should just keep your pivot table's (rel_company_package) id.

    Let's back to your error directly:

    Firstly in your company factory you load all packages and pick one randomly (and your package usage factory is), you'd better not to do that instead ask MySQL to pick a random one for you

    For random company:

    $company = $\App\Models\Company::query()->inRandomOrder->first();
    // and use $company->id
    

    For random package:

    $package = \App\Models\Package::query()->inRandomOrder()->frist();
    // and use $package->id
    

    The other point is, you should create a PackageUsage instance like below:

    $company->packageUsages()->save(factory(\App\Models\PackageUsage::class)->make([
        'package_id' => $company->first()->package_id
    ]));
    

    It doesn't need to pass the company_id again. and it seems you missed the packageUsages() method from Company model, it should like:

    public function packagesUsage() 
    {
        return $this->hasMany('\App\Models\PackageUsage', 'company_id')
    }
    

    it may by the typing mistake you replace comapny_id with package_id.

    But as I mentioned at first, you'd better firstly fix your structure.