Eloquent Query Scope on Relationships - php

I have two models, App\Song (belongsTo App\Host) and App\Host (hasMany App\Song).
I have the following query in my Controller:
$songs = Song::whereHas('host', function($query) {
$query->where('skip_threshold', '>', \DB::raw('songs.attempts'))
->where('active', 1);
})
->whereNull('downloaded')
->get();
For reusability I would like to turn into a query scope(s).
I'm quite new to Eloquent so I'm not sure this is the correct way to do this being that its two Models as its not returning any results (where there should be).
Song.php
public function scopeEligable($query)
{
$query->where('skip_threshold', '>', \DB::raw('songs.attempts'));
}
public function scopeActiveHost($query)
{
$query->where('active', 1);
}
public function scopeInDownloadQueue($query)
{
$query->whereNull('downloaded');
}

You should put scopes into Models they belong to. Looking at your initial query scopes scopeEligable and scopeActiveHost belongs to Host model, so you should move them into Host model and then you'll be able to use your query using scopes like this:
$songs = Song::whereHas('host', function($query) {
$query->eligable()->activeHost();
})->inDownloadedQueue()->get();
and as already pointed in comment you should add return to each scope so they could be used as they intended.
EDIT
If you would like to make using it shorter, you could create new relationship in Song model:
public function activeHost()
{
return $this->belongsTo(Host:class)->eligable()->activeHost();
}
so now, you could write:
$songs = Song::whereHas('activeHost')->inDownloadedQueue()->get();

I think you're mistaken about 2 models. I think this should work
Song.php
public function scopeEligable($query, $active) {
return $query->whereHas('host', function($q) {
$q->where('skip_threshold', '>', \DB::raw('songs.attempts'))->where('active', $active);
})
}
public function scopeInDownloadQueue($query)
{
$query->whereNull('downloaded');
}
Usage
$songs = Song::eligable(true)->inDownloadQueue()->get();

Related

laravel pagination not working with search

i have this code in my controller
if (request('tag')) {
$Posts = Tags::where('name' ,request('tag'))->firstOrFail()->Posts;
} else {
$Posts = Blog::paginate(10);
}
return view('Panel.Posts',['Posts' => $Posts]);
and this is my model function
public function tag(){
return $this->belongsToMany(Tags::class,'Post_tag','Post_id');
}
It's a web application I am creating and it's a blog.
I'm trying to create a category.
I want to paginate my search method but I can't do it.
If I use paginate method it says posts relation is not found in paginate.
I tried this:
$Posts = Tags::where('name' ,request('tag'))->firstOrFail()->paginate(10)->Posts;
and this
$Posts = Tags::where('name' ,request('tag'))->firstOrFail()->Posts->paginate(10);
Can any one help me to fix this?
try my code
$Posts = Blog::whereHas('tag', function (Builder $query) {
$query->where('name', request('tag'));
})->paginate(25);
you can yse where ha and query builder instans a relation in models
Make sure Tags has relation relation in model. Also use ->posts instead ->Posts, but anyway:
There is no point of using separate query if relation is defined or not. Instead you need combination of when and whereHas.
$posts = Blog::when(request('tag'), function ($q, $tagName) {
return $q->whereHas('tag', function ($q) use ($tagName) {
return $q->where('name', $tagName);
});
})->paginate(10);
When fill apply what is inside it if condifion will match. So it is just replace for "if". Docs https://laravel.com/docs/7.x/queries#conditional-clauses
WhereHas will check if relation model exists and allow you to use "where" (or other queries) on that relation. Docs https://laravel.com/docs/7.x/eloquent-relationships#querying-relationship-existence
If you wanna still use "if" nad not "when":
$posts = Blog::query();
if (request('tag')) {
$posts = $posts->whereHas('tag', function ($q) {
return $q->where('name', request('tag'));
});
}
$posts = $posts->paginate(10);

Laravel Eloquent deep nested query

I'm still learning Laravel and I can't find the solution for this problem.
I need to get invoices(with expenses) that are related to specific Partner Type.
I tried this:
$p = Project::with(['invoices.partner.partnerType' => function($query){
$query->where('partnerTypeName', 'Lieferant');
}, 'expenses'
])->where('id', $id)
->first();
I want to select invoices for Lieferant, but I get all invoices for one project.
Project Model:
public function invoices()
{
return $this->hasMany('App\Invoice');
}
Invoice Model
public function expenses()
{
return $this->hasMany('App\Expense');
}
public function partner()
{
return $this->belongsTo('App\Partner');
}
Partner Model
public function partnerType()
{
return $this->belongsTo('App\PartnerType');
}
Edit: PartnerType Model
public function partners()
{
return $this->hasMany('App\Partner');
}
Edit 2: Database
Partner(partnerID, name, partnerTypeId)
PartnerType(partnerTypeId, partnerTypeName)
Project(projectID, name)
Invoice(invoiceID, name, projectID, partnerID)
Expenses(expenseID, invoiceID)
If your models look like that.
Should be like :
$p = Project::with(['invoices' => function($query){
$query->where('partnerTypeName', 'Lieferant')
->with(['expenses','partner' => function($q){
$q->with('partnerType');
}]);
}])->where('id', $id)
->first();
return dd($p);
The solution to your problem is to update your query like this:
$p = Project::with(['invoices' => function($query){
$query->with('expenses')->whereHas('partner.partnerType', function($q){
$q->where('partnerTypeName', 'Lieferant');
});
}])
->where('id', $id)
->first();
But a cleaner solution would be using a scope for your problem.
In your Invoice model.
// Invoice.php
public function scopeByPartnerType($query, $partnerType)
{
$query->whereHas('partner.partnerType', function($q) use ($partnerType) {
$q->where('partnerTypeName', $partnerType);
});
}
And then in your Project model, add another relation that will just get Invoices with a particular partner type.
// Project.php
public function lieferantInvoices()
{
return $this->hasMany('App\Invoices')->byPartnerType('Lieferant');
}
Now you can do just this:
$project->find($id)->load('lieferantInvoices');

Eloquent scope relation

I'm using Laravel 5.4
Code
$usersWithFacebookNotifications = User::has('validSocialAccountForFacebookNotifications')
->has('activeFacebookNotifications')
->with('preferences')->with('socialAccount')
->get();
User model
public function activeFacebookNotifications () {
return $this->notificationsChannels()
->where('active', 1)
->where('name', 'facebook_messenger');
}
public function validSocialAccountForFacebookNotifications () {
return $this->socialAccount()
->where('provider', 'facebook')
->whereNotNull('page_scoped_id');
}
Questions
Is it possible to create a scope relation in the Users model so I could call something like
$users = User::has('allNeeded')->get();
where allNeeded would be
activeFacebookNotifications + validSocialAccountForFacebookNotifications
Yes but a little bit different:
$users = User::allNeeded()->get();
And in your User model you add:
public function scopeAllNeeded($query)
{
return $query
->has('validSocialAccountForFacebookNotifications')
->has('activeFacebookNotifications');
}

Eloquent scope method gives different results

I'm using Laravel 5.4
Working code
$cityWithEvents = City::with(['events' => function ($q) {
$q->whereDate('start_time', Carbon::today('America/Montreal'))->orwhereBetween('start_time', [Carbon::today('America/Montreal'), Carbon::tomorrow('America/Montreal')->addHours(4)]);
}])->where('active', 1)->get()->keyBy('id');
Not working code
$cityWithEvents = City::with('todayEventsWithAfterHoursIncluded')
->where('active', 1)
->get()
->keyBy('id');
City model
public function events() {
return $this->hasManyThrough('App\Event', 'App\Venue');
}
public function todayEventsWithAfterHoursIncluded () {
return $this->events()
->whereDate('start_time', Carbon::today('America/Montreal'))
->orwhereBetween('start_time', [
Carbon::today('America/Montreal'),
Carbon::tomorrow('America/Montreal')->addHours(4)
]);
}
Questions
When trying to create a scope method the query gives me different result. I can't see why and what should I change
I've only used scopes a few times, but never within a ->with() clause. On your City model, create a new scope:
public function scopeTodayEventsWithAfterHoursIncluded($query){
return $query->with(["events" => function($subQuery){
$subQuery->whereDate('start_time', Carbon::today('America/Montreal'))->orWhereBetween('start_time', [Carbon::today('America/Montreal'), Carbon::tomorrow('America/Montreal')->addHours(4)]);
});
}
Then, on your City query, add it as a scope function:
$cityWithEvents = City->where('active', 1)
->todayEventsWithAfterHoursIncluded()
->get();
I think the way you are using it requires that your Event model has the scope on it, as you're technically calling with("events") on your base query and your scoped one.
Let me know if this changes you results.
If you do the query, you should do it like this:
$cityWithEvents = City::withTodayEventsWithAfterHoursIncluded()
->where('active', 1)
->get()
->keyBy('id');
You scope in you model should look like this:
public function scopeWithTodayEventsWithAfterHoursIncluded ($query)
{
return $query
->with(['events' => function ($q) {$q
->whereDate('start_time', Carbon::today('America/Montreal'))
->orwhereBetween('start_time', [
Carbon::today('America/Montreal'),
Carbon::tomorrow('America/Montreal')->addHours(4)
]);
}]);
}
Now it should be equal.

Laravel - search by title?

How to search by title in the ServiceType only? There is also a title field in the Package which should be avoided
For example, in the Model:
class Package extends Eloquent {
protected $table = 'package';
function serviceType()
{
return $this->belongsTo('ServiceType');
}
public static function getPackagesByServiceType($service)
{
return Package::with('serviceType')->where('title', '=', $service);
}
}
Note:
There is a service_type_id field in the Package and id, title fields in the serviceType
in the controller:
$packages = Package::getPackagesByServiceType('something')->get();
No result appeared for some reason? It should search for something in the serviceType
It seem it wouldn't work to combine with() and where(). When I remove the where() and it work.
You can't use where() like that to filter by a related model. You should use whereHas() instead:
public static function getPackagesByServiceType($service)
{
return Package::with('serviceType')->whereHas('serviceType', function($q) use ($service){
$q->where('title', '=', $service);
});
}
Note if you don't need serviceType in the packages afterwards you don't have to eager load it, ergo you can remove the with('serviceType')
Also if you call get() in the controller you should use a query scope. It offers the same functionality but it's not a static function and it's the Laravel way
public function scopeByServiceType($query, $service){
return $query->with('serviceType')->whereHas('serviceType', function($q) use ($service){
$q->where('title', '=', $service);
});
}
And you use it like this:
$packages = Package::byServiceType('something')->get();
class Package extends Eloquent {
protected $table = 'package';
function serviceType()
{
return $this->belongsTo('ServiceType');
}
public static function getPackagesByServiceType($service)
{
return Package::with('serviceType')->where('title', '=', $service)->get();
}
}
You forgot the ->get();
The ->get() should be in the Model
public static function getPackagesByServiceType($service)
{
return Package::with('serviceType')->where('title', '=', $service)->get(); // here
}
and in the controller it should be like this:
$packages = Package::getPackagesByServiceType('something');
Hope that helps... I had similar issues in my Model - Controller structure....

Categories