Laravel factory creation of Eloquent Models creates models with wrong attributes - laravel

I have a seeder in which I try to bind possibilities to questions using factory.
...
$question->possibilities()->saveMany(
factory(Possibility::class, $random_num)
->make()
->each(function ($item, $index) use ($correct_answer) {
if ($index === $correct_answer) {
$item->correct = true;
}
})
);
...
When I use factory the Eloquent model has an attribute of answer despite me removing the answer column from the migrations and removing all occurrences of answer in the Possibility model.
The Possibility Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Possibility extends Model
{
protected $fillable = ['question_id', 'correct'];
public function question()
{
return $this->belongsTo('App\Models\Question');
}
}
The Possibility migration
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePossibilitiesTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('possibilities', function (Blueprint $table) {
$table->bigIncrements('id');
// $table->string('answer');
$table->boolean('correct')->default(false);
$table->unsignedBigInteger('question_id');
$table
->foreign('question_id')
->references('id')
->on('questions')
->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('possibilities');
}
}
When I use the Model::create without using factory I get don't have the attribute as expected.
$possibility = Possibility::create([
'question_id' => $question->id,
'correct' => 0
]);
EDIT
Missed something extremely obvious, the PossibilityFactor definition itself. Thanks to #lagbox for pointing it out.

You made a factory for Possibility. In that you are defining the array of attributes to be used. You are returning an array with a key for answer. Remove that.

This is probably what you need
factory(Possibility::class, $random_num)->create([
'question_id' => $question->id,
])->each(function ($item, $index) use ($correct_answer) {
if ($index === $correct_answer) {
$item->correct = true;
} else {
$item->correct = false;
}
})

Related

How to add information to a pivot table in laravel?

I have a many:many relation between student and course.
Here are my models and my pivot table:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Student extends Model
{
use HasFactory;
protected $fillable = [
'name',
'surname',
'age',
'tdah',
'description',
'hobbies',
];
/* public function course(){
return $this->belongsTo(Course::class);
} */
public function data(){
return $this->hasMany(Datum::class, 'student_id', 'id');
}
public function configurations(){
return $this->hasMany(Configuration::class, 'student_id', 'id');
}
public function courses(){
return $this->belongsToMany(Course::class, 'course_student', 'student_id', 'course_id');
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Course extends Model
{
use HasFactory;
protected $fillable = [
'name',
'academic_course_id',
'user_id',
];
public function user()
{
return $this->belongsTo(User::class);
}
public function academicCourse()
{
return $this->belongsTo(AcademicCourse::class);
}
public function planification(){
return $this->hasOne(Planification::class, 'course_id', 'id');
}
public function subjects(){
return $this->hasMany(Subject::class, 'course_id', 'id');
}
/* public function students(){
return $this->hasMany(Student::class, 'course_id', 'id');
} */
public function students(){
return $this->belongsToMany(Student::class, 'course_student', 'course_id', 'student_id');
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('course_student', function (Blueprint $table) {
$table->id();
$table->foreignId('course_id')->constrained()->onDelete('cascade');
$table->foreignId('student_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('course_student');
}
};
I'd like to create a student that belongs to a group. I know how to create a student and a group, but I'd like to create a register for that student in my course_student pivot table. My StudentController method store is:
public function store(Request $request)
{
Student::create($request->all());
$courses = Course::all();
$academicCourses = AcademicCourse::all();
return view('course.index', compact('courses', 'academicCourses'));
}
It creates a new student but I don`t know how to create a new register for the pivot table.
I thought about doing something like:
course_student::create(request)
in my store method but I think it isn`t that way.
In Laravel you can do it by using attach function, Attach() function has two parameters :
1- the another foreign key in you case attach(course_id).
2-(this is optional) array for any another columns inside pivot table for example :
attach(course_id,['users_id'=>$usersId])
In your case you will create the records inside the pivot table (course_student) via the object from student model .
$student->courses()->attach($course_id);
note :
there is another method which does the opposite detach() but you need to use it carefully if you write it without any parameter it will remove all the records inside (course_student) that belong to the student object so specify the course that you want to remove it by that way :
$student->courses()->detach($course_id);
I hope that will help you ...
$student->courses()->attach($courseId);
https://laravel.com/docs/9.x/eloquent-relationships#updating-many-to-many-relationships
Edit :
You can use attach() method on the relationship. In your code it would look like this :
public function store(Request $request, Course $course) //add parameter course to know which course needs to be attached
{
$newStudent = Student::create($request->all()); // create() returns instance of created Student
$newStudent->courses()->attach($course->id);
$courses = Course::all();
$academicCourses = AcademicCourse::all();
return view('course.index', compact('courses', 'academicCourses'));
}

How to correctly use relationships with() in Laravel?

I just wanted to clarify using the relationship in tables. Right now, I wanted to fetch records of designation names from designation_id in employees table.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\{
Designations,
Positions
};
class Employees extends Model
{
use HasFactory;
protected $table = 'employees';
protected $primaryKey = 'id';
public $timestamps = true;
protected $casts = [
'designation_id' => 'array',
'position_id' => 'array',
'basic_pay' => 'decimal:2',
];
protected $dates = ['created_at', 'updated_at'];
protected $fillable = [
'first_name',
'last_name',
'designation_id',
'position_id',
'basic_pay',
];
public function designations()
{
return $this->hasMany(Designations::class, 'id', 'designation_id');
}
public function positions()
{
return $this->hasMany(Positions::class, 'id', 'position_id');
}
}
Here's my designation model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\Employees;
class Designations extends Model
{
use HasFactory;
protected $table = 'designations';
protected $primaryKey = 'id';
public $timestamps = true;
protected $dates = ['created_at', 'updated_at'];
protected $fillable = [
'name',
'description'
];
public function employees()
{
return $this->belongsTo(Employees::class, 'designation_id');
}
}
Here's my EmployeeController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\{
Employees,
Designations
};
class EmployeesController extends Controller
{
public function index()
{
$employees = Employees::with('designations', 'positions')->get();
return array_reverse($employees);
}
}
I checked my api url, http://localhost:8000/api/employees and got this error:
SQLSTATE[HY093]: Invalid parameter number (SQL: select * from designationswheredesignations.id in (52))
I would recommend you to install phpstorm, it gives you hints of function parameters and you won't have this kind of issues anymore.
correct format is:
return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
in your designations model:
public function DesignationNames()
{
return $this->hasMany(\App\Models\Employees::class, 'designation_id', 'id');
}
When you retrieve them in your controller you need to use the with() method as:
Designations::with('DesignationNames')->get();
And then to access properties in the related employee collection you would need to:
$designation->DesignationNames->DesignationProperty
Your relationships parameters are wrong. It's
hasMany(class, foreignKey, relatedPrimaryKey)
# Employee
public function designations()
{
return $this->hasMany(Designations::class, 'employee_id', 'id');
}
public function positions()
{
return $this->hasMany(Positions::class, 'employee_id', 'id');
}
If you're eager loading more than 1 relationship, use array notation.
Also, $employees will be an instance of a Collection, so you can't use it as an argument to array_reverse.
You can either use collection methods to achieve the same result, or use $employees->all() to get the underlying array.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Employees;
class EmployeesController extends Controller
{
public function index()
{
$employees = Employees::with(['designations', 'positions'])->get();
return $employees->reverse()->values()->all();
// OR
return array_reverse($employees->all());
}
}
This is assuming your tables have a structure like this:
Schema::create('employees', function (Blueprint $table) {
$table->id();
...
});
Schema::create('designations', function (Blueprint $table) {
$table->id();
$table->foreignId('employee_id')->constained('employees');
...
});
Schema::create('positions', function (Blueprint $table){
$table->id();
$table->foreignId('employee_id')->constained('employees');
...
});
Since you're using increments instead of id(), the code has to be a little different.
Schema::create('employees', function (Blueprint $table) {
$table->increments('id')->unique();
...
});
Schema::create('designations', function (Blueprint $table) {
$table->increments('id')->unique();
$table->unsignedInteger('employee_id');
$table->foreign('employee_id')->references('id')->on('employees');
...
});
Schema::create('positions', function (Blueprint $table){
$table->increments('id')->unique();
$table->unsignedInteger('employee_id');
$table->foreign('employee_id')->references('id')->on('employees');
...
});

Laravel API: How to make a default image in a table instead of NULL

I am currently working on having a default image in a table instead of null. I already have an API that will put an image in that specific column (web_banner_profile) which is a POST method and a DELETE method that will make that column NULL, all of which using postman. I want to know how I can put a default image on all of the webinars table in the web_banner_profile.
This is the Banner Upload Controller:
<?php
namespace App\Http\Controllers;
use App\Models\Banner;
use Illuminate\Http\Request;
// use Illuminate\Support\Facades\Validator;
class BannerUploadController extends Controller
{
public function FileUpload(Request $request, $id)
{
$uploaded_files = $request->file->store('public/uploads/');
$webinar = Banner::find($id);
$webinar->web_banner_profile = $request->file->hashName();
$results = $webinar->save();
if($results){
return ["result"=>"Image Added"];
}else{
return ["result"=>"Image Not Added"];
}
return ["result"=>"$uploaded_files"];
}
public function DeleteBanner($id)
{
$webinar = Banner::find($id);
if(is_null($webinar)){
return response()->json('Record not found!', 401);
}
$webinar->update(['web_banner_profile' => null]);
return response('Banner Deleted', 200);
}
}
This is the webinar table migration:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateWebinarTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('webinar', function (Blueprint $table) {
$table->id();
$table->string('web_title');
$table->text('web_description');
$table->dateTime('web_start_date_time')->nullable();
$table->dateTime('web_end_date_time')->nullable();
$table->string('status')->nullable();
$table->string('remarks')->nullable();
$table->string('web_banner_profile')->nullable();
$table->bigInteger('created_by')->unsigned()->nullable();
$table->bigInteger('updated_by')->unsigned()->nullable();
$table->string('web_link')->nullable();
$table->timestamps();
});
Schema::table('webinar', function(Blueprint $table) {
$table->foreign('created_by')->references('id')->on('admins');
$table->foreign('updated_by')->references('id')->on('admins');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('webinar');
}
}
Any type of help/suggestion would be greatly appreciated. Thank you in advance!
I would consider using one of the Eloquent model events, specifically the created event. Something like the following:
class Webinar extends Model
{
protected static function booted()
{
static::created(function ($webinar) {
$webinar->update(['web_banner_profile' => 'your-image.jpg']);
});
}
}
Then whenever a new Webinar is created, the created event will be triggered and your default web_banner_profile value will be added to that record.
You don't have to hard code the value your-image.jpg, you could obtain it from a config of env file if you didn't want it in your code base to (arguably) make changing the value easier.

Use trait in Laravel database migration

My superior told me I could create a seeder-like trait which I can then use inside a migration. When the migration is being run on the server the database automatically gets seeded while migrating instead of running a separate seeder after the migration succeeded.
Now I created a trait which I included in the database migration.
<?php
namespace App\Services;
use App\Models\Module\Module;
use App\Models\Plan\Plan;
/**
* PlanAndModuleGenerator class.
*/
trait PlanAndModuleGenerator
{
private static $plans = [
'free',
'basic',
'premium',
];
public function up()
{
foreach ($this->plans as $planName) {
// Get or create Plan.
$plan = Plan::create([
'machine_name' => '',
'name' => $planName
]);
}
}
}
My superior told me they did this before, but I can't find anything like this on the internet. I included my trait like this.
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use App\Services\PlanAndModuleGenerator;
class ModulePlan extends Migration
{
use PlanAndModuleGenerator;
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('module_plan', function (Blueprint $table) {
$table->unsignedInteger('plan_id');
$table->unsignedInteger('module_id');
$table->foreign('plan_id')->references('id')->on('plans');
$table->foreign('module_id')->references('id')->on('modules');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('module_plan');
}
}
When I run the migration the up function inside my trait isn't executed. I know this because the Plan table isn't seeded yet. Any ideas on how I might tackle this? As my superior isn't in the office the upcoming days and I can't access the repository where they did this before.
Besides this, can anyone tell me how I can properly debug this trait? The way I am doing this now, just running the migration and wait for errors, seems a bit cumbersome.
I don't see any reason for this trait at all, but if you really want to use it you would need to alias the up method of the trait and then call that in your up method of the migration:
class ModulePlan extends Migration
{
use PlanAndModuleGenerator { up as traitUp; }
public function up()
{
...
$this->traitUp();
}
}
It would be better to just use a different name for the method in the Trait, but there is no reason for this trait in the first place it would seem.
It will definitly not work because you have two up methods, one in your trait and the one in your migration. You need to delete the up in your migration and use the one in your trait as shown below. Here is the trait
<?php
namespace App\Services;
use App\Models\Plan\Plan;
use App\Models\Module\Module;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
/**
* PlanAndModuleGenerator class.
*/
trait PlanAndModuleGenerator
{
private static $plans = [
'free',
'basic',
'premium',
];
public function up()
{
$createmigration = Schema::create('module_plan', function (Blueprint $table) {
$table->unsignedInteger('plan_id');
$table->unsignedInteger('module_id');
$table->foreign('plan_id')->references('id')->on('plans');
$table->foreign('module_id')->references('id')->on('modules');
});
if ($createmigration) {
foreach ($this->plans as $planName) {
// Get or create Plan.
$plan = Plan::create([
'machine_name' => '',
'name' => $planName
]);
}
}
}
}
Confirm first that the migration was created before creating your Plan.
Here is how your migration should look like
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use App\Services\PlanAndModuleGenerator;
class ModulePlan extends Migration
{
use PlanAndModuleGenerator;
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('module_plan');
}
}
Hope this will help

Why the related Answer model doesn't get project_id?

I have the next models:
Project <(Many to Many)> Experiment (One to Many)> Question (One to Many)> Answer
When I try Project::with('experiments.questions.answers')->find(1)
as result, I get answers not only from project with id: 1, but from others too.
Structure Answer model:
<?php
use App\Models\Answer;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAnswersTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('answers', function (Blueprint $table) {
$table->increments('id');
$table->string('answer')->nullable();
$table->integer('confidence_score')->unsigned()->default(0);
$table->integer('state')->unsigned()->default(Answer::READY);
$table->integer('element_id')->unsigned();
$table->integer('question_id')->unsigned();
$table->integer('project_id')->unsigned();
$table->timestamps();
$table->foreign('question_id')->references('id')->on('questions')->onDelete('cascade');
$table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::table('answers', function ($table) {
$table->dropForeign(['question_id']);
$table->dropForeign(['project_id']);
});
Schema::drop('answers');
}
}
If I add the next condition, it is work:
Project::with(['experiments.questions.answers' => function($query) {$query->where('project_id', 1);}])->find(1)
But how can I remove it from the code and to make it global?
I can't figure out why this selection touch other projects, however, if you want to create a handy shortcut to fetch project with relations - create a method in a Model or your Repository:
public function findWithExperiments($id) {
$project = Project::find($id);
$project->load(['experiments.questions.answers' => function ($query) use ($id) {
$query->where('project_id', $id);
}]);
return $project;
}
Try this code
Project::with('experiments.questions.answers')->find([1])

Resources