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.
Related
is it possible to use variables in a with()?
for example:
groep::find(1)->with(['belongsToManyAgent'])
belongsToManyAgent looks like:
public function belongsToManyAgent($ignoreVariables=true, $columns=['*'], $showBoth=false, $showRemoved=false) {
return $this->belongsToMany(
'App\Models\Agent', // doel model
'agent_groep', // pivot tabel
'groep_id', // local key
'agent_id' // foreign key
)
->when(!filter_var($ignoreVariables, FILTER_VALIDATE_BOOLEAN), function($query) use ($showRemoved) {
$query->select($columns)
->when(!filter_var($showBoth, FILTER_VALIDATE_BOOLEAN), function($query) use ($showRemoved) {
$query->where('verwijderd',$showRemoved);
})
->when(filter_var($showBoth, FILTER_VALIDATE_BOOLEAN), function($query) use ($showRemoved) {
$query->when(!filter_var($showRemoved, FILTER_VALIDATE_BOOLEAN), function($query){
$query->where('verwijderd','0');
});
});
});
}
can I access the variables in the function via with() so that I can put $showRemoved on true for example?
There are some options to further narrow down a relationship you have, like so:
$users = User::with(['posts' => function ($query) {
$query->where('title', 'like', '%code%');
}])->get();
See https://laravel.com/docs/9.x/eloquent-relationships#constraining-eager-loads . I don't think it is possible to actually pass an array of variables to your relation function since it is called internally without parameters.
Also you probably want to simplify that function name to just public function agents(...).
It's hard to explain what I need, but I will try.
$this->order->where("client_id", 5)
->with([
"roadOrderStatus" => function ($query) {
$query->select("id", "identifier");
},
"currency" => function ($query) {
$query->join("translations", "currencies", '=', 'translations.id')
->join('translation_entries', 'translations.id', '=', 'translation_entries.translation_id')
->join('languages', 'translation_entries.language_id', '=', 'languages.id')
->whereIn('languages.code', ["eng"]);
},
])->get();
I have two relationships on order (roadOrderStatus and currency). if in callbacks, I don't write anything, response is great. it works. but currency needs translation, because (currencies table has column called name and it's integer, because it gets translated). so if you look at my code, when fetching currency relationship , i also need to join to translations to get translated currency, but if I write joins in currency relationship callback like above, it returns - currency - null
I tried -
1) ->whereIn('languages.code', ["eng"])->get();
2) return $query->join("translations", .e.t.c)
My order model--
<?php
namespace App\Http\Models\Orders;
use App\Http\Traits\ScopeTrait;
use Illuminate\Database\Eloquent\Model;
class RoadOrder extends Model
{
use ScopeTrait;
public function orderFinancialProperty(){
return $this->morphOne("App\Http\Models\Orders\OrderFinancialProperty", "orderFinancialPropertable");
}
public function roadOrderStatus(){
return $this->belongsTo("App\Http\Models\Orders\RoadOrderStatus");
}
public function roadTransportType(){
return $this->belongsTo("App\Http\Models\Transport_Type\RoadTransportType");
}
public function currency(){
return $this->belongsTo('App\Http\Models\Statics\Currency');
}
public function insurancer(){
return $this->belongsTo("App\Http\Models\Insurance\Insurancer");
}
}
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');
I have been building queries and repeating code, is there a way to build this into the eloquent model?
I have a model Transaction where I am selecting specific currencies. How can I add this into the model? Is there a way of changing this:
Transaction::select('*')->where('currency', '=', 'GBP')
So that I can do this:
Transaction::select('*')->currency('GBP')
Then in the model it adds onto the query somehow. I've tried to create Transaction::currency but it didn't work. This is just an example and I plan on adding a few selectors to keep the code clean.
class Transaction extends Model
{
protected $table = 'transactions';
public function currency($query, $currency) {
return $query->where('currency', '=', $currency);
}
}
Laravel has such thing called Query Scopes. It allows you to do exactly what you want. You just need to prefix your currency() method with scope keyword like this:
class Transaction extends Model
{
protected $table = 'transactions';
public function scopeCurrency($query, $currency) {
return $query->where('currency', '=', $currency);
}
}
Then you can do this Transaction::select('*')->currency('GBP')
Read more about scopes here
you are almost done,you have to write currency method as query scope.
public function scopeCurrency($query, $currency) {
return $query->where('currency', '=', $currency);
}
after doing that you can use scope like this
Transaction::select('*')->currency('GBP')
For more details go here https://laravel.com/docs/5.2/eloquent#local-scopes
You can do this by using scope.
Add these code to Transaction.php file
public function scopeCustom($query, $column, $exp, $value)
{
return $query->where('votes', $exp, $value); // ('currency', '=', 'GBP')
}
Now use this scope as like
Transaction::select('*')->custom('currency', '=', 'GBP');
Transaction::select('*')->custom('amount', '>', 1000);
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();