Get Collection With HasOne Relationship - laravel-5

In my User model in Laravel 5.2 I have a relationship setup with their status to the company.
public function companyStatus()
{
return $this->hasOne('CompanyUser')->select('status');
}
The CompanyUser table has a company_id, user_id, and status field
Then in my controller I do the following:
$company = Company::find($company_id);
$users = CompanyUser::where('company_id', $company_id)->pluck('user_id')->toArray();
$user_data = User::with('companyStatus')->find($users);
but when I dump the user_data array it has all of the users related to the company, but just shows null for their status relationship
{
"id":2,
"name":"Moderator",
"email":"mod#company.com",
"created_at":"2016-09-08 15:26:20",
"updated_at":"2016-09-08 15:26:25",
"company_status":null
}
If I however return just the User collection to the view, and iterate over each user and run
$user->companyStatus->status
the value displays, but I am trying to include this within the collection for a JSON API to consume.
UPDATE
I tried adding the foreign key to the select call on my relationship method:
public function companyStatus()
{
return $this->hasOne('CompanyUser')->select('status', 'user_id');
}
and it now returns the following:
{
"id":2,
"name":"Moderator",
"email":"mod#company.com",
"created_at":"2016-09-08 15:26:20",
"updated_at":"2016-09-08 15:26:25",
"company_status": {"status":"1","user_id":"2"}
}
Not sure if this is the best/correct method or not though.

Okay I figured it out.
I tried adding the foreign key to the select call on my relationship method:
public function companyStatus()
{
return $this->hasOne('CompanyUser')->select('status', 'user_id');
}
Then that returns:
{
"id":2,
"name":"Moderator",
"email":"mod#company.com",
"created_at":"2016-09-08 15:26:20",
"updated_at":"2016-09-08 15:26:25",
"company_status": {"status":"1","user_id":"2"}
}
Without the foreign key Laravel obviously can't determine the related data on the other table.

Related

Why does this Eloquent relationship return null in a foreach loop?

I have a relationship that's acting weird. If I get the results and dd(), it has the correct data. However, if I run the collection through a foreach loop it's like the relationship disappears.
The users table has a field called referred_by, which stores the user ID of the person who referred the user. In my instance I'm looking for all the new users who were referred by the user with the ID of 3.
Here's the relationship in User.php
public function referrer()
{
return $this->belongsTo(User::class, 'referred_by');
}
Here's the code which is returning funky results
$users = User::where('referred_by', 3)
->with('referrer')
->get();
// doing dd() here returns a collection with full referrer relationship;
// the returned data is as expected
dd($users);
foreach($users as $user)
{
// dd($user) here returns the relationship, as it should
// dd($user->referrer) here returns null, like the relationship doesn't exist
}
Try with
foreach($users as $user)
{
// dd($user) here returns the relationship, as it should
dd($user['referrer'])
}
Rather then
// dd($user->referrer)
Turns out I had a database column on the users table named referrer that was causing an issue. I changed the relationship's method name from referrer() to referredBy() and it worked. *eyeroll*

Laravel 5.7 query with 3 tables

I'm pretty new to Laravel and i got stuck with building a more complex query:
I've 3 tables with their models:
Codes:
id
description
factor
public function purposes() {
return $this->hasMany('App\Purpose', 'code_purposes', 'code_id', 'purpose_id');
//I could be wrong here
}
Purpose:
id
name
description
Code_purposes:
code_id
purpose_id
public function codes() {
$this->belongsToMany('App\Code'); //I could be wrong here
}
public function purposes() {
$this->belongsToMany('App\Purpose'); //I could be wrong here
}
What I want is to fetch all the codes with the condition where the purposes name = 'some_name'
I thought this would be easy with the relationships, but I can't figure it out.
So how do I do this in Laravel?
In Code model:
public function purposes() {
return $this->belongsToMany('App\Purpose');
}
In Purpose model:
public function codes() {
return $this->belongsToMany('App\Code');
}
Now you can get data like:
$codes = Purpose::where('name', 'some_name')->first()->codes;
Relation table name must be code_purpose. And no need any model for this table.
Source: Laravel Many To Many Relationships

Laravel oneToMany accessor usage in eloquent and datatables

On my User model I have the following:
public function isOnline()
{
return $this->hasMany('App\Accounting', 'userid')->select('rtype')->latest('ts');
}
The accounting table has activity records and I'd like this to return the latest value for field 'rtype' for a userid when used.
In my controller I am doing the following:
$builder = App\User::query()
->select(...fields I want...)
->with('isOnline')
->ofType($realm);
return $datatables->eloquent($builder)
->addColumn('info', function ($user) {
return $user->isOnline;
}
})
However I don't get the value of 'rtype' for the users in the table and no errors.
It looks like you're not defining your relationship correctly. Your isOnline method creates a HasMany relation but runs the select method and then the latest method on it, which will end up returning a Builder object.
The correct approach is to only return the HasMany object from your method and it will be treated as a relation.
public function accounts()
{
return $this->hasMany('App\Accounting', 'userid');
}
Then if you want an isOnline helper method in your App\User class you can add one like this:
public function isOnline()
{
// This gives you a collection of \App\Accounting objects
$usersAccounts = $this->accounts;
// Do something with the user's accounts, e.g. grab the last "account"
$lastAccount = $usersAccounts->last();
if ($lastAccount) {
// If we found an account, return the rtype column
return $lastAccount->rtype;
}
// Return something else
return false;
}
Then in your controller you can eager load the relationship:
$users = User::with('accounts')->get(['field_one', 'field_two]);
Then you can do whatever you want with each App\User object, such as calling the isOnline method.
Edit
After some further digging, it seems to be the select on your relationship that is causing the problem. I did a similar thing in one of my own projects and found that no results were returned for my relation. Adding latest seemed to work alright though.
So you should remove the select part at very least in your relation definition. When you only want to retrieve certain fields when eager loading your relation you should be able to specify them when using with like this:
// Should bring back Accounting instances ONLY with rtype field present
User::with('accounts:rtype');
This is the case for Laravel 5.5 at least, I am not sure about previous versions. See here for more information, under the heading labelled Eager Loading Specific Columns
Thanks Jonathon
USER MODEL
public function accounting()
{
return $this->hasMany('App\Accounting', 'userid', 'userid');
}
public function isOnline()
{
$rtype = $this->accounting()
->latest('ts')
->limit(1)
->pluck('rtype')
->first();
if ($rtype == 'Alive') {
return true;
}
return false;
}
CONTROLLER
$builder = App\User::with('accounting:rtype')->ofType($filterRealm);
return $datatables->eloquent($builder)
->addColumn('info', function (App\User $user) {
/*
THIS HAS BEEN SUCCINCTLY TRIMMED TO BE AS RELEVANT AS POSSIBLE.
ARRAY IS USED AS OTHER VALUES ARE ADDED, JUST NOT SHOWN HERE
*/
$info[];
if ($user->isOnline()) {
$info[] = 'Online';
} else {
$info[] = 'Offline';
}
return implode(' ', $info);
})->make();

Laravel using pivot table to generate a raply from database in one request

I am using datatables so I need to make a request to my database and get a return in one json.
I have a table of users and a table that is linking the users to them to specify if they have a manager. I m doing this through a pivot table.
#User#
id
name
#User_user#
user_id
manager_id
I have the following model for the User.php:
public function managers()
{
return $this->belongsToMany('App\User', 'users_users' , 'user_id', 'manager_id')->withPivot('manager_type')->withTimestamps();
}
public function employees()
{
return $this->belongsToMany('App\User', 'users_users' , 'manager_id', 'user_id')->withPivot('manager_type')->withTimestamps();
}
In order to generate my data for Datatables, I am using:
$userList = $this->user->select('id', 'name','email','is_manager', 'region', 'country', 'domain', 'management_code', 'job_role', 'employee_type');
$data = Datatables::of($userList)->make(true);
This gives me the info I need except, I would like to add the manager so that I can use it in datatables. I don't understand how I can configure this in order to get this in 1 reply of the DB using Eloquent.

Laravel: How to write a join count query on belongsToMany relationships?

I got the following:
User, Role, with a role_user pivot table and a belongsToMany
relationship
User, Location, with a location_user pivot table and a belongsToMany relationship
There's 2 roles for the user: owner & gardener
Location has a 'gardeners_max' field
In model Location:
protected $appends = ['is_full'];
public function getIsFullAttribute()
{
return $this->attributes['name'] = $this->remainingGardeners() <= 0;
}
public function countGardeners()
{
return $this->gardeners()->count();
}
public function remainingGardeners()
{
return $this->gardeners_max - $this->countGardeners();
}
Now, doing that :
Location::all();
I get that :
[
{
name: 'LocationA',
gardeners_max: 3,
owners: [...],
garderners: [...]
...
is_full: false
}
]
which is cool. BUT... it's not possible to do a WHERE clause on the appended attribute.
Location::where('is_full',true)->get() // Unknown column 'is_full' in 'where clause'
So i'd like to write a join query so I can do a where clause on is_full
And I just can't find the way. Any help will be greatly appreciated!
IMPORTANT:
I know the filter() method to get the results but I need to do a single scopeQuery here
You could try to manipulate the Collection after loading the object from database:
Location::get()->where('is_full', true)->all();
(You have to use get first then all, not sure it works otherwise)
Not sure it's optimized thought.
You can make scope in your location model like this
public function scopeFull(Builder $query)
{
return $query->where('is_full', true);
}
Now you just get all location like this
Location::full()->get();

Resources