Laravel Eloquent: One to Many Accessor - php

How do I make One to Many Accessor ?
User Model :
public function histories()
{
return $this->hasMany('App\History');
}
History Model :
public function user()
{
return $this->belongsTo('App\User');
}
If I do like below, it will works.
User Model :
public function getHappyHistoryAttribute()
{
return $this->products()->happyHistory()->first()->text;
}
History Model :
public function scopeHappyHistory(Builder $builder)
{
return $builder->where(function (Builder $query) {
return $query->where('status', 'happy');
});
}
Controller :
public function show($id)
{
return view("my_view", [
'user' => User::find($id)
]);
}
Blade view :
{{ dd($user->happy_history) }}
It works !
But, How should I do if I want this :
{{ dd($user->histories->happy) }}
I try like
History Model :
public function scopeHappyHistory(Builder $builder)
{
return $builder->where(function (Builder $query) {
return $query->where('status', 'happy');
});
}
public function getHappyAttribute()
{
return $this->happyHistory->first()->text;
}
It doesn't works !
If I code like
{{ dd($user->histories->happy) }}
It will return
Property [happy] does not exist on this collection instance.
If I code like
{{ dd($user->histories()->happy) }}
It will return
Undefined property: Illuminate\Database\Eloquent\Relations\HasMany::$happy
Correct me If I wrong

It will not work like that because the relationship is oneToMany.
Try following in your User model
/**
* #return mixed
*/
public function happyHistory()
{
//Happy history is your scope form History model
//Following will not get you first model
return $this->histories()->happyHistory()->first();
}
That will give you first model and then you can do following from User model
$user->happyHistory->text
Not tested but this should work.
Also you dont need to pass the query builder in scope. You can do following
public function scopeStatus($query, $status = "happy")
{
return $query->where('status', $status);
}
Now you can use it for all status type.
History::status()->all(); //Will return all Happy history
History::status("sad")->all(); //Will return all Sad history
Hope this helps.

Related

How to fix the relationship problem of "Property [id] does not exist" problem

I have three model 1. Task.php 2. Project.php 3. User.php.
Task have a one to many relationship with user and project.php.
Task.php
public function project()
{
return $this->belongsTo(Project::class);
}
public function user()
{
return $this->belongsTo(User::class,'member_id');
}
User.php
public function tasks()
{
return $this->hasMany(Task::class);
}
Project.php
public function tasks()
{
return $this->hasMany(Task::class);
}
Nowi want to retrieve all the task that is associated with users where project id is one (Actually not one, id is passed through url).Here is my attempt
public function taskList($id)
{
$project=Project::with('tasks')->find($id);
$tasks=Task::has('user')->whereHas('project',function($project){
$project->where('id',$project->id);
})->get();
dd($tasks);
return view('leader.tasks',compact('project'));
}
In the above either i give the $id which is i get from the url either $project->id, it always through me the error.Please help me to solve this.
public function taskList($id)
{
$project=Project::with('tasks')->find($id);
$tasks=Task::has('user')->whereHas('project', function($query) use ($project) {
$query->where('id', $project->id);
})->get();
dd($tasks);
return view('leader.tasks',compact('project'));
}
You need to use with() method along with has()
public function taskList($id)
{
$project=Project::with('tasks')->find($id);
$tasks=Task::with('user')->has('user')->whereHas('project', function($query) use ($project) {
$query->where('id', $project->id);
})->get();
dd($tasks);
return view('leader.tasks',compact('project'));
}

Laravel function in User model that will be called for User::all()

Imagine I have this function in User model:
public function loadOrderStatus(){
$this->load(['status' => function ($query) {
$query->ofTranslatedStatuses();
}]);
}
This function works fine for single model instance like : User::first()
but how this function can be used with User::all() ?
You can use a scopeQuery in the User model
public function scopeStatusAll($query)
{
return $query->with(['status' => function ($query) {
$query->ofTranslatedStatuses();
}]);
}
call it with
User::statusAll()->get();
Or you could put a static function in User model
public static function statusAll()
{
$users = User::all();
return $users->load(['status' => function ($query) {
$query->ofTranslatedStatuses();
}]);
}
call it with
User::statusAll();

how to fix relationship error between models?

I have set up two models and made the relationship between them. I want to pass the attributes of the user as well as the user_detail.
I have used a similar code somewhere and it worked perfectly. But it is not working here.
//This is the function in "User.php" model.
public function user_detail(){
return $this->hasOne('App\Profile');
}
//This is the function in "Profile.php" model.
public function user(){
return $this->belongsTo('App\User');
}
//edit function in ProfileController
public function edit($id)
{
$user=User::find($id);
return view('profile.edit')->with('data',$user->user_detail);
}
When I click the edit button in the view, I expect the extract all the details from user table as well as from user_detail table.
I think you should edit your this code a little bit
public function edit($id)
{
$user=User::findOrFail($id);
return view('profile.edit')->with('data',$user);
}
And in your blade file (profile.edit), You can get all details from User and Profile Model.
{{ $data->id }}
{{ $data->user_detail->YOURPARAMETERS }}
The problem is with the relationship naming. Make it camelCase like,
//This is the function in "User.php" model.
public function userDetail(){
return $this->hasOne('App\Profile');
}
//edit function in ProfileController
public function edit($id)
{
$user=User::find($id);
return view('profile.edit')->with('data',$user->userDetail);
}
Reference: https://github.com/laravel/framework/issues/4307#issuecomment-42037712
try using where instead of find and then use with:
$user = User::where('id', $id)->with('user_detail')->first();
return view('profile.edit')->with('data', $user);
In your model:
public function user_detail(){
return $this->hasOne('App\Profile', 'student_no');
}

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 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