LARAVEL Eloquent: BelongsTo Relationship - Bulk update - php

Imagine I want to lend multiple books to a customer.
A customer can loan many books (hasMany) but a book can only be lend to one customer (BelongsTo).
I want to change the BelongsTo side of the relationship for multiple books in one go.
Laravel Documentation says to use the associate() function. But as far as I can see, that's for changing one object at a time. So I have to use a loop as in massLoanOne. This generates a lot of queries (n + 1).
Another way of doing this is by updating the customer_id on the Book model directly, as in massLoanTwo.
This generates only 2 queries. But this is not the proper Object Oriented way.
Is there a better way to update the BelongsTo side of this one-to-many (reverse) relationship bij using the Eloquent magic?
<?php
public function massLoanOne(MassLoanCustomerRequest $request)
{
foreach ($request->ids as $id) {
Book::find($id)->customer()->associate(Customer::find($request->customer_id))->save();
}
return response()->noContent();
}
public function massLoanTwo(MassLoanCustomerRequest $request)
{
Book::whereIn('id', request('ids'))->update(['customer_id' => $request->customer_id]);
return response()->noContent();
}
?>

Related

Select other data in another table using eloquent

I have cases, cases_judges, judges and witness tables.
A case can have many judges and witness
I want to be able to select all the judges and witness associated to a case
I don't know how to create a relationship between case and judges because their is no direct link between judges and case. Cases_judges is the only intermediary between case and judge. I also tried using Hasmanythrough relationship but without success
Cases model
public function case_judges(){
return $this->hasMany(Cases_judges::class,'case_id','id');
}
public function witness(){
return $this->hasMany(Witness::class,'case_id','id');
}
public function judges(){
return $this->hasManyThrough(Judges::class, Case_judge::class,'judge_id','id');
}
Judges model
public function judges(){
return $this->hasMany(Case_judge::class,'case_id','id');
}
Cases_judge model
public function case(){
return $this->belongsTo(Cases::class);
}
Cases Controller
My case controller is something like this
public function index()
{
return Cases::with('case_judge','witness')->get();
}
This fiddle (http://sqlfiddle.com/#!9/217dcb/2/0)shows how I would have done it using raw query but I want to do it the eloquent way
To arrive at the solution, first we have to uncover what really is the relationship between your Case and Judge models.
The simplified diagram highlights the connection between your Case and Judge models via an intermediate CaseJudge model. Indeed, a Case really can have multiple Judges. However, we must not miss out the fact that a Judge can handle multiple Cases.
From this, we can conclude that Case and Judge models have a many-to-many relationship.
To establish a many-to-many relationship between models in Laravel, you must use belongsToMany relationship method, as opposed to hasManyThrough which only applies to one-to-many relationship.
class Cases extends Model {
...
public function judges() {
return $this->belongsToMany(Judges::class, 'case_judges', 'case_id', 'judge_id');
}
}
Here, belongsToMany has four arguments:
Model that has the many-to-many relationship with our Cases model.
Intermediate table connecting tables cases and judges.
Foregin key on intermediate table that connects to table cases.
Foregin key on intermediate table that connects to table judges.
When all is set and done, running:
return Cases::with(['judges', 'witness'])->get();
will now return what you want — a collection of cases, along with witnesses and judges associated to their respective cases.
As a side note: you might want to follow Laravel's conventions on naming things, to avoid running into weird default Eloquent behaviors as consequence, and enjoy any conveniences it brings when you conform to these conventions.
I think cases and judges are 'Many to Many' relationship with intermediate table cases_judges.
You can follow this document to setup this : https://laravel.com/docs/9.x/eloquent-relationships#many-to-many

How do I handle multiple pivot relationships in Laravel?

In my app, you can create lists of roles that are attached to contacts. So you can assign the contact "Bob" the roles of "Gardener" and "Pet Sitter". Then you can create the list "People" and add "Gardener (Bob)" and "Pet Sitter (Bob)" to it.
I have the following tables:
contacts
id
name
roles
id
name
contact_role (pivot)
id
contact_id
role_id
lists
id
name
contact_role_list (pivot)
id
contact_role_id
list_id
Everything was working smoothly until the second pivot table linked to the first pivot table. My pivot tables are (currently) not having any models so I'm not sure if there is a built-in feature to tackle this in Laravel or if I need to think differently.
I currently have this in my List model:
public function list_roles(): BelongsToMany
{
return $this->belongsToMany(XYZ::class, 'contact_role_list', 'list_id', 'contact_role_id');
}
Is this even close? What do I put where it says XYZ::class?
Ok, so the below is doing what I want, but is there an even better way to do it? The key to solving my problem was to create a Model for ContactRole and changing extends Model to extends Pivot.
I placed this in my List Model:
public function list_roles(): BelongsToMany
{
return $this->belongsToMany(ContactRole::class, 'contact_role_list', 'list_id', 'contact_role_id');
}
And this in my ContactRole Model:
public function contact(): BelongsTo
{
return $this->belongsTo(Contact::class);
}
Now I could reach the contact data by using something like this: List::first()->contact_roles->first()->contact
Any way to use with, pivot or similar to tidy this up even more? Thanks!
I like to approach these issues in terms of Models rather than pivots. I think many new Developers in Laravel get over obsessed with what's going on in the Database which is fine, but theres a lot of Magic going on so you can write very simple code that does a lot of Heavy lifting, so that being said if I fully understand your problem
You have a Contacts Model
This model can have many roles
so in your contacts Model you need a role relationship
public function roles()
{
return $this->belongsToMany(Roles::class);
}
next of course you have a role Model (pun intended)
your each role can have many list
public function lists()
{
return $this->hasMany(List::class)
}
then the idea is now that you have roles on contacts and lists on roles you should be able to have many lists through contact
public function lists()
{
return $this->hasManyThrough(List::class, Role::class);
}
I've done similar things before and for your description it seems like that's the approach you might need to take.

Append data from another model in a model (laravel 5.4)

I want to append the count of data from a table in my database but I am having a problem with the relationship.
I have 3 Models
Voucher model:
vouchers table
VoucherSerial Model:
voucher_serials table
Each voucher will have many serials
UserVoucher Model:
user_vouchers table
When the user redeemed the voucher it will be stored in user_vouchers table. I also had defined the relationship for all the Models
In Voucher.php I want to append the count of the voucher redeemed by the user.
public function getVoucherRedeemedAttribute()
{
//query to append the voucher redeemed count for each voucher
}
I've tried so many solution but mostly I got errors. My biggest problem is because to count the voucher redeemed for each voucher based on user_vouches table but each voucher has many serial_id which i want to count as the same voucher
I know my explanation regarding the question is bad but I need some help regarding this. Hope someone can help me.
Thank you so much in advance
You can add the number of related objects to the result with the withCount method:
If you want to count the number of results from a relationship without
actually loading them you may use the withCount method, which will
place a {relation}_count column on your resulting models. For example:
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
Source: https://laravel.com/docs/5.8/eloquent-relationships#counting-related-models
If I understand your problem correctly, you want to get the count one level deeper (number of vouchers instead of number of voucher serials). You might be able to use a hasManyThrough relationship:
The "has-many-through" relationship provides a convenient shortcut for accessing distant relations via an intermediate relation. For example, a Country model might have many Post models through an intermediate User model. In this example, you could easily gather all blog posts for a given country.
Source: https://laravel.com/docs/5.8/eloquent-relationships#has-many-through
Combined it will look something like this:
class User {
//...
public function vouchers()
{
return $this->hasManyThrough(App\Voucher::class, App\VoucherSerial::class);
}
}
// Controller
$user->withCount('vouchers');
I've never actually used withCount and hasManyThrough together but it should work ;)

Laravel Eager Loading, multiple same hasOne relations

I have 2 simple models. First, one is called Builds and the second one is called SlotOptions. Each build can have like 5 assigned slots.
class BuildDB extends Model
And has 5 such relations slot1-5 and id changes to slot1-5_id
public function slot1()
{
return $this->hasOne('\App\SlotOptions', 'id', 'slot1_id');
}
In the controller I call it such way;
BuildDB::with([ 'slot1', 'slot2', 'slot3', 'slot4', 'slot5'])->find(5);
\App\SlotOptions model doesn't contain any extra coding.
This generates 5 "same" queries. - atm the eager loading would work if I get a list of builds and each slot would have whereIn clause, is it possible to have it a one big wherein the clause, or does it require to change the DB schema.
It's not possible to optimize eager loading in this case.
I recommend that you change your database schema to a many-to-many relationship.
This design is more flexible, it allows you to easily add more slots in the future.
Create a pivot table named build_slot_option with these columns: build_id, slot_option_id
Add an additional column if you want to number/order the slots.
Then define a BelongsToMany relationship:
class BuildDB extends Model
{
public function slots()
{
return $this->belongsToMany(
SlotOptions::class, 'build_slot_option', 'build_id', 'slot_option_id'
);
}
}
BuildDB::with('slots')->find(5);

How to make Query between two tables in laravel

Hello I want to ask for how to make query in 2 tables to get the summary of value from table payments where type_id = id from types table and this is tables structure
this is the charts code
$chartـin_contract = Charts::create('bar', 'highcharts')
->title('My nice chart')
->labels($value_type->type)
->values([5,10])
->dimensions(0,500);
in labels i get the values from database but did't work with me
and this is the direct query
$value_type = DB::select('select type, value from payments,types where payments.`type_id` = types.id');
I want view the data that came from database in chart I am using laravel chart package
Please review Eloquent Relationships in the Laravel docs - https://laravel.com/docs/5.5/eloquent-relationships
The relationship between the two tables is defined (as in a one-to-one, one-to-many or many-to-many relationship) and then you will be able to query those relationships.
For this you need to establish the Relationship in tables.
NOTE: I am assuming that your task may have multiple payments
In your tasks model its one to many relation.
Task.php (Model add the following)
public function payments(){
return $this->hasMany(Payments::class);
}
And in your payments model its many to one relation.
Payments.php (Assuming)
public function task(){
return $this->belongsTo(Task::class);
}
Think now you want to get all the payments for a task. Let me show the eg: in command line
php artisan tinker
$task = App\Task::where('id',1)->get(); //Think that you have first task
$task->payments; //You can call the relation ship established condition function
Think you are browising for payment and want a task then
php artisan tinker
$payment = App\Payment::where('id',1)->get();//find the payment with id 1, just for the sake of example
$payment->task; //This will be the task() function that you have written int the model

Categories