Laravel Eloquent Query containing pivots and relations - laravel

I want to make graphs based on values i get from my questionnaire. This is how my database looks like (Only showing the ones I need):
questionnaires
id
client_id
questionnaire_answers
id
questionnaire_id
questionnaire_question_id
answer
questionnaire_questions
id
question
What I want is basically get all the answers of question 1 (of all their questionnaires) from a specific client.
The idea is that they answer a question that is based on severity, so 1 - 5 basically.
I've tried this in the controller:
$questionnaires = JsonResource::collection(
Questionnaire::where('client_id', '=', 2)
->with([
'clients',
'questionnaire_answers',
'questionnaire_answers.questionnaire_question',
'questionnaire_answers.questionnaire_question.questionnaire_question_type'
])
->get()
);
dd($questionnaires);
All relations work.

What I want is basically get all the answers of question 1 (of all their questionnaires) from a specific client. The idea is that they answer a question that is based on severity, so 1 - 5 basically.
I'd redo the query so it starts from the Question model.
Assuming the following relationships:
Question belongsToMany Questionnaire (using Answer as the pivot model)
// Question model
public function questionnaires()
{
return $this->belongsToMany(Questionnaire::class)
->withPivot('answer')
->as('answer')
->using(Answer::class);
}
$client_id = ...;
$question = Question::query()
->whereHas('questionnaires', function ($questionnaire) use ($client_id) {
$questionnaire->where('client_id', $client_id);
})
->with([
'questionnaires' => function ($questionnaire) use ($client_id) {
$questionnaire->where('client_id', $client_id)
->with('client');
},
'question_type',
])
->find(1);
foreach ($question->questionnaires as $questionnaire) {
$questionnaire->client->... // client attributes
$question->.... // question attributes
$questionnaire->answer->... // answer attributes.
$questionnaire->question_type->... // question_type attributes
}

Related

How can I link 2 existing models in my laravel code?

I have two models customer and orders. They are already fecthed separately
$customers = customer::all();
$orders = orders::all();
customerID=1 has orderID : 1, 2,4 customerID=2 has orderID : 3,5,9
They are related (hasMany, belongsTo) but the problem is inside my for a certain reason they are separated but I want to send them as response in API using toJson or ToArray as one data having the orders nested to their correct customers.
How can I achieve that linking to have at the end one variable $customersWithOrders that should be transformed to JSON ?
I am using laravel 5.5
I don't know what the context is. Defining relationships as other answers mentioned is a good solution.
In addition, I recently read a pretty good article about this specific scenario.
So you can also do something like this, if you have already retrieved customers and orders:
$customers = Customer::all();
$orders = Order::all();
return $customers->each(function ($customers) use ($orders) {
$customer->setRelation('orders', $orders->where('customer_id', $customer->id));
});
If you already have a relation you just use it. For example, in model Customer.php:
public function orders()
{
return $this->hasMany(Order::class);
}
Then you'd get customer orders by calling $customer->orders
If you already have defined relations, you can simply fetch data with eager loading
// in customer model
public function orders()
{
return $this->hasMany(orders::class, 'orderID');
}
// in controller
$customersWithOrders = customer::with('orders')->get();
return response()->json(['customersWithOrders' => $customersWithOrders]);
// in js
for (let customer in response.customersWithOrders){
let orders = customer.orders
}

Laravel - insert multiple rows with pivot table data

I have 3 tables:
Questions
Answers
question_answers
Question Model:
public function answer()
{
return $this->belongsToMany(Answer::class);
}
I created a page where I type a question and 4 answers, and I insert them to my database.
$question = new Question;
$question->title = $request->question_title;
$question->save();
$answers = $request->answers;
$answer = Answer::insert($answers);
How I can insert the question_answers for each question too?
question_answers looks like this:
I couldn't find a clue how to do it with the insert method
Since you're using many-to-many relationship, you should use attach() method. For example:
$question = Question::create($request->question); // Save question.
$answersIds = [];
foreach ($request->answers as $answer) {
$answersIds [] = Answer::create($answer)->id; // Save each answer.
}
$question->answers()->attach($answersIds); // Attach answers to the question.
Also you can't use insert() to bulk insert answers, because you need to get answer IDs to attach answers to the question.

Creating a Many-to-many relationship in Laravel with additional data

I have in my database a pivot table that stores extra information. It has 2 foreign keys, and an additional field. Here's what it looks like:
EventTeam
int event_id (fk)
int team_id (fk)
boolean home
The intent here is that an Event may have many teams (in fact, it must have at least 2, but that's not a database constraint), and a team may participate in many events. However, for each event-team relationship, I want to also track whether the team is considered the home team for that event.
How do I define my model with this in mind? Do I have an EventTeam model at all, or do I define a belongsToMany relationship in both the Team and Event models? If I need a separate model, what relationships do I define in it? If I don't, how do I add the boolean field to the pivot table that gets used? I really have no idea how to do this.
You dont need a EventTeam model per se, but it could come in handy for seeders or if you are going to attach models to your EventTeam connection anywhere else in your app. This should work:
Event model:
public function teams()
{
return $this->belongsToMany('Team');
}
Team model:
public function events()
{
return $this->belongsToMany('Event');
}
For the extra boolean you can use ->withPivot().
$this->belongsToMany('Event')->withPivot('is_home');
See http://laravel.com/docs/eloquent#working-with-pivot-tables for more info.
Updated answers:
1) I would put it in both models so you can access the pivot data from both sides without a problem.
2) It should be to column name indeed.
3) Like i said its not really needed for you in this situation, but you could do this:
EventTeam model:
public function event()
{
return $this->belongsTo('Event');
}
public function team()
{
return $this->belongsTo('Team');
}
Add withPivot('home') on your relations definitions, then you can access it like this:
$team->events->first()->pivot->home; // 0/1
$event->teams->first()->pivot->home; // 0/1
first is just an example of getting single related model here.
Now, next thing is adding that value to the relation:
$team = Team::find($id);
$event = Event::find($eventId);
$team->events()->attach($event, ['home' => 1]);
// or
$team->events()->attach($eventId, ['home' => 1]);
// or using sync
$event->teams()->sync([1,5,15], ['home' => 0]);
Another thing is querying that field:
// load first team and related events, that the team hosts
$team = Team::with(['events'=>function ($q) {
$q->wherePivot('home', 1);
}])->first();
// load only teams that are hosts for any event
$hostTeams = Team::whereHas('events', function ($q) {
// wherePivot won't work here!
$q->where('event_team.home', 1);
})->get();
and so on.

Laravel 4 - Finding out if an M:N relation exists between 2 models

I have 2 models, one is a Poll, and another is an User. I have set up a M:N relationship between both of them:
User.php
public function votedPolls() {
return $this->belongsToMany('Polls', 'votes');
}
Poll.php
public function voters() {
return $this->belongsToMany('User', 'votes');
}
And it all works nicely. When I vote on a poll, the table votes get populated properly. But now I want to check inside the controller if a user has already voted in a poll.
I figured it would be something like this, but I am not sure of the syntax. I tried this (which does not work):
$voters = $poll->voters()->where('id', '=', $user->id)->first();
$voted = count($voters) == 1;
How could I achieve this?
count is preferred way of checking if relations exist.
Your code didn't work because your pivot table also has id column and where clause was ambiguous, so change it to:
where('users.id','=',$user->id)

Filtering eager-loaded data in Laravel 4

I have the following setup:
Clubs offer Activities, which are of a particular Type, so 3 models with relationships:
Club:
function activities()
{
return $this->hasMany('Activity');
}
Activity:
function club()
{
return $this->belongsTo('Club');
}
function activityType()
{
return $this->hasMany('ActivityType');
}
ActivityType:
function activities()
{
return $this->belongsToMany('Activity');
}
So for example Club Foo might have a single Activity called 'Triathlon' and that Activity has ActivityTypes 'Swimming', 'Running', and 'Cycling'.
This is all fair enough but I need to show a list of ActivityTypes on the Club page - basically just a list. So I need to get the ActivityTypes of all the related Activities.
I can do that like so from a controller method that receives an instance of the Club model:
$data = $this->club->with(array('activities', 'activities.activityTypes'))->find($club->id)
That gets me an object with all the related Activities along with the ActivityTypes related to them. Also fair enough. But I need to apply some more filtering. An Activity might not be in the right status (it could be in the DB as a draft entry or expired), so I need to be able to only get the ActivityTypes of the Activities that are live and in the future.
At this point I'm lost... does anybody have any suggestions for handling this use case?
Thanks
To filter, you can use where() as in the fluent DB queries:
$data = Club::with(array('activities' => function($query)
{
$query->where('activity_start', '>', DB::raw('current_time'));
}))->activityType()->get();
The example which served as inspiration for this is in the laravel docs, check the end of this section: http://laravel.com/docs/eloquent#eager-loading
(the code's not tested, and I've taken some liberties with the property names! :) )
I think if you first constraint your relationship of activities, the activity types related to them will be automatically constrained as well.
So what I would do is
function activities()
{
return $this->belongsToMany('Activity')->where('status', '=', 'active');
}
and then your
$data = $this->club->with(array('activities', 'activities.activityTypes'))->find($club->id)`
query will be working as you would expect.

Resources