Laravel, selecting all Posts where its first approved revision is published - laravel

I have 2 tables Articles and Revisions
There is a relationship between them one2many
The Revisions table has 2 columns isApproved and isPublished
Now, I want to select all articles, when its first Approved revision is Published
Article::whereHas('revisions', function ($query) {
$query->where('isApproved', 1)->where('isPublished', 1);
});
But this code selected all articles that have approved and published revisions

Try filter the results:
$articles = Article::
whereHas('revisions', function ($query) {
$query->where('isApproved', 1)->where('isPublished', 1);
})
->get()
->filter(function ($article) {
return $article->revisions->first(function ($revision) {
return $revision->isApproved == true;
})
->isPublished == true;
});

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();
}

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();

Laravel, Eloquent, getting related items from a morph relationship (advice)

The title of the question may seem weird, but I'll explain it briefly. I'm not experiencing any issues yet, but considering any advice regarding the following approach.
I have the following tables:
articles
hasMany(tags)
movies
hasMany(tags)
series
hasMany(tags)
subs (morph)
media_id
media_type
user_id
All of the first three tables have many tags, where the subs table have a Morph relation between them all.
The concept is simple, the user can subscribe to movies and/or series and NOT articles, let's say, opt-in a record from movies.
The following record will be inserting:
media_id => 1, media_type => movie, user_id => 1
Let's say we have 10K records like this in subs table. Now, I create a record in articles with specific tags. I want to notify all users in subs, who are opted-in movies and/or series that have exactly the same tags as the record I'm targeting from articles.
TL;DR: Notifying users that are interested in specific tags from movies or series.
To achieve this, I added the morphTo in the subs model:
public function media(){
return $this->morphTo();
}
The logic is straightforward:
Get the article.
Get the tags() relationship and save it into a variable.
Check if tags are not empty, if not, continue
Get all the subs.
Get the morphTo relationship and get its tags.
Compare and continue.
$article = MediaArticle::find($notificationable_id);
$article_tags = $article->tags;
$subbed_users = array();
if (empty($article_tags) === false) {
$subs = NotificationMediaSub::all();
foreach ($subs as $sub) {
$sub_tags = $sub->media->tags;
foreach ($sub_tags as $sub_tag) {
foreach ($article_tags as $article_tag_title) {
if (strcasecmp($sub_tag->title, $article_tag_title->title) === 0) {
array_push($subbed_users, $sub->user_id);
}
}
}
}
// continue here
}
I find this approach really intensive and may cause the server to slow down a lot. Is there a better approach to achieve my goal?
=========================
Approach
I defined the media_tags_relationship as a Model, inside it:
public function media() {
if ($this->taggable_type === AdminHelper::MORPH_MEDIA_TRANSLATION_MOVIE['original']) {
return $this->belongsTo(MediaMovie::class, 'taggable_id', 'id');
} elseif ($this->taggable_type === AdminHelper::MORPH_MEDIA_TRANSLATION_SERIES['original']) {
return $this->belongsTo(MediaSeries::class, 'taggable_id', 'id');
}
return $this->belongsTo(MediaMovie::class, 'taggable_id', 'id')->where('id', 0);
}
Now, I'm fetching the subs like this:
$article = MediaArticle::find($notificationable_id);
$article_tags = $article->tags;
$subbed_users_tokens = array();
if (empty($article_tags) === false) {
$article_tags = $article_tags->map(function ($tag) {
return $tag->title;
});
$related_tags = MediaTag::whereIn("title", $article_tags)->get();
$related_tags = $related_tags->map(function ($tag) {
return $tag->id;
});
$tags_relationship = MediaTagsRelationship::whereIn("tag_id", $related_tags)->where("taggable_type", "movie")->orWhere("taggable_type", "series")->get();
foreach ($tags_relationship as $tag_relationship) {
$media = $tag_relationship->media()->with('subs')->get();
if (empty($media) === false) {
$media = $media[0];
$subs = $media->subs->map(function ($sub) {
return $sub->firebase_token;
});
array_push($subbed_users_tokens, $subs);
}
}
}

Laravel - Merge two tables, then remove entries based on a where query

I have a database of fruit, and lets say I want all the red fruit from it:
$allfruit = DB::table('fruits')->Where("color","=","red")->paginate(10);
I also have a table of user's least favourite fruit. If a user is logged in I'm trying to pass their ID through to get a list of all their hated fruit:
$leastfav = DB::table('dislikes')->Where("userID","=",Auth::user()->id)->get();
Now what I'd like to do is remove all the entries from $allfruit that appear in the 'dislikes' table with that user's ID.
What I've tried is something like:
$allfruit = DB::table('fruits')->Where("color","=","red")->merge($leastfav)->where(fruits.ID,"!=", "dislikes.fruitID")->paginate(10);
My DB is SQLite if that helps. Thanks
You could use whereNotExists (the inverse of whereExists()):
$allfruitQuery = DB::table('fruits')->where('color', 'red');
if (auth()->check()) {
$allfruitQuery->whereNotExists(function ($query) {
$query->select(DB::raw(1))
->from('dislikes')
->where('userID', auth()->id())
->whereRaw('fruits.ID = dislikes.fruitID');
});
}
$allfuit = $allfruitQuery->paginate(10);
Alternatively, (if you're using 5.2+) you could use when():
$allfuit = DB::table('fruits')->where('color', 'red')
->when(auth()->check(), function ($query) {
$query->whereNotExists(function ($query) {
$query->select(DB::raw(1))
->from('dislikes')
->where('userID', auth()->id())
->whereRaw('fruits.ID = dislikes.fruitID');
});
})
->paginate(10);

Laravel 5: Count product by category where active product only

In Laravel 5.4, I have my relationship:
public function products()
{
return $this->hasMany(Product::class);
}
public function category()
{
return $this->belongsTo(Category::class);
}
And my query is:
$x = Category::with('products')->where('active', 1)->get();
It does show all category names correctly, but how to count only product 'active = 1'? I don't wish to count all products, but active products only.
try this:
$x = Category::with(['products' => function($query) { $query->where('active','=', 1); }])->where('active', 1)->get();
That will give you products that are active and then only categories where products are active.
Try this to be more specific. Additional from idea of #linuxartisan
$x = Category::whereHas('products', function ($query) {
$query->where('products.active', '=', 1);
})
->where('categories.active', '=', 1)
->get();
I am assuming you have active field for product as well as category.
Just try this query
$x = Category::whereHas('products', function ($query) {
$query->where('products.active', 1);
})->where('categories.active', 1)
->get();
public function products()
{
return $this->hasMany(Product::class)->where('active',1);
}
This is working in Laravel 7

Resources