Multiple nested queries in Laravel Eloquent - laravel

I have a chat app, and I need to filter those user conversations that have attachment[s] in the groups last message, by using eloquent ORM. I have following DB tables/models:
Groups & Users. Relation is N-N between them, so there's a pivot table between groups and users, named group_user. Although this table has been expanded with some extra fields, as a result group_user have it's own model now.
Messages. Relations are 1-N from both groups and users.
Attachments. Relation is 1-N from messages.
The algorithm of this filtering isn't the problem - take user (because I need specific users groups) and his groups, find all group messages, sort them by date of creation in descending order and take the top one. And then check whether this message have any records in 'attachments' table. Obivously I do have all the relations defined in the respective Models. Right now I have this code, but it returns all groups that have attachments in any of the messages.
Also, I need to sort the query by the last message 'created_at' date.
return $user->user_groups()->whereHas('group', function($query) {
$query->whereHas('messages', function ($query) {
$query->whereHas('attachments');
});
})->with('group')->latest()->paginate($this->group->getPerPage());
Edit.
class User
{
public function messages()
{
return $this->hasMany('App\Models\Message');
}
public function user_groups()
{
return $this->hasMany('App\Models\GroupUser');
}
}
class Message
{
public function attachments()
{
return $this->hasMany('App\Models\Attachment');
}
public function author()
{
return $this->belongsTo('App\Models\User');
}
public function group()
{
return $this->belongsTo('App\Models\Group');
}
}
class Attachment
{
public function message()
{
return $this->belongsTo('App\Models\Message');
}
}
class GroupUser
{
public function user()
{
return $this->belongsTo('App\Models\User');
}
public function group()
{
return $this->belongsTo('App\Models\Group');
}
}
class Group()
{
public function messages()
{
return $this->hasMany('App\Models\Message');
}
public function group_users()
{
return $this->hasMany('App\Models\GroupUser');
}
}

Add a groups relationship to your User model:
public function groups() {
return $this->belongsToMany('App\Models\Group');
}
Then try this:
$user->groups()
->select('groups.*')
->join('messages', function($join) {
$join->on('groups.id', 'messages.group_id')
->where('created_at', function($query) {
$query->selectRaw('max(created_at)')
->from('messages')
->where('group_id', DB::raw('groups.id'));
});
})
->whereExists(function($query) {
$query->from('attachments')
->where('attachments.message_id', DB::raw('messages.id'));
})
->orderByDesc('messages.created_at')
->paginate($this->group->getPerPage());

Related

How to access a relationship through another relationship

I am working on a survey system and I have a problem accessing one relationship through another.
I have these models and these relationships
Post
id
survey_id
public function survey()
{
return $this->belongsTo(Survey::class);
}
Surveys
id
survey_type_id
public function surveyOptions()
{
return $this->belongsToMany(Survey_options::class);
}
public function surveyType()
{
return $this->belongsTo(Survey_type::class);
}
public function posts()
{
return $this->hasMany(Post::class);
}
Survey_types
id
public function surveyOptions()
{
return $this->belongsToMany(Survey_options::class);
}
Survey_options
id
survey_type_id
public function values()
{
return $this->hasMany(Option_value::class);
}
options_values
survey_options_id
survey_id
public function surveyOptions()
{
return $this->belongsTo(Survey_options::class);
}
Survey_Survey_options (pivot)
survey_id
survey_options_id
I need to do a query with eadger loading to bring me all the posts with survey, surveyOptions, withCount comments y options_values of each surveyOptions. Something like this:
$post->survey->surveyOptions->options_values_count
I have managed to create this query that works for me everything except fetching the option_values count from each SurveyOption. How can I do it?
$posts = Post::with([
'survey' => function ($query) {
$query->withCount('totalSurveys');
},
'survey.surveyOptions',
'image',
'categories'
])
->withCount('comments')
->get();

Laravel One to Many relation with 2 primary keys

I've 2 tables
Users table
id
name
...
Chats table
id
from_id // fk - id on users, the user sent the message
to_id // fk - id on users, intended user
message_body
Now, I'm trying to set up One to Many relation where user has many chat messages but a chat message has two user. from and to user
How can I define this relationship?. I have to user eager loading.
I tried to use Compoships but it didn't work.
My code
User Model
public function chats() {
return $this->hasMany(Chat::class, ['from_id', 'to_id'], 'id');
}
Chat Model
public function user() {
return $this->belongsTo(User::class, ['from_id', 'to_id'], 'id');
}
public function chats() {
return $this->hasMany(Chat::class, 'from_id', 'id') + $this->hasMany(Chat::class,'to_id','id');
}
public function userFrom() {
return $this->belongsTo(User::class, 'from_id', 'id');
}
public function userTo() {
return $this->belongsTo(User::class, 'to_id', 'id');
}
Why don't you do something like that?
On your User model, set up two relationships like this:
public function chatsFrom() {
return $this->hasMany('App\Chat', 'from_id');
}
public function chatsTo() {
return $this->hasMany('App\Chat', 'to_id');
}
Then, on your Chat model, also set up two relationships, one to from and another to to, both referencing a User model. Like this:
public function fromUser() {
return $this->belongsTo('App\User', 'from_id');
}
public function toUser() {
return $this->belongsTo('App\User', 'to_id');
}
This way, you can access the relationships using something like this:
$user->chatsFrom();
$user->chatsTo();
$chat->fromUser();
$chat->toUser();

Laravel BelongsToMany with two pivot table

I have the following table structure:
products
id
product_formats
id
product_id
product_prices
id
product_format_id
market_id
markets
id
A product can have multiple formats, with each having their own price which belongs in a different market. How can I retrieve the list of Markets from the Product model?
I used to have a single pivot table, however now I have two pivot.
class Product extends Model
{
public function markets()
{
return $this->belongsToMany(Market::class);
}
}
Update
To get the result I want, I did the following:
public function markets()
{
return Market::whereIn('id', $this->prices()->distinct()->pluck('market_id')->toArray());
}
However, I'm wondering if there's a way to accomplish this via a relationship.
You need to build relationships in models.
ProductModel:
public function productFormats()
{
return $this->belongTo(ProductFormatsModel::class);
}
ProductFormatsModel:
public function productPrices()
{
return $this->belongTo(ProductPricesModel::class);
}
ProductPricesModel:
public function markets()
{
return $this->hasOne(MarketsModel::class);
}
in Controller:
foreach($product->productFormats as $productFormat)
{
foreach($productFormat->productPrices as $productPrice)
{
var_dump($productPrice->markets);
}
}
For unique markets
in ProductModel:
public function productPrices()
{
return $this->hasManyThrough(
'App\ProductPricesModel',
'App\ProductFormatsModel',
'product_id',
'product_format_id'
'id',
'id'
);
}
in Controller
foreach($product->productPrices as $productPrice)
{
var_dump($productPrice->markets)
}

Laravel Jenssegers MongoDb Relationships not working?

I'm trying to setup some simple favoriting for my app using Laravel/Jenssegers MongoDB
I have two models:
class Item {
public function favorites()
{
return $this->hasMany('App\Models\User', 'favorites');
}
}
class User {
public function favorites()
{
return $this->hasMany('App\Models\Item', 'favorites');
}
}
So an item can have many users and a user can have many items or "favorites".
When I try to run a simple query like:
Item::with('favorites')->get();
The relationship for favorites is there but an empty array.
The favorites table is simple and just has three columns:
_id, item_id, user_id
I've also tried using embedsMany with no luck:
return $this->embedsMany('App\Models\User', 'favorites');
I've also tried every combination of parameters.
return $this->embedsMany('App\Models\User', 'favorites', 'building_id', 'user_id');
return $this->embedsMany('App\Models\User', 'favorites', 'user_id', 'building_id');
return $this->hasMany('App\Models\User', 'favorites', 'building_id', 'user_id');
return $this->hasMany('App\Models\User', 'favorites', 'user_id', 'building_id');
Strange results when using:
return $this->hasMany('App\Models\User', 'favorites', 'building_id', 'user_id');
Only in this situation it returns ALL users, even though favorites has only one record.
Also trying to debug using getQueryLog just gives me an empty array at all times.
print_r(\DB::connection('mongodb')->getQueryLog());
I've done this using mysql and have never had issues. So not really sure where the issues are coming from.
Can you try these instead, I know its the same thing but might do the trick :
use App\User;
use App\Item;
class Item {
public function favorites()
{
return $this->hasMany('User');
}
}
class User {
public function favorites()
{
return $this->hasMany('Item');
}
}
class Favourite {
public function users()
{
return $this->belongsTo('User');
}
public function items()
{
return $this->belongsTo('Item');
}
}
Also add inverse of the above relations like: belongsTo.
and try this query then :
//Just for testing purpose, if raw query returns right result
$items = DB::table('items')
->join('favorites','items.id','=','favorites.id')
->get();

Laravel 4 Relationship: messaging system

I followed the suggestions from user ehp in order to build a lightweight messaging-system:
https://stackoverflow.com/a/18717864/1084315
Users: id | username
Messages: id | from | content
user_messages: user_id | message_id
class User extends Eloquent {
public function messages()
{
return $this->belongsToMany('Message');
}
public function sent_messages()
{
return $this->hasMany('Messages', 'from');
}
}
class Message extends Eloquent {
public function from()
{
return $this->belongsTo('User', 'from');
}
public function to()
{
return $this->belongsToMany('User');
}
}
I create a message like this:
User::find(2)->messages()->create(array('text'=>'this is a message from admin to someone', 'from'=>'1');
Now I need to find / get every Message from a specific user to a specific user.
But in this example only the 'from' IDs are stored in the 'messages' table directly.
I can not even access the pivot of any Message by using
User::find(1)->sent_messages()->get();
What are best practices for collecting messages between one and another user?
Any help highly appreciated
First of all, I think there's a small typo:
public function sent_messages() {
return $this->hasMany('Messages', 'from');
}
This should probably be:
public function sent_messages() {
return $this->hasMany('Message', 'from');
}
Now, if you're looking to get all the messages sent from one user to another, what about this? Untested, but placing a constraint on the to relationship should do the trick.
$messages_from_A_to_B = Message::where('from', $UserA->id)->whereHas('to', function($q) use ($UserB) {
$q->where('user_id', $UserB->id);
})->get();
On a side note, I'm assuming that you specifically require that a user can send a message to more than one user? Else the following table structure seems like it would be easier:
users: id
messages: from | to
And then you just need:
class User extends Eloquent {
public function messages() {
return $this->hasMany('Message', 'to');
}
public function sent_messages() {
return $this->hasMany('Message', 'from');
}
}
class Message extends Eloquent {
public function from() {
return $this->belongsTo('User', 'from');
}
public function to() {
return $this->belongsTo('User', 'to');
}
}
$messages_from_A_to_B = Message::where('from', $UserA->id)->where('to', $UserB->id)->get();

Resources