Different relation by condition - laravel

I have the table followers, with:
id, user_id, follower_id, type
type = type of follow, if follow user = 0, page = 1, group = 1
I use the user_id to put the page_id and group_id too.
Now is the problem, I want to make different relations if type is different... If type = 0, will relate with users table and share table, if type = 1, will relate with pages table...
I'm try like this:
Model:
public function page_links()
{
return $this->hasMany(Link::class, 'page_id', 'user_id')->Select('links.id', 'links.title', 'links.photo', 'links.country', 'links.friendly_url', 'links.clicks', 'links.description', 'links.suggestions', 'links.count_comments', 'links.url', 'links.shares', 'links.page_id', 'links.tag_id', 'links.created_at')->where('sponsored', 0)->where('scheduled', 0)>where('status', 1)->take(3)->orderBy('id','desc');
}
public function user_links()
{
return $this->hasMany(Share::class, 'user_id', 'user_id')->Select('id', 'link_id', 'user_id', 'shared_in', 'content', 'created_at')->take(3)->orderBy('id', 'desc')->where('type', '=', 0);
}
public function scopeProfile($query) {
return $query
->when($this->type == 0, function($q){
return $q->with('user_links');
})
->when($this->type == 1, function($q){
return $q->with('page_links');
})
->when($this->type == 2, function($q){
return $q->with('group_links');
});
}
Controller:
$feed = Feed::Profile()->where('follower_id', auth()->user()->id)
->take(10)
->get();
But ALL, even the type 1 returns "user_links" relation. I don't know if the relation is correct...
Someone can help me?

Well, in my point of view, you should modify your migration.
Instead of have your table with :
id, user_id, follower_id, type
I'll do this :
id, user_id, page_id, group_id, follower_id, type
Don't do weird stuffs like that in your database, just add 2 fields and / or relations in your migration table. Set it to unsigned() AND nullable(), this way you'll be able to know quickly if there is a relation or not with each one of your page or group, and you won't have to make weird stuff to check for it :D

Related

How to improve where clause condition with unique of two columns combined?

I have two tables of User(id, name, ...) and friends(id, user_id, friend_id). I would like to check the relationship if users are already friends. But I feel like it could have done better. Please advise, thank you.
With the below function, I can already is user_a is a friend with user_b regarding who added who.
public function isFriend($friend_id) {
$isFriend = false;
$isFriend = Friend::where('user_id', auth()->id())
->where('friend_id', $friend_id)->exists();
if(!$isFriend)
$isFriend = Friend::where('friend_id', auth()->id())
->where('user_id', $friend_id)->exists();
return $isFriend;
}
I removed $isFriend = false; Here is my data:
id
user_id
friend_id
1
1
2
public function hasFriendship($friend_id) {
return Friend::where(function($query) use ($friend_id)
{
$query->where('user_id', auth()->id())
->where('friend_id', $friend_id);
})
->orWhere(function($query) use ($friend_id)
{
$query->where('friend_id', auth()->id())
->where('user_id', $friend_id);
})
->first();
}

how to join table in laravel

I have 2 tables
lets say the 1st one have something like id_teacher_a, id_teacher_b
the 2nd table have the detailed information from table 1 (teacher name, teacher address, etc)
i want to show only the teacher name, so how to join them?
my controller
$data['getid'] = Classroom::select(
'classroom.id',
'teacher.id as tchr_id',
'teacher.name as tchr_name'
)
->join('teacher', 'classroom.teacher_id', '=', 'teacher.id')
->first();
when i want to show table of classroom it would be like
#classroom id #teacher_name_a #teacher_name_b
1 a b
The following should work just fine:
DB::table('classrooms')
->join('teacher as ta', 'ta.id', '=', 'classrooms.id_teacher_a')
->join('teacher as tb', 'tb.id', '=', 'classrooms.id_teacher_b')
->select([
'classrooms.id as classroom_id',
'ta.name as teacher_name_a'
'tb.name as teacher_name_b'
])->get();
Alternatively if you have the following to your Classroom model:
class Teacher extends Model
{
public function teacherA()
{
return $this->belongsTo(Teacher::class, 'id_teacher_a');
}
public function teacherB()
{
return $this->belongsTo(Teacher::class, 'id_teacher_b');
}
}
Then you can also do:
Classroom::query()
->with(['teacherA', 'teacherB'])
->map(function ($classroom) {
return [
'classroom id' => $classroom->id,
'teacher_name_a' => $classroom->teacherA->name,
'teacher_name_b' => $classroom->teacherB->name
];
});

Get only one column from relation

I have found this: Get Specific Columns Using “With()” Function in Laravel Eloquent
but nothing from there did not help.
I have users table, columns: id , name , supplier_id. Table suppliers with columns: id, name.
When I call relation from Model or use eager constraints, relation is empty. When I comment(remove) constraint select(['id']) - results are present, but with all users fields.
$query = Supplier::with(['test_staff_id_only' => function ($query) {
//$query->where('id',8); // works only for testing https://laravel.com/docs/6.x/eloquent-relationships#constraining-eager-loads
// option 1
$query->select(['id']); // not working , no results in // "test_staff_id_only": []
// option 2
//$query->raw('select id from users'); // results with all fields from users table
}])->first();
return $query;
In Supplier model:
public function test_staff_id_only(){
return $this->hasMany(User::class,'supplier_id','id')
//option 3 - if enabled, no results in this relation
->select(['id']);// also tried: ->selectRaw('users.id as uid from users') and ->select('users.id')
}
How can I select only id from users?
in you relation remove select(['id'])
public function test_staff_id_only(){
return $this->hasMany(User::class,'supplier_id','id');
}
now in your code:
$query = Supplier::with(['test_staff_id_only:id,supplier_id'])->first();
There's a pretty simple answer actually. Define your relationship as:
public function users(){
return $this->hasMany(User::class, 'supplier_id', 'id');
}
Now, if you call Supplier::with('users')->get(), you'll get a list of all suppliers with their users, which is close, but a bit bloated. To limit the columns returned in the relationship, use the : modifier:
$suppliersWithUserIds = Supplier::with('users:id')->get();
Now, you will have a list of Supplier models, and each $supplier->users value will only contain the ID.

Laravel Pivot Table, Get Room belonging to users

The structure of my pivot table is
room_id - user_id
I have 2 users that exist in the same room.
How can I get the rooms they both have in common?
It would be nice to create a static class to have something like this.
Room::commonToUsers([1, 5]);
Potentially I could check more users so the logic must not restrict to a certain number of users.
Room::commonToUsers([1, 5, 6, 33, ...]);
I created a Laravel project and make users, 'rooms', 'room_users' tables and their models
and defined a static function in RoomUser Model as below :
public static function commonToUsers($ids)
{
$sql = 'SELECT room_id FROM room_users WHERE user_id IN (' . implode(',', $ids) . ') GROUP BY room_id HAVING COUNT(*) = ' . count($ids);
$roomsIds = DB::select($sql);
$roomsIds = array_map(function ($item){
return $item->room_id;
}, $roomsIds);
return Room::whereIn('id', $roomsIds)->get();
}
in this method, I use self join that the table is joined with itself, A and B are different table aliases for the same table, then I applied the where condition between these two tables (A and B) and work for me.
I hope be useful.
I don't know the names of your relations, but I guess you can do like this :
$usersIds = [1, 5];
$rooms = Room::whereHas('users', function($query) use ($usersIds) {
foreach ($usersIds as $userId) {
$query->where('users.id', $userId);
}
})->get();
It should work. whereHas allows you to query your relation. If you need to have a static method, you can add a method in your model.
There might be a more efficient way but laravel collection does have an intersect method. You could create a static function that retrieves and loop through each object and only retain all intersecting rooms. something like this
public static function commonToUsers($userArr){
$users = User::whereIn('id',$userArr)->get();
$rooms = null;
foreach($users as $user){
if($rooms === null){
$rooms = $user->rooms;
}else{
$rooms = $rooms->intersect($user->rooms);
}
}
return $rooms;
}
This code is untested but it should work.
Room has many users, user has many rooms, so you can find the room which have those two users.
If your pivot table's name is room_users, then you can easily get the common room like this:
public static function commonToUsers($user_ids) {
$room = new Room();
foreach($user_ids as $user_id) {
$room->whereHas('users', function($query) use ($user_id) {
$query->where('room_users.user_id', $user_id);
});
}
return $room->get();
}
This code will convert to raw sql:
select *
from `rooms`
where exists (
select * from `rooms` inner join `room_users` on `rooms`.`id` = `room_users`.`room_id` where `rooms`.`id` = `room_users`.`room_id` and `room_users`.`user_id` = 1
)
and exists
(
select * from `rooms` inner join `room_users` on `rooms`.`id` = `room_users`.`room_id` where `rooms`.`id` = `room_users`.`room_id` and `room_users`.`user_id` = 5
)

Laravel: How to get data from 3 tables with relationship

I have 3 Tables:
Customers
id
name
Sales
customer_id
sale_date
Contacts
customer_id
contact_date
There aren't any update operations in the contacts table. Each process opens a new record in the contacts table. So, a user can have more than one records in the contacts table.
Here are my relations in models:
Customer
public function contacts()
{
return $this->hasMany(Contact::class);
}
public function sales()
{
return $this->hasMany(Sale::class);
}
Contact
public function customer()
{
return $this->belongsTo('App\Customer', 'customer_id');
}
Sale
public function customer()
{
return $this->belongsTo('App\Customer');
}
I would like to have the latest record of the contacts table and make it join with the other related tables.
Here is the query which I have tried:
$record = Contact::groupBy('customer_id')
->select(DB::raw('max(id)'));
$result = Customer::query();
$result->where('is_active', 'YES');
$result->with('sales');
$result->whereHas('contacts', function ($q) use($record){
return $q->whereIn('id', $record)->where('result', 'UNCALLED');
});
return $result->get();
In the blade file, I get some result in foreach loops. However, I am unable to get the related data from the sales and contacts table.
#foreach($result as $item)
#foreach($item->sales as $sale) // Has no output and gives error: Invalid argument supplied for foreach()
#foreach($item->contacts as $contact) // Has no output and gives error: Invalid argument supplied for foreach()
Can anyone help me how to display the sale and contact date? Or any idea for how to improve this code quality?
If you want the latest record of the contacts you can declare another relationship on the Customer model, e.g.:
public function latest_contact()
{
return $this->hasOne(Contact::class)->latest('contact_date');
}
BTW you can always declare one or more hasOne additional relationship if you have a hasMany in place the foreign key used is the same.
In this way you can retrieve latest_contact eager loaded with your Customer model:
$customer = Customer::with('latest_contact')->find($id);
Or use this relationship in your queries, something like that:
$customers = Customer::where('is_active', 'YES')
->with('sales')
->with('contacts')
->whereHas('last_contact', function ($q){
return $q->where('result', 'UNCALLED');
})->get();
Or that:
$customers = Customer::where('is_active', 'YES')
->with('sales')
->with('contacts')
->with('last_contact', function ($q){
return $q->where('result', 'UNCALLED');
})->get();
If you want you can declare last_contact with the additional where:
public function latest_contact()
{
return $this->hasOne(Contact::class)
->where('result', 'UNCALLED')
->latest('contact_date');
}
This way all other queries should be easier.
I hope this can help you.
I'm not sure, but can you try to do the following:
return Customer::where('is_active', 'YES')
->with([
'sale',
'contact' => function ($query) use($record) {
return $query->whereIn('id', $record)->where('result', 'UNCALLED');
}
])->get();

Resources