Laravel advanced query - php

Affiliate has many affiliatesHistory, affiliatesHistory belongs to affiliate, how to make the following query?
Take affiliates, where has affiliatesHistory, if affiliatesHistory records count is equal to 1, then do not take affiliatesHistory, which has status of uninstalled.
$affiliates = $user->affiliates()
->whereDoesntHave('affiliatesHistory', function ($q) {
$q->where('affiliates_histories.status', 'Installed earlier')
->orWhere('affiliates_histories.status', 'Uninstalled / Installed earlier');
The following query works, but I need to not take those affiliates, where affiliatesHistory count is equal to 1 and the status is uninstalled.
Any help will be appriaciated.

So, for what I understand you want to get the affiliates which affiliatesHistory status is Installed earlier. If this is the case then try this:
$user_affiliates = $user->affiliates();
$affiliates = $user_affiliates->whereHas('affiliatesHistory', function($q){
$q->where('status', 'Installed earlier');
})->get();
dd($affiliates);

For your case if there are more than one affiliatesHistory items then return else if there is only one affiliatesHistory then it should not contain Uninstalled status, I guess you can use conditional count to get desired results as
$affiliates = Affiliate::withCount([
'affiliatesHistory',
'affiliatesHistory as affiliatesHistoryUninstalled_count' => function ($query) {
$query->where('status', 'Uninstalled');
}
])->where('user_id', $user->id)
->havingRaw('affiliatesHistory_count > 1 OR (affiliatesHistory_count = 1 AND affiliatesHistoryUninstalled_count = 0)')
->get();

Related

Order by relevance based on multiple conditions using Laravel

I am having an issue while using order by based on multiple conditions.
User with most filled information should show up top and then the one's with less filled information.
$users = User::where('status',1)
->withCount('reviews')
->with('reviews','about')
->orderByRaw("CASE WHEN is_native != '0' AND photo != '' THEN 0 ELSE 1 END")// how i can match the about us relationship value here? means if user have added about intro then it should come first and reviews count?
->paginate(10);
Here is my About Relationship on User
public function about()
{
return $this->hasOne('App\UserAbout', 'user_id')->select('about');
}
NOTE: i am trying to do it with CASE, if there is any other good option you can please point out that.
Thank you
this means that you have to orderby about's count and then by review count, that will get the result you want:
$users = User::where('status',1)
->withCount(['reviews','about'])
->with('reviews','about')
->orderByRaw('about_count desc,reviews_count desc')
->paginate(10);
now user with 'about' will have about_count=1 others will have about_count =0
As #OMR suggested you can do that. But you don't need to use raw Query
$users = User::where('status',1)
->withCount(['reviews','about'])
->with('reviews','about')
->orderBy('about_count','desc')
->orderBy('reviews_count','desc')
->paginate(10);

Apply where condition only to one occurrence

I'm building search functionality for my app. To simplify things:
there are two tables: shops and subscriptions
Each shop can have multiple subscription records, subscription has field expires_at. Now, I assume that shop has active subscription if subscription exsists and at least one of shop's subscripion expires_at date is bigger than now().
It is one of the conditions to the whole query. Here is code:
$shops = Shop::when($subscription, function($query, $subscription) {
$query->doesntHave('subscriptions')->orWhereHas('subscriptions', function($q) use ($subscription, $query) {
$query->where('expires_at', '<', now());
});
});
It doesn't work as expected because if i.e. shop has three related subscriptions and at least one of them is expired – it assumes that shop has no active subscription (even though it has).
I would need to implement some nested function inside or whereHas, I guess, to sort by expires_at desc and then limit to one and only then pass where expires_at clause, however I've no idea how.
And I rather need to stick with Eloquent Query Builder rather than DB facade or raw sql.
Basically, it is the same problem what wasn't answered here:
https://laracasts.com/discuss/channels/eloquent/latest-record-from-relationship-in-wherehas?page=1
Try this:
$shops = Shop::doesntHave('subscriptions')->orWhereHas('subscriptions', function ($query) {
$query->whereDate('expires_at', '<', now());
})->get();
try this :
$shops = Shop::WhereHas('subscriptions')->withCount('subscriptions as
active_subscriptions_count' => function ($query) {
$query->where('expires_at', '<', now());
}])->having('active_subscriptions_count', '>=', 3)->get();
Ok, so after some tries in raw sql I figured it out:
$query->select('shops.*')
->leftJoin('subscriptions', 'shops.id', 'subscriptions.shop_id')
->whereNull('subscriptions.id')
->orWhere('subscriptions.expires_at', '<', now())
->where('subscriptions.id', function($q) {
$q->select('id')
->from('subscriptions')
->whereColumn('shop_id', 'shops.id')
->latest('expires_at')
->limit(1);
});
It is not only faster than where exists clause but also gives me what I needed – only the "highest" subscription for given shop is under consideration.

Laravel paginator display more pages

what i am trying to do is to get distinct values (date) of my Model and for each date the display the corresponding data. When i make pagination of the distinct dates, it works fine, but in the pagination pages i see all the results instead of the distinct. Here is my function:
public function showdaily($id) {
$capacities = array();
// DB::enableQueryLog();
$capacity_daily = CapacityDaily::select('for_date')->where('capacity_id', '=', $id)->orderBy('for_date', 'asc')->distinct()->paginate(2);
// dd(DB::getQueryLog());
foreach($capacity_daily as $cap) {
$get_capacity = CapacityDaily::where('capacity_id', '=', $id)->where('for_date', '=', $cap->for_date)->orderBy('for_date', 'asc')->distinct()->get();
$capacities[] = array('for_date' => $cap->for_date, 'values' => $get_capacity, 'capacity' => Capacity::find($id)->first());
}
return view('admin.capacity.daily', compact('capacities', 'capacity_daily', 'id'));
}
I have totally 4 distinct dates and 96 rows in that table. As i want to display only 2 dates per page it should show me only one additional page available, but instead of that i have from 1-47.
What i make wrong ?
The toSql() method will show the query it is running, but I suspect you need to group on for_date rather than use distinct
$capacity_daily = CapacityDaily::select('for_date')->where('capacity_id', '=', $id)->orderBy('for_date', 'asc')->groupBy('for_date')->paginate(2);

Laravel search where relationship does not exist

I can query where relationships exist by using the whereHas method, but now I need to get the inverse of this result, where the result does not match the result in the whereHas clause.
Here's my query:
$query->whereHas('actions', function ($query) {
$query->where('actions.created_at', '>', Carbon::now()->addDays(-30));
});
This gets things that are actioned in the last 30 days, but I need to get the things that are NOT actioned in the last 30 days.
It seems like I need to get the max(actions.created_at) from the relationship and see if that value is > 30 days ago, but I'm not sure how I can do this with eloquent.
Note: the relationship between person and action is 1 to many, so there could be multiple action records linked, so I can't just flip the operator to be a "<="
Remember whereHas has more than two parameters:
$query->whereHas('actions', function ($query) {
$query->where('actions.created_at', '>', Carbon::now()->addDays(-30));
}, '=',0);
As a matter of fact, it has two more, but by default it is set to '>=' and '1'. So we add the parameters '=' and '0' (or '<', and '1' for what it matters) to convert it in a subquery like 'all the actions that are not in the subset of actions added in less than 30 days).
whereHas method: http://laravel.com/api/4.1/Illuminate/Database/Eloquent/Builder.html#method_whereHas
Can't you just change your where query to a smaller or equal than?
$query->whereHas('actions', function ($query) {
$query->where('actions.created_at', '<=', Carbon::now()->addDays(-30));
});
Try this [Pseudo Code]:
$actions = App\Models\Action::all();
foreach($actions as $actionArray){
$actionCreatedAt = new Carbon($actionArray->created_at);
$now = Carbon::now();
$difference = $actionCreatedAt->diff($now)->days;
if($difference>30){
$exceedingThirty[] = $actionArray;
} else {
continue;
}
}
Then, you can use $exceedingThirty array, in your view.
OR
Try this:
$sql = "DATEDIFF(actions.created_at, '".date('Y-m-d')."' ) > ". 30;
return App\Models\Action::whereRaw( $sql )->get();
See, if that helps.

Laravel Eloquent: ordering Many-to-Many query- user favorites first

My Laravel app returns a list of ships for users to manage. I want to return ships that are "favorited" by the active user before the rest of the ships. eg:
Best ship (*)
Ship I manage often (*)
Another ship
Beta ship
Cornelia
I have a many-to-many relationship between User and Ship, where ship->userFavorite returns the users who have "favorited" the ship.
I can imagine 2 solutions, but I'm not sure if they are possible and how to implement them if they are.
An orderby query that sorts based on favorite being equal to active user
$ships = Ship::orderby([ship->userfavorite being equal to Auth::User()], 'ASC')
->orderby('name', 'ASC')->get();
My current solution: I use a wherehas query to return the favorited ships first ($favships), and use another query to return all the ships ($ships). For this solution I would like to remove the favoried ships from the second query. But how can I elegantly remove these ships from the results?
$user = Auth::user();
$favships = Ship::wherehas('userFavorite', function($q) use($user)
{
$q->where('id', $user->id);
})->orderBy('name', 'ASC')->get();
$ships = Ship::orderBy('name', 'ASC')->get();
Any help to increase my understanding of this problem would be greatly appreciated!
you can use
$normalShips= $ships->diff($favships);
But I think you can reduce from 2 queries to 1 query:
//Ship.php
public function currentUserFavorite() {
$this->userFavorite()->where('id', Auth::user()->id);
}
// get all ships
$ships = Ship::with('currentUserFavorite')->orderBy('name', 'ASC')->get();
// finally
$favships = $ships->where('currentUserFavorite.0.id', Auth::user()->id);
$normalShips = $ships->diff($favships);
// or do a loop
foreach ($ships as $ship) {
if ($ship->currentUserFavorite->count() > 0) {
$favships[] = $ship;
} else {
$normalShips[] = $ship;
}
}

Categories