laravel with relationship callback function and join it - php

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

Related

Complex query constraint on queryScope in laravel

users table have the following columns:
created_at (timestamp)
last_order (timestamp)
when new users is created then last_order is null and created_at is current timestamp.
When user place an order then last order column is updated with timestamp of order placing time.
Now I want to retrieve all users who didn't place any order last 7 days. I define my user model as follows:
class User extends Model
{
/**
* #var string The database table used by the model.
*/
public $table = 'users';
/**
* #var array Validation rules
*/
public $rules = [
];
public $hasMany = [
'voucher' => ['ItScholarBd\Api\Models\Voucher'],
'order' => ['ItScholarBd\Api\Models\OrderLog']
];
public function scopeCustomer($query)
{
return $query->where('role_id', '=', 5);
}
public function scopeNew($query,$days)
{
return $query->where('created_at', '>=', Carbon::now()->subDays($days));
}
public function scopeIdle($query,$days)
{
$dayOffset = Carbon::now()->subDays($days);
return $query->where(function($q,$dayOffset){
$q->whereNull('last_order')
->where('created_at','<',$dayOffset);
})->orWhere(function($q,$days){
$q->whereNotNull('last_order')
->whereRaw("DATEDIFF(created_at,last_order)>$days");
});
}
}
Here scopeNew() is working perfectly but scopeIdle() is throwing the following error:
Type error: Too few arguments to function App\User::App\{closure}(),
1 passed in .\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Builder.php on line 222 and exactly 2 expected
You don't get to make up parameters for functions you don't control. The closure will be passed a single argument, and you need to import any other variables with use:
public function scopeIdle($query, $days)
{
$dayOffset = Carbon::now()->subDays($days);
return $query
->where(function ($q) use ($dayOffset) {
$q->whereNull('last_order')
->where('created_at', '<', $dayOffset);
})
->orWhere(function ($q) use ($days) {
$q->whereNotNull('last_order')
->whereRaw("DATEDIFF(created_at, last_order) > ?", [$days]);
});
}
Note the use of parameters in the whereRaw statement, which can save you from SQL injection. You will also find your code easier to work on if you consistently indent and space it.
Try using a closure:
return $query->where(function($q) use ($dayOffset){

Laravel Eloquent rather complex relationship

I have two tables, users and jobs. A user owns a job:
from User model:
public function getJobs(){
return $this->belongsTo('App\Jobs','operative_id','id');
}
public function getIncompleteJobs(){
return $this->belongsTo('App\Jobs','operative_id','id')
->where('jobStatus_id', '=', 1);
}
This is fine but the job has a status (completed, pending etc) in a field called 'status_id' and I want to further refine getJobs() so it only returns those with status_id equalling 2.
May be I am being thick!
Edit from the comments:
$op = App\User::where('client_id', $us->getCompany->id)
->where('operative', 1)
->orderby('name')
->where('active', 1)
->with('getIncompleteJobs')
->get();
You could create the relationship, and them call it with whatever condition you want in another method.
Like that:
public function jobs()
{
return $this->belongsTo('App\Jobs','operative_id','id');
}
public function completedJobs()
{
return $this->jobs()->where('status_id', '=', 2);
}

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 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 where on relationship object

I'm developing a web API with Laravel 5.0 but I'm not sure about a specific query I'm trying to build.
My classes are as follows:
class Event extends Model {
protected $table = 'events';
public $timestamps = false;
public function participants()
{
return $this->hasMany('App\Participant', 'IDEvent', 'ID');
}
public function owner()
{
return $this->hasOne('App\User', 'ID', 'IDOwner');
}
}
and
class Participant extends Model {
protected $table = 'participants';
public $timestamps = false;
public function user()
{
return $this->belongTo('App\User', 'IDUser', 'ID');
}
public function event()
{
return $this->belongTo('App\Event', 'IDEvent', 'ID');
}
}
Now, I want to get all the events with a specific participant.
I tried with:
Event::with('participants')->where('IDUser', 1)->get();
but the where condition is applied on the Event and not on its Participants. The following gives me an exception:
Participant::where('IDUser', 1)->event()->get();
I know that I can write this:
$list = Participant::where('IDUser', 1)->get();
for($item in $list) {
$event = $item->event;
// ... other code ...
}
but it doesn't seem very efficient to send so many queries to the server.
What is the best way to perform a where through a model relationship using Laravel 5 and Eloquent?
The correct syntax to do this on your relations is:
Event::whereHas('participants', function ($query) {
return $query->where('IDUser', '=', 1);
})->get();
This will return Events where Participants have a user ID of 1. If the Participant doesn't have a user ID of 1, the Event will NOT be returned.
Read more at https://laravel.com/docs/5.8/eloquent-relationships#eager-loading
#Cermbo's answer is not related to this question. In that answer, Laravel will give you all Events if each Event has 'participants' with IdUser of 1.
But if you want to get all Events with all 'participants' provided that all 'participants' have a IdUser of 1, then you should do something like this :
Event::with(["participants" => function($q){
$q->where('participants.IdUser', '=', 1);
}])
N.B:
In where use your table name, not Model name.
for laravel 8.57+
Event::whereRelation('participants', 'IDUser', '=', 1)->get();
With multiple joins, use something like this code:
$userId = 44;
Event::with(["owner", "participants" => function($q) use($userId ){
$q->where('participants.IdUser', '=', 1);
//$q->where('some other field', $userId );
}])
Use this code:
return Deal::with(["redeem" => function($q){
$q->where('user_id', '=', 1);
}])->get();
for laravel 8 use this instead
Event::whereHas('participants', function ($query) {
$query->where('user_id', '=', 1);
})->get();
this will return events that only with partcipats with user id 1 with that event relastionship,
I created a custom query scope in BaseModel (my all models extends this class):
/**
* Add a relationship exists condition (BelongsTo).
*
* #param Builder $query
* #param string|Model $relation Relation string name or you can try pass directly model and method will try guess relationship
* #param mixed $modelOrKey
* #return Builder|static
*/
public function scopeWhereHasRelated(Builder $query, $relation, $modelOrKey = null)
{
if ($relation instanceof Model && $modelOrKey === null) {
$modelOrKey = $relation;
$relation = Str::camel(class_basename($relation));
}
return $query->whereHas($relation, static function (Builder $query) use ($modelOrKey) {
return $query->whereKey($modelOrKey instanceof Model ? $modelOrKey->getKey() : $modelOrKey);
});
}
You can use it in many contexts for example:
Event::whereHasRelated('participants', 1)->isNotEmpty(); // where has participant with id = 1
Furthermore, you can try to omit relationship name and pass just model:
$participant = Participant::find(1);
Event::whereHasRelated($participant)->first(); // guess relationship based on class name and get id from model instance
[OOT]
A bit OOT, but this question is the most closest topic with my question.
Here is an example if you want to show Event where ALL participant meet certain requirement. Let's say, event where ALL the participant has fully paid. So, it WILL NOT return events which having one or more participants that haven't fully paid .
Simply use the whereDoesntHave of the others 2 statuses.
Let's say the statuses are haven't paid at all [eq:1], paid some of it [eq:2], and fully paid [eq:3]
Event::whereDoesntHave('participants', function ($query) {
return $query->whereRaw('payment = 1 or payment = 2');
})->get();
Tested on Laravel 5.8 - 7.x

Categories