I'm working on a Laravel project, where i have the models "Reservation" and "Week".
The model "Week" has a starting date and a price, the model "Reservation" has a starting date and an ending date.
I want to be able to do a eloquent selection like this: Reservation::with('weeks')->get(), but if i do something below eloquent doesn't recognize it as a relationship, and i can't use "HasMany" in Reservation model because i don't associate the tables with ids, but only with dates.
How can i get the weeks as a relationship?
class Reservation extends Model
{
public function weeks()
{
return Week::whereDate('starting_date', '>=', $this->starting_date)
->whereDate('starting_date', '<', $this->ending_date)
->orderBy('starting_date')
->get();
}
}
edited: thanks #Tim Lewis
I finally managed it thanks this repository: https://github.com/johnnyfreeman/laravel-custom-relation
The repository is archived and not installable for Laravel 8, so i just copied the files in this folders:
app\Relations\Custom.php
app\Traits\HasCustomRelations.php
This allows me to use Reservation::with('weeks')->get(); with eadger constraints
use App\Models\Week;
use App\Traits\HasCustomRelations;
class Reservation extends Model
{
use HasCustomRelations;
public function weeks()
{
return $this->custom(
Week::class,
// add constraints
function ($relation) {
if($this->starting_date && $this->ending_date) {
$relation->getQuery()
->where('weeks.starting_date', '>=', $this->starting_date)
->where('weeks.starting_date', '<', $this->ending_date);
}
else {
$relation->getQuery();
}
},
// add eager constraints
function ($relation, $models) {
$starting_date = $models[0]->starting_date;
$ending_date = $models[count($models)-1]->ending_date;
$relation->getQuery()
->where('weeks.starting_date', '>=', $starting_date)
->where('weeks.starting_date', '<', $ending_date);
},
// add eager matcher
function ($models, $results, $foreignTable, $relation) {
foreach ($models as $model) {
$model->setRelation($foreignTable, $results
->where('starting_date', '>=', $model->starting_date)
->where('starting_date', '<', $model->ending_date));
}
return $models;
}
);
}
}
You're almost there. Just use an accessor to get the weeks:
public function getWeeksAttribute()
{
return Week::whereDate('starting_date', '>=', $this->starting_date)
->whereDate('starting_date', '<', $this->ending_date)
->orderBy('starting_date')
->get();
}
and you'll be able to get the weeks as if it were any other attribute. If you need to serialize your model to Json, remember to add weeks to the $appends array in your model.
Related
I'm trying to compare two date columns from different tables.
Here's my model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ExitAccessionGood extends Model
{
protected $fillable = ['exit_accession_id', 'norm_id', 'quantity'];
public function entry_goods()
{
return $this->hasMany('App\EntryAccessionGood', 'norm_id', 'norm_id');
}
public function packagings()
{
return $this->hasMany('App\PackagingItem', 'norm_id', 'norm_id');
}
}
The entry_goods() function gather all "norms" from the entry_accession_goods table that have the same norm_id as in the exit_accession_goods table.
public function entry_goods()
{
return $this->hasMany('App\EntryAccessionGood', 'norm_id', 'norm_id');
}
But it gathers all of them, however I only need the get the ones with the date less or equal to the model's table.
So for example, if there are norm ids 1 and 2 added in the exit_accession_goods table (yesterday), and the same ids (1 and 2) added today in the entry_accession_goods table (today), the function entry_goods() should NOT get the data from the entry_accession_goods table.
This is what I tried:
public function entry_goods()
{
return $this->hasMany('App\EntryAccessionGood', 'norm_id', 'norm_id')
->whereRaw('entry_accession_goods.created_at <= exit_accession_goods.created_at');
}
I'm unable to access the exit_accession_goods.created_at which is the problem, anyone know the solution? Thank you.
You can put it as an eloquent query...
$date = 2020/01/01'
$var = ExitAccessionGood::whereHas('entry_good', function ($query) use ($date) {
$query->where('created_at', '<', $date);
})->get();
That should do what you want, assuming I understood the question
This Correct Relation
public function entry_goods(){
return $this->hasMany('App\EntryAccessionGood', 'norm_id', 'norm_id')
->where(created_at <= $this->attributes['created_at']);
}
In Laravel 5.7 I have read the Has Many Through documentation but I still can't use it correctly for my case.
Here's the DB:
Analytics
id
data
subscriber_id
Subscribers
id
city_id
Cities
id
name
I need the Analytics model to get data from Analytics with subscribers.id and cities.name
What I have done:
Connected Analytics and Subscribers models
<?php
class Analytics extends Model
{
public function subscriber()
{
return $this->belongsTo('App\Models\Subscriber');
}
}
class Subscriber extends Model
{
public function analytics()
{
return $this->hasMany('App\Models\Analytics');
}
}
Made a request that gets the data from the Analytics table with subscribers data:
$results = Analytics::where('survey_id', '4')
->with('subscriber')
->whereDate('created_at', '>=', $last_survey_date)
->orderBy('data')
->get();
If anybody has any ideas how to get city names, please, share it.
// maybe this will work for you?
class Analytics extends Model
{
public function subscriber()
{
return $this->belongsTo('App\Models\Subscriber');
}
public function cities() {
return $this->hasManyThrough('App\City', 'App\Subscriber');
}
}
I'm not sure if I understood your request correctly. Do you want to get all Analytics of Subscribers of a given City? Or do you want the city name of an analytics' subscriber? Either way, here are both solutions.
To get all analytics for subscribers of a given city:
$city = 'Vienna';
$results = Analytics::query()
->with('subscriber')
->whereHas('subscriber', function ($query) use ($city) {
$query->whereHas('city', function ($query) use ($city) {
$query->where('name', $city);
});
})
->where('survey_id', '4')
->whereDate('created_at', '>=', $last_survey_date)
->orderBy('data')
->get();
Or to get the city name for an analytics record, you have two options. One is to use the Laravel eager loading for relationships, which works, but will probably load a lot of unnecessary data into memory:
$results = Analytics::query()
->with('subscriber.city') // you can nest relationships as far as they go
->where('survey_id', '4')
->whereDate('created_at', '>=', $last_survey_date)
->orderBy('data')
->get();
foreach ($results as $analytics) {
$city = $analytics->subscriber->city->name;
}
The other is to join the tables yourself and select only the necessary data:
$results = Analytics::query()
->join('subscribers', 'subscribers.id', '=', 'analytics.subscriber_id')
->join('cities', 'cities.id', '=', 'subscribers.city_id')
->where('analytics.survey_id', '4')
->whereDate('analytics.created_at', '>=', $last_survey_date)
->orderBy('analytics.data')
->select('analytics.*', 'cities.name as city_name')
->get();
foreach ($results as $analytics) {
$city = $analytics->city_name;
}
Beware that you could use select('analytics.*', 'cities.name'), but this will override a name column selected of the analytics table if one exists. So it is better to use a column alias with as col_alias.
Thank you Nick Surmanidze and Namoshek for your answers!
I have found the way it works last night:
class Subscriber extends Model
{
public function analytics()
{
return $this->hasMany('App\Models\Analytics');
}
public function subscriberCity()
{
return $this->belongsTo('Modules\Directories\Entities\City', 'city_id', 'id');
}
}
class City extends Model
{
public function subscriber()
{
return $this->hasMany('App\Models\Subscriber');
}
}
And the needed result can be get by this way:
$results = Analytics::where('survey_id', '4')
->with(['subscriber' => function($i){
$i->with(['subscriberCity']);
}])
->whereDate('created_at', '>=', $last_survey_date)
->orderBy('data')
->get();
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');
Hi I am implement polymorph relation in laravel framework
currently i have 2 models
CreditLog and User
Creditlog has property sourceable , which is sourceable to User model
class CreditLog extends Model
{
...
public function sourceable()
{
return $this->morphTo();
}
...
}
And then in User i have relation like this
class User extends Authenticatable
{
public function creditLogs()
{
return $this->morphMany('App\Models\CreditLog', 'sourceable');
}
}
And then in some controller i need to get user credit log
$user = User::find($id);
$CreditLogs = $user->creditLogs;
Can i adding parameter in creditLogs method , i mean can laravel morphMany add the parameter like this
$CreditLogs = $user->creditLogs
->where('created_at', '>=', $inputReq['start'])
->where('created_at', '<=', $inputReq['end']);
Thank you for responses the question
You can use load() method with lazy eager loading.
$user->load(['creditLogs' => function ($query) use($inputReq) {
$query->where('created_at', '>=', $inputReq['start'])
->where('created_at', '<=', $$inputReq['end']);
}]);
Or use with() methid with Constraing eager loading
I have the following set for relations in my eloquent models/db:
Student has many PhdReport
PhdReport has one Link
I need to get Students where the (single) most recent PhdReport (by the attribute date_to) is more than 6 months and the Link connected to the PhdReport has a status of 'complete'.
I am trying to do this using eloquent relationships and I'm pretty new to the concept of querying relationships so I would like to know if there's a better approach to the one I'm taking.
Here's the relevant code so far:
PhdReport.php
public function link()
{
return $this->belongsTo(\App\Models\Link::class);
}
Student.php
public function phdReport()
{
return $this->hasMany(\App\Models\PhdReport::class);
}
public function latestPhdReport()
{
return $this->hasOne(\App\Models\PhdReport::class)->latest('date_to');
}
/* this doesn't work! */
public function lastPhdReportSixMonthsAgo()
{
$sixMonthsAgo = \Carbon\Carbon::now()->subMonth(6);
return $this->whereHas('latestPhdReport', function ($query) use ($sixMonthsAgo) {
$query->where('date_to', '<=', $sixMonthsAgo);
});
}
This is my best shot at it so far but I am unsure if the first whereHas applies to the second whereHas also?
$sixMonthsAgo = \Carbon\Carbon::now()->subMonth(6);
$students = $this->student
->whereHas('phdReport.link', function ($query) {
$query->where('status', 'complete');
})
->whereHas('latestPhdReport', function ($query) use ($sixMonthsAgo) {
$query->where('date_to', '<=', $sixMonthsAgo);
})
->get();
If I run:
$students = $this->student
->has('lastPhdReportSixMonthsAgo')
->get();
I get:
BadMethodCallException in Builder.php line 1994:
Call to undefined method Illuminate\Database\Query\Builder::getRelated()
Any advice would be much appreciated!
This is my current solution, I haven't had time to refactor yet but I'm sure it can be improved by doing only a query and no filter. Will edit when I get time but thought I'd put it up as it might help someone else. Much credit to #JarekTkaczyk
$monthsAgo = \Carbon\Carbon::now()->subMonth($months);
// Query 1: students with a completed report
$studentsWithCompletedLink = $studentsWithCompletedLink
->whereHas('phdReport.link', function ($query) {
$query->where('status', 'complete');
})
->get();
if ($months != 0) {
// filter through to get students where the report date_to is more than $months months ago
$studentsWithReport = $studentsWithCompletedLink->filter(function ($student) use ($monthsAgo) {
return $student->latestReport->date_to <= $monthsAgo;
});
}
Student.php
public function latestPhdReport()
{
return $this->hasOne(\App\Models\PhdReport::class)->latest('date_to');
}