Eloquent model's relations - php

I'm developing a simple survey system, and I'm having problem with getting the right data.
I'm trying to retrieve all categories with questions and answers, that are assigned to a specific survey.
ERD:
The following code nearly works, however it does not filter the questions that are assigned to a specific survey.
$categories = Category::whereHas('questions.surveys', function ($query) use ($id) {
$query->where('surveys.id', $id);
})->with('questions', 'questions.answers', 'questions.surveys')
->get();
Question Model:
class Question extends Model
{
public function answers()
{
return $this->belongsToMany('App\Models\Surveys\Answer', 'question_answers');
}
public function category()
{
return $this->belongsTo('App\Models\Surveys\Category');
}
public function surveys()
{
return $this->belongsToMany('App\Models\Surveys\Survey', 'survey_questions');
}
}
Category Model:
class Category extends Model
{
public function questions()
{
return $this->hasMany('App\Models\Surveys\Question');
}
}
Survey Model
class Survey extends Model
{
public function questions()
{
return $this->belongsToMany('App\Models\Surveys\Question', 'survey_questions');
}
}

For this you need to constrain your eager loads as well:
$categories = Category::with([
'questions' => function ($query) use ($id) {
$query->with('answers', 'surveys')
->whereHas('surveys', function ($query) use ($id) {
$query->where('id', $id);
});
},
])->whereHas('questions.surveys', function ($query) use ($id) {
$query->where('id', $id);
})->get();
This way you're saying only get you the categories that are related to a specific survey and only get the question that relate to that category and the specific survey.

Related

Sub-relationship in Laravel 5?

Currently I've got 3 models, Listing, Offer & Payment which have the following relationships:
Listing
class Listing extends Model {
public function offers() {
return $this->hasMany(\App\Models\Offer::class)->orderBy('created_at', 'desc');
}
}
Offer
class Offer extends Model {
public function payment() {
return $this->hasOne(\App\Models\Payment::class, 'item_id', 'id')->where('item_type', \App\Models\Offer::class)->where('status', '1');
}
public function listing() {
return $this->belongsTo(\App\Models\Listing::class)->withTrashed();
}
}
Payment
class Payment extends Model {
public function offer() {
return $this->belongsTo(\App\Models\Offer::class, 'item_id', 'id')->withTrashed();
}
}
How can I go from them Listing model & return a relationship with the payments table directly?
Listing can have unlimited amounts of Offer but Offer can only have 1 max Payment
To find any corresponding payment information, I'm having to query the Offer based on the listing_id within the model, and then access the Offer->payment, when I'd much prefer to be able to just do something like this:
$transaction_id = $id;
$listing = Listing::whereHas('payment', function($q) use ($id) {
$q->where('transaction_id', $id);
$q->where('user_id', Auth::user()->id);
})->first();
Use HasManyThrough:
public function payments() {
return $this->hasManyThrough(Payment::class, Offer::class, null, 'item_id')
->where('payments.item_type', Offer::class)
->where('payments.status', '1')
->orderBy('offers.created_at', 'desc');
}

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');

Laravel 5.2 polymorphic model return all where relationship has value

I have a polymorphic model, named FeedItem which is has polymorphic relationships to an Alert and Publication model.
There is also another model, named Category, which the Alert and Publication models have a hasMany relationship to.
FeedItem
public function feedable()
{
return $this->morphTo();
}
Publication
public function feedItem()
{
return $this->morphOne('FeedItem', 'feedable');
}
public function categories()
{
return $this->hasMany(Category::class);
}
Alert
public function feedItem()
{
return $this->morphOne('FeedItem', 'feedable');
}
public function categories()
{
return $this->hasMany(Category::class);
}
I want to be able to get all FeedItems where the feedable models have a given category.
I've tried:
$items = FeedItem::with(['feedable.categories' => function($item) {
$item->whereHas('feedable.categories', function ($q) {
$q->where('categories.name', 'my-category');
})->orderBy('id', 'desc')
->paginate();
However, the issue is that the feedable relation never gets eager loaded, which prevents the categories relationship scoping from being performed.
For example, if I do this:
$items = FeedItem::with(['feedable.categories' => function($item) {
dd('This should be displayed');
$item->whereHas('feedable.categories', function ($q) {
$q->where('categories.name', 'my-category');
})->orderBy('id', 'desc')
->paginate();
The dd() statement is never called.
Can anyone provide a way of doing this sort of query?

Laravel relation returning empty

I want to get all the books under a certain category. The category has many subjects and books are linked to subjects.
My code:
class BookCategory extends Model {
public function subjects() {
return $this->hasMany('BookSubject', 'category_id', 'id');
}
public function getBooksAttribute() {
return Books::whereHas('subjects', function ($query) {
return $query->join('book_category', 'book_category.id', '=', 'book_subject.subject_id')
->where('book_category.id', $this->id);
})->get();
}
}
and my Books model:
class Books extends Model {
public function subjects() {
return $this->belongsToMany('BookSubject', 'book_by_subject', 'book_id', 'subject_id');
}
}
If I do:
$cats = \App\Models\BookCategory::all();
foreach ($cats as $c) {
echo $c->books->count();
}
It's always returning 0 for all the rows. What I'm I doing wrong?
I believe the problem is in your subquery:
return $query->join('book_category', 'book_category.id', '=', 'book_subject.subject_id')
->where('book_category.id', $this->id);
I'm pretty sure book_subject.subject_id should be book_subject.category_id
Hard to tell without seeing your db schema.

Laravel 4.1 eager loading

I'm having trouble on the eager loading.
Let's say I have models of Members, TrainingCategory, TrainingCategoryResult and Registration
Member Model:
public function registration() {
return $this->hasMany('Registration', 'member_id');
}
public function trainingResults(){
return $this->hasMany('trainingResult', 'member_id');
}
public function trainingCategoryResults() {
return $this->hasMany('TrainingCategoryResult', 'member_id');
}
TrainingCategory Model:
public function trainings() {
return $this->hasMany('Training', 'id');
}
public function trainingCategoryResults() {
return $this->hasMany('trainingCategoryResult', 'category_id');
}
TraningCategoryResult Model:
public function category() {
return $this->belongsTo('TrainingCategory', 'id');
}
public function member() {
return $this->belongsTo('Member', 'id');
}
Registration Model:
public function course() {
return $this->belongsTo('Course', 'course_id');
}
public function member() {
return $this->belongsTo('Member', 'id');
}
I am trying to eager load all the registration info and its related info including the TraningCategoryResult info but I not sure how to get that TraningCategoryResult which required two foreign keys (category_id and member_id), is there any way to do that?
Here is my code atm:
$members= Member::where(function($query) use ($id, $site) {
$query
->where('id', '=', $id)
->where('site', '=', $site);
});
$members= $members
->with('registration.course',
'registration.course.traningCategories',
->get(['member.id']);
Thank you.
This will not work Member::with('categoryResult')->with('registration')->get()
You can make a new relation in Member Model
public function categoryResult()
{
return $this->belongsTo('Category')->with('Registration');
}
//and then call
Member::with('categoryResult')->get();
You could use a few options to achieve that:
OPTION 1: create a variable relationship
Change your relation in the Member model
public function trainingCategoryResults($category_id = null) {
if(empty($category_id))
return $this->hasMany('TrainingCategoryResult', 'member_id');
else
return $this->hasMany('TrainingCategoryResult', 'member_id')
->where('category_id', $category_id);
}
The code above might have limitations and it doesn't take advantage of many laravel features, but it will work.
OPTION 2: Access from relationship
You can keep everything as is, and load as follow:
$members= Member::where(function($query) use ($id, $site) {
$query
->where('id', '=', $id)
->where('site', '=', $site);
})
->where('id', $member_id) // set the id of the memeber
->with(array(
'traningCategoryResults' => function($q)use($category_id){
$q->where('category_id', $category_id); // This makes sure you get only desired results
}
))
In this way you will have only what you need, assuming you know the $category_id

Categories