Some help with many to many relationships in Laravel:
Using the example for roles and users - basically:
a table for all the roles
a table for the users
and table with user_id and role_id.
I want to add to the third table, eg Year. basically the pivot table will have user_id, role_id and year_id.
I want to be able to make a query to pull for example all users assigned a specific role in a specific year. Eg All users with role_id = 2, and year_id = 1.
Any help will be appreciated
Before answering, I would like to suggest you not to put year on database like this.
All your tables should have created_at and updated_at which should be enough for that.
To filter users like you want. You could do this:
// This queries all users that were assigned to 'admin' role within 2013.
User::join('role_users', 'role_users.user_id', '=', 'users.id')
->join('roles', 'roles.id', '=', 'role_users.role_id')
->where('roles.name', '=', 'admin')
->where(DB::raw('YEAR(role_users.created_at)', '=', '2013')
->get();
This example may not be the precise query you are looking for, but should be enough for you to come up with it.
The best way to achieve a three way relation with Eloquent is to create a model for the table representing this relation. Pivot tables is meant to be used for two way relations.
You could have then a table called roles_users_year which could have data related to this 3 way relation like a timestamp or whatever...
A very late answer to a very old question, but Laravel has supported additional intermediate (pivot) table columns of at least Laravel 5.1 judging from the documentation, which hasn't changed at least through Laravel 6.x.
You can describe these extra columns when defining your many-to-many relationship:
return $this->belongsToMany(Role::class)->withPivot('column1', 'column2');
or in your case, the below would also do the job:
return $this->belongsToMany(Role::class)->withTimestamps();
which you can then access via the pivot attribute on your model:
$user = User::find(1);
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}
Note that the pivot attribute is on the distant relationship model (a single Role) and not on the relationship itself.
To get all the Roles assigned to Users in any given year, you might create a special relationship:
// User.php
public function rolesInYear($year) {
return $this->belongsToMany(Role::class)
->wherePivot('created_at', '>=', Carbon::create($year))
->wherePivot('created_at', '<', Carbon::create($year + 1));
}
Related
I have something like this:
Table 1: Training Name, created_at, user_id (Plan_Treninga)
Table 2: user_id, created_at, expire_at (InvoiceUser)
I want to pull all from Table 1 where created_at is between Table 2 created_at and expire_at.
This is something what i am trying to..
$plan = Plan_Treninga::whereBetween(function($q) use ($id){
$inv = InvoiceUser::where([
["user_id",$id],
["status","paid"],
])->latest("id")->first();
})
I haven't finished it yet, but my brain stopped working so I have to ask here.
If I understand what you want clearly is. you want to query all from table 1 which created exist between table 2 created and expire_at right? if so you can use where exist query to achieve this.
// assume your table name is plan_treningas & invoice_users
Plan_Treninga::whereExists(function ($query) {
$query->select(DB::raw(1))
->from('invoice_users')
->whereRaw('plan_treningas.created_at BETWEEN invoice_users.created_at AND invoice_users.expire_at'); // add more query depend your logic
})->get();
for more you can take a look at docs
or if you want to use raw query
SELECT
*
FROM plan_treningas
WHERE EXISTS (
SELECT 1 FROM invoice_users WHERE plan_treningas.created_at BETWEEN invoice_users.created_at AND invoice_users.expire_at
)
Take a look at joins https://laravel.com/docs/7.x/queries#joins
I am not saying this is the exact solution but I have something similar that I have changed to point you in the right direction.
With joins you can do lots of things.
$results = DB::table('table1')
->join('table2', function ($join) {
$join->on('table1.user_id', '=', 'table2.user_id')
->where('table2.status', '=', 'paid')
->where('table2.created_at', '>', 'table1.created_at');
})
->get();
Also look at relationships. There is some good answers for setting up many to many relationships.
https://laravel.com/docs/7.x/eloquent-relationships#many-to-many
I have users and habits, and a habit_user table to join them.
I am querying like this:
$track = $h->userAnswers()->where('user_id', Auth::user()->id)->wherePivot('created_at', '=', Carbon\Carbon::now()->subDays($i))->first();
This is running in a loop that is counting back for 7 days. there is a record in the db that is created_at: 2018-10-23 04:48:44
In my habit model I have the method you'd expect:
public function userAnswers()
{
return $this->belongsToMany('App\Habit', 'habit_user_answers')->withTimestamps()->withPivot('answer_one', 'created_at')->orderBy('pivot_created_at', 'desc');
}
Why won't query get a record?
You are comparing the date time so only if both date and time is same, the query will throw a result.
You can compare dates like so:
wherePivot('created_at', '>=', Carbon\Carbon::now()->subDays($i)->startOfDay())->wherePivot('created_at', '<=', Carbon\Carbon::now()->subDays($i)->endOfDay())
First, I think you need to consider Laravel conventions about naming methods and properties.
I'll assume the following based on your structure that includes users and habits. So, we have a User model and a Habit model, a user belongsToMany habits and a habit belongsToMany users. Also the pivot table habit_user contains extra fields like answer_one, answer_created_at and timestamps.
If you want now to query the habits now you have two solutions:
1- using wherePivot()
auth()->user()->habits()->wherePivot('answer_created_at', today())->get();
auth()->user()->habits()->wherePivot('answer_one', '!=', 'something')->get();
2- using whereHas()
auth()->user()->whereHas('habits', function($query){
$query->where('pivot.answer_one', 'something');
})->get();
I have a user, group, group profile, group members, table.
Relation:
users belongs to many groups,
groups belongs to many users (name of function is groupMembers),
group has one groupProfile
I want to display the groups of a certain user with group profile and group member count.
Currently, I have this:
$userGroups = UserView::findOrFail($userId)
->groups()
->get();
I also have this: it return everything I want, except the fact that it is not user specific. (it returns all groups)
$userGroups = GroupView::with(['groupProfile'])
->withCount('groupMembers as groupMemberCount')
->get();
$userGroups = UserView::findOrFail($userId)
->groups()
->with(['groupProfile'])
->withCount('groupMembers as groupMemberCount')
->get()
Laravel relations return a query builder (if you call the function instead of the property) this means anything you can do with a collection of models you can do the same with the collection of related models.
in a Laravel application, I have a list of companies. Some of those are related to a subscriptions table in a 1 x n relation.
I want to order the companies in a way that the ones which have a subscription appear before the ones which have no subscription using just one db query.
Any idea is much appreciated!
thanks!
Laravel uses separate queries to eager load relationships. So if you want to do this you will need to join the tables and order by...
Something in the form...
$companies = App\Companies::join('subscriptions as sub', 'sub.company_id', '=', 'company.id')
->orderBy('sub.somefield', 'DESC')
->select(what you need)
->with('if you need the relation data');
You know you can also only query those records that have a relationship with
$companies = App\Companies::has('subscription')->get();
Maybe that is all you need... and
$companies = App\Companies::doesntHave('subscription')->get();
... returns the opposite where the company has no subscription...
I've got a many to many relationship between a student and an institution_contact.
students should only ever have two institution_contacts and I have an attribute on the pivot table named type to be set as 1 or 2.
So, my pivot table looks like this:
institution_contact_student: id, institution_contact_id, student_id, type
I've run into difficulty in deciding how to approach the issue of adding/updating the pivot table. Let's say I have 100 students and I want to assign them a contact with the type of 1.
My current solution is to delete the contact then add it:
$students = Student::all(); // the 100 students
$contactId = InstitutionContact::first()->id; // the contact
foreach ($students as $student) {
// remove existing contact
$student
->institutionContacts()
->newPivotStatement()
->where('type', 1)
->delete();
// add new contact
$student
->institutionContacts()
->attach([$contactId => ['type' => 1]]);
}
However, I'm thinking that this is going to hit the database twice for each student, right? So would I be better off creating a model for the pivot table and removing all entries that matched the student id and the type then simply adding the new ones? Or would creating a model for the pivot table be considered bad practice and is there a better way of accomplishing this that I've missed?
Please note the reason I'm not using sync is because I'm relying on the type attribute to maintain only two contacts per student. I'm not aware of a way to modify an existing pivot without causing issues to my two contacts per student requirement.
Edit:
Instead of creating a model I could run the following code to perform the delete using DB.
DB::table('institution_contact_student') // the pivot table
->whereIn('student_id', $studentIds)
->where('type', 1)
->delete();
If I have understood your question correctly then you can use the updateExistingPivot method for updating your pivot table.But first of course you have to define the pivot in your relationship. For instance,
public function institutionContacts(){
return $this->belongsToMany('institutionContact')->withPivot('type');
}
after this, all you have to do is use the following code:
$student
->institutionContacts()
->updateExistingPivot($contactId, ["type" => 1]);
Hope this helps.