Laravel - Factory seeding unique data on pivot table - laravel

Event::factory(5)
->hasAttached(
Team::factory()->count($this->faker()->numberBetween(0, 60)),
[
'team_name' => $this->faker()->unique()->name,
'score' => $this->faker()->numberBetween(0, 50)
],
'participants'
)
->create([
'user_id' => $user,
'quiz_id' => $quiz
]);
The above snippet of code creates 5 events for said $user using said $quiz. It will have a random amount of participants which is a pivot table (Team and Event). On that pivot table there is a team_name and score column. Because teams can change their team name we want to know the team name at the time of participating and also the score in which they got for the event.
With the current code, because $this->faker()->numberBetween(0, 60), $this->faker()->unique()->name and $this->faker()->numberBetween(0, 50) are not evaluated within an iteration, all the pivot table data is the same.
How can I make this data different per pivot row?
TIA

Figured it out;
Event::factory(5)
->hasAttached(
Team::factory()->count($this->faker()->numberBetween(0, 60)),
function() {
return [
'team_name' => $this->faker()->unique()->name,
'score' => $this->faker()->numberBetween(0, 50)
];
},
'participants'
)
->create([
'user_id' => $user,
'quiz_id' => $quiz
]);

Related

make better code in laravel many to many relation

hi i wrote this code and it works just fine but i think its not the best way to do it!
i want to get all the jobs for 1 company.
each company can have many addresses and each address can have many jobs
here is my code:
$company = Company::find($id)->with('addresses.jobDetails.job')->first();
$jobs = [];
foreach ($company->addresses as $address) {
foreach ($address->jobDetails as $detail) {
array_push($jobs, [
'id' => $detail->job->id,
'title' => $detail->job->title,
'country' => $detail->job->country,
'city' => $detail->job->city,
'type' => $detail->job->type,
'work_types' => JobType::where('job_id',$detail->job->id)->pluck('title'),
'income' => $detail->income,
]);
}
}
return $jobs;
can anyone help me to change this to better code please
thank you in advance
You do the opposite and start with JobDetails
$jobDetails = JobDetail::whereHas('address.company', function($companyQuery) use($id) {
$companyQuery->where('id', $id);
})->whereHas('jobs', function($jobQuery) {
$jobQuery->where('is_active', 1);
})->with('jobs')->get();
foreach ($jobDetails as $detail) {
array_push($jobs, [
'id' => $detail->job->id,
'title' => $detail->job->title,
'country' => $detail->job->country,
'city' => $detail->job->city,
'type' => $detail->job->type,
'work_types' => JobType::where('job_id',$detail->job->id)->pluck('title'),
'income' => $detail->income,
]);
}
return $jobs;
EDIT:
In your query
Company::find($id)->with('addresses.jobDetails.job')->first();
You run 4 queries with eager loading. one for each model. You can check in the result that you got that all the data is present in the variable $company.
The example I gave you it runs only two queries, the first one (job_details) will use joins to filter the Job results by the id of the companies table (you can make it faster by using the field company_id in the addresses table)
The second one is for the jobs relation using eager loading.

Laravel 8 adding data to pivot table with seeder using factory

I have Product and Category model and I've crated a many to many relationships with a pivot table named category_product.
I have also created 2 factories for category and product.
I am trying to add 100 products with 2 categories for each. Somehow it ads 200 records to my categories table. What I am doing wrong?
My CategoryFactory class:
public function definition()
{
return [
'name' => $this->faker->numberBetween(1, 12),
'created_at' => Carbon::now()->format('Y-m-d H:i:s'),
'updated_at' => Carbon::now()->format('Y-m-d H:i:s')
];
}
And my ProductFactory class is looking like that:
public function definition()
{
return [
'name' => $this->faker->name,
'description' => $this->faker->text,
'image' => 'image-' . rand(1, 10) . '.jpg',
'price' => $this->faker->numberBetween(300, 20000),
'code' => $this->faker->regexify('[A-Z0-9]{12}'),
'created_at' => Carbon::now()->format('Y-m-d H:i:s'),
'updated_at' => Carbon::now()->format('Y-m-d H:i:s')
];
}
I have these in my ProductSeeder
Product::factory(100)->has(Category::factory()->count(2))->create();
My pivot table migration:
Schema::create('category_product', function (Blueprint $table) {
$table->id();
$table->foreignId('product_id')->constrained();
$table->foreignId('category_id')->constrained();
$table->timestamps();
});
In your factory seeder, you are essentially saying "for every product, create two new categories, and then link them to the product", which is why you are ending up with 200 categories. If you look in your pivot table, you'll see you also have 200 entries, but no category_id will be the same.
If, I am assuming, you want 100 products, 2 categories and a linkage between them, your seeder should be more like
$categories= Category::factory()->count(2)->create();
$products = Product::factory()
->count(100)
->hasAttached($categories)
->create();
You'll end up with 100 products, 2 categories, and 200 pivot entries where the category_id has a cardinality of 2 (i.e. ids 1 and 2)

laravel 7 Validation unique multiple columns

how to viladate multiple columns
table name "exam_starts"
columns
id
exam_id
exam_multi_id
student_id
exam_class_id
exam_code_id
Status
class Student extends Model
{
// exam
public function exams(){
return $this->belongsToMany('App\Exam','exam_starts','student_id','exam_id')
->withPivot(['id','exam_multi_id','exam_class_id','exam_code_id','Attend','pass','Status'])-
>withTimestamps();
}
}
how to unique columns student_id and exam_class_id in table exam_starts ?
My Try in controller
class ExamClassController extends Controller
{
$this->validate($request, [
'exam_class_id' => [
'required',
'integer',
'unique:exam_startss,exam_class_id,'.$request->exam_class_id .',student_id' ,
],
'student_id' => 'bail|required|integer',
'exam_start_id' => 'bail|required|integer',
'exam_id' => 'bail|required|integer',
'exam_multi_id' => 'bail|required|integer',
'code' => 'nullable|string',
'student' => 'bail|required|string',
]);
}
this mysql resulte
(SQL: select count(*) as aggregate from `exam_starts` where `exam_class_id` = 1 and `student_id` <> 1)
Use Unique rule class and you can pass multiple wheres to it, to make an unique constraint that takes two columns.
(new Unique('exam_starts'))
->where('student_id ', $request->student_id)
->where('exam_class_id', $request->exam_class_id);
Set it into validation like so
'exam_class_id' => [
'required',
'integer',
(new Unique('exam_starts'))
->where('student_id ', $request->student_id)
->where('exam_class_id', $request->exam_class_id),
],

correct value instead of array

scenario is crm with tables account, account_contact, contact and account_contact_role. The latter contains roles like 'project lead' or 'account manager' for the combos defined in the junction table.
My challenge is the account view, that is listing also the connected persons with their roles. I want my grid to show: Doe | John | employee.
The problem is now when the contact has 2+ entries in the junction table. How can I print the correct role for the row? As you can see in the code I solved it the static way which shows only 1 out of n times the correct value. Tried it with inner join without success. Is it a problem of the search in the model or with the access in the view?
the relation from the account model to the contacts:
public function getContacts($role = null)
{
// many-to-many
return $this->hasMany(ContactRecord::className(), ['id' => 'contact_id'])
->via('accountContacts')
->innerJoinWith(['accountContacts.role'])
->andWhere(['account_contact.account_id' => $this->id])
->andWhere(['account_contact_role.type' => $role])
;
}
the view
<?= \yii\grid\GridView::widget([
'dataProvider' => new \yii\data\ActiveDataProvider([
'query' => $model->getContacts('internal'),
'pagination' => false
]),
'columns' => [
'lastname',
'firstname',
[
'label' => 'Role',
'attribute' => 'accountContacts.0.role.name',
],
[
'class' => \yii\grid\ActionColumn::className(),
'controller' => 'contacts',
'header' => Html::a('<i class="glyphicon glyphicon-plus"></i> Add New', ['contact-records/create', 'account_id' => $model->id]),
'template' => '{update}{delete}',
]
]
]); ?>
defined relations are:
account has many accountContacts has one contact
accountContacts has one accountContactRole
Many thanks in advance!
You are showing account's contacts, so you have to list from Contact model.
Inside Contact model (or Contact ActiveQuery file):
public static function queryContactsFromAccountAndRole($account, $role = null)
{
// many-to-many
return ContactRecord::find()->innerJoinWith(['accountContacts' => function($q) use($account, $role) {
$q->innerJoinWith(['accountContactsRole'])
->andWhere(['account_contact.account_id' => $account->id])
->andWhere(['account_contact_role.type' => $role]);
}])
->andWhere(['account_contact.account_id' => $account->id]);
}
Now you have one record for each contact and the gridview will show all contacts.

Laravel 5.2 Many to Many withPivot save

I'm having problem on this many to many. My code is
$purchase_order = PurchaseOrder::find(1);
$item = Item::find(1);
$purchase_order->items()->save($item);
This one works well. I have a pivot table name items_purchase_order it creates
item_id 1 and purchase_order_id 1
The problem is the pivotColumn on how to add it.
$array = [
'qty' => 4,
'unit_price' => '23'
];
My PurchaseOrder Model
return $this->belongsToMany('App\Item', 'items_purchase_orders', 'purchase_order_id', 'item_id')->withPivot('qty','unit_price');
I have tried other examples on here but to no avail. Like this one
$purchase_order = PurchaseOrder::find(1);
$item = new Item($array);
$purchase_order->items()->save($item);
This becomes ambiguous because there is no item id. The items are already created and the purchase order is the one I'm going to create. I have tried the reverse one with the Item Model.
return $this->belongsToMany('App\PurchaseOrder', 'items_purchase_orders', 'item_id', 'purchase_order_id')->withPivot('qty','unit_price');
//pivot table added with purchase_order columns
$array = [
'po_number' => '454jk', // I minimized it to one column. Purchase Orders Column
'qty' => 4, // Pivot Column
'unit_price' => '23' // Pivot Column
];
$item = Item::find(1);
$purchase_order= new PurchaseOrder($array);
$item->purchaseOrder()->save($purchase_order);
Still it doesn't work.
I think I found this 1
$shop->products()->attach(1, ['products_amount' => 100, 'price' => 49.99]); Yeah it work! Reference http://laraveldaily.com/pivot-tables-and-many-to-many-relationships/

Resources