Laravel eloquent - one to one relation with different table - laravel

I have three tables,
1. user (id, name)
2. state (id, stateName, code)
3. user_relations(id, user_id, state_id)
3 models,
1. User
2. State
3. UserRelation
By logic, User has one UserRelation and UserRelation has one State
I can get user state by
User::first()->relation->state
What I want is call this by User::with('state')->first().

If you would like to keep the current db schema like it is, you may use Laravel accessors
Accessors and mutators allow you to format Eloquent attribute values
when you retrieve or set them on model instances.
class User
public function getStateAttribute($value) {
return $this->relation->state;
}
Usage
User::findOrFail($id)->state;
Edit
User Model
class User extends Authenticatable
{
//...
public function state() {
return $this->relation()->with('state');
}
public function relation() {
return $this->hasOne('App\UserRelation', 'user_id');
}
}
UserRelation Model
class UserRelation extends Model
{
protected $table = 'user_relations';
public function state() {
return $this->belongsTo('App\State'); // must be belongsTo instead of hasOne
}
}
State Model
class State extends Model
{
protected $table = 'states';
}
Controller
App\User::with('state')->find(1);
{
"id": 1,
"name": "Test",
"email": "test#test.com",
"email_verified_at": "2020-02-13 00:00:00",
"created_at": "2020-02-20 00:00:00",
"updated_at": null,
"state": {
"id": 2,
"state_id": 2,
"user_id": 1,
"state": {
"id": 2,
"stateName": "state 2",
"code": "code 2"
}
}
}

If user can only have one state,
add a state_id in the user table and get rid of the third table user_relations
class User extends Model
{
public function state()
{
return $this->hasOne('App\State');
}
}
class State extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
}
In this way, you can
$state = User::find(1)->state;
$user = State::find(1)->user;

First way:
You have to define state_id in users table for one to one relation.
class User extends Model
{
public function state()
{
return $this->hasOne('App\State');
}
}
Or if you need same database structure, then
Second Way:
Basically it's Many to Many relations between User and State.
So better way is to define many to many relation function in Models of User and State considering UserRelation a middle(pivot) table.
class User extends Model
{
public function states()
{
return $this->belongsToMany('App\State', 'user_relations');
}
}
class State extends Model
{
public function users()
{
return $this->belongsToMany('App\User', 'user_relations');
}
}
and then to access states of user, you can call:
$user->states->first();
It's ok that you need one to one relation, you can call first() method to fetch first record od state.
Simillery to access user of state
$state->users->first();
And if you need to access user_relations (one to one) only, you have to define methods in both user and state model:
class User extends Model
{
public function user_relation()
{
return $this->hasOne('App\UserRelation');
}
}
class State extends Model
{
public function user_relation()
{
return $this->hasOne('App\UserRelation');
}
}

Since user and state are many to many, you can user belongsToMany function inside User model.
public function states(){
return $this->belongsToMany(State::class, 'user_relations', 'user_id', 'state_id');
}
You can get Eager Loading by retrieving both models related to the user like:
$user = User::with('states')->first();
And now to retrieve states, you can loop through the states like:
foreach($user->states as $state){
//Retrieved state model variable
....
}

If you are doing one-to-one relationship you won't need a pivot table, in your case the user_relations table. Pivot tables are used to express many-to-many relations.
And for the solution you want. You will have to store the user_id in the state table.
so your state table should look like the following:
state (id, user_id, stateName, code)
Relations should be like:
User model:
public function state()
{
return $this->hasOne('App\State');
}
State model:
public function user()
{
return $this->belongsTo('App\User');
}

Related

Laravel many to many relationship with pivot

I'm using Laravel Filament.
I got a projects and responsibles tables in a many-to-many relationship. But also another table of responsabilityTypes
projects
id
title
responsibles
id
name
responsabilityTypes
id
name
project_responsible
project_id
responsible_id
responsibilityType_id
And here are my relationships setup:
Responsible.php
public function projects() {
return $this->belongsToMany(Project::class,'rel_project_responsible','responsible_id','project_id')
->withPivot('responsibilityType_id')
->withTimestamps()
->using(AcademicoProyecto::class);
}
Project.php
public function responsibles() {
return $this->belongsToMany(Responsible::class,'rel_project_responsible','project_id','responsible_id')
->withPivot('responsibilityType_id','sort')
->withTimestamps()
->using(AcademicoProyecto::class);
}
I have set up a class for the pivot table like so:
ProjectResponsible.php
use Illuminate\Database\Eloquent\Relations\Pivot;
class AcademicoProyecto extends Pivot
{
}
ResponsibilityType.php
//Don't know how to set up
My question is, when the user is in a Project Edit page and clicks on the "attach" button, in order to add a Responsible record, a Modal pops up to select a Responsible, but I also implemented a Select list to display the different types of responsibilities.
What am I missing to set up in order to access and display the types of responsibilities in the select list and attach it to the pivot table?
Your question asks about "access and display" but you have no controller or view code. But for the model, it's just a simple relationship between two tables, so define it as such:
class AcademicoProyecto extends Pivot
{
use SoftDeletes;
public function responsibilityType() {
return $this->belongsTo(ResponsibilityType::class);
}
}
class ResponsibilityType extends Model
{
protected $fillable = ["name"];
}
Now you simply update the other models to access the relationship in the withPivot() call.
class Responsible extends Model {
public function projects() {
return $this->belongsToMany(Project::class,'rel_project_responsible','responsible_id','project_id')
->withPivot('responsibilityType')
->withTimestamps()
->using(AcademicoProyecto::class);
}
}
class Project extends Model {
public function responsibles() {
return $this->belongsToMany(Responsible::class,'rel_project_responsible','project_id','responsible_id')
->withPivot('responsibilityType', 'sort')
->withTimestamps()
->using(AcademicoProyecto::class);
}
}
Now you should be able to do, for example:
$foo = Responsible::with("projects")->first();
foreach ($foo->projects as $project) {
echo $project->pivot->responsibilityType?->name;
}

Getting multiple related models together

In my Laravel app, I have three models, User, Course and CourseScore. I want to get all courses for an specific user with his score. something like:
{
id: 1,
name: first_name,
corses: [
{
id: 1,
name: 'course one',
score: 17, // this is what i need for every course,
},
{
id: 2,
name: 'course two',
score: 19, // this is what i need for every course,
},
]
}
Here are my Models:
User
<?php
class User extends Authenticatable
{
public function courseScores()
{
return $this->hasMany(CourseScore::class);
}
public function courses()
{
return $this->belongsToMany(Course::class);
}
}
Course
<?php
class Course extends Model
{
public function courseScores()
{
return $this->hasMany(CourseScore::class);
}
public function users()
{
return $this->belongsToMany(User::class);
}
}
CourseScore
<?php
class CourseScore extends Model
{
protected $table = 'course_scores';
public function course()
{
return $this->belongsTo(Course::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
And of course, please let me know if the relations are incorrect.
If I understand it correctly, you can do something like this,
in your User model
public function courses(){
return $this->hasMany(Course::class); // changed belongsToMany to hasMany
}
in your Course model
public function users(){
return $this->belongsTo(User::class); // changed belongsToMany to belongsTo
}
In your controller
$usersData = User::with('courses.courseScores')->get();
dd($usersData);
I dont fully understand your table structure so Im assuming you have multiple row of Scores for single Course (One to Many).
If the relationship is one is to one then add in your Course Model
public function courseScores(){
return $this->hasOne(CourseScore::class); //changed hasMany to hasOne
}
You can try using this
$val = DB::table('course as c')
->join('user as u', 'u.id','c.id')
-join('courseScore as cs','u.id','cs.id')
->get();
Then just dd($val) to see what value it shown

laravel model relationship seems backwards compared to docs

So I am setting up a one to one relationship between MyModel and the users table.
MyModel obviously has a user_id column to tie back to the users.
However - when i go to setup the relationship in MyModel I have to set it up in a way which seems backward!
This is in MyModel:
public function user()
{
return $this->hasOne('App\User', 'id', 'user_id');
}
Why Am i having to set the opposite foreign and local keys... ? Am i missing something?
Do it like this
class MyModel {
public function user()
{
return $this->belongsTo('App\User');
}
}
class User {
public function myModel()
{
return $this->hasOne('App\MyModel');
}
}
And that should work as intended (one to one) relationship

Laravel how to define 3 relationships with one model?

I have a Model which is called Championship. Championship may have 3 judges which are called Main Judge, Main Secretary and Judge Operator.
All of them linked to User Model and stored in the database as user ID.
My relationships looks like this
class Championship extends Model
{
protected $table = 'championships';
public function mainJudge()
{
return $this->hasOne('App\User', 'id', 'main_judge');
}
public function mainSecretary()
{
return $this->hasOne('App\User', 'id', 'main_secretary');
}
public function judgeOperator()
{
return $this->hasOne('App\User', 'id','judge_operator');
}
}
But I can't undertand how to define inverse relationship in User model
class User extends Authenticatable
{
public function sex()
{
return $this->belongsTo('App\Models\Sex');
}
public function player()
{
return $this->hasOne('App\Models\Player', 'user_id');
}
public function championship()
{
????
}
You just have to add it like you are adding other relations :
public function championship()
{
return $this->belongsTo('App\Championship');
}
When you do :
$championship = Championship::find($id);
$mainJudge = $championship->mainJudge;
$mainSecretary = $championship->mainSecretary;
// All objects will be exactly same
dd($mainJudge->championship,$mainSecretary->championship,$championship);
I assume all the user records have a foreign key to championships table championship_id
When you call the $user->championship relation it will return you the championship wrt to its foreign key championship_id
No need to worry you are just confusing the inverse relations:
See it this way:
Your mainJudge, mainSecretary, judgeOperators are of type App\User and every user have a championship_id when you will call the (App\User)->championship it will always return you its respective championship or null if the championship_id is empty.
Its just matter of perspective.
Just try the above code it will clear out your confusion.

Laravel eloquent relationships, 3 way join

I have 3 tables (simplified here)
users
id, name, email
teams
id, name
team_user
team_id, user_id
I want to send an API query to return all teams a user Id belongs to, and what other members are also in that team. Rather than returning just userIds, I want to fill the arrays with their actual user data, e.g name and email.
Route::get('/user/{id}/teams/', 'UserController#getTeams');
User.php (model)
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
public function teams()
{
return $this->belongsToMany('App\Team', 'team_user', 'team_id', 'user_id');
}
}
Team.php (model)
class Team extends Model
{
public function users()
{
return $this->belongsToMany('App\User', 'team_user', 'team_id', 'user_id');
}
}
TeamUser.php (model)
class TeamMember extends Model
{
public function user()
{
return $this->hasOne('App\User');
}
public function team()
{
return $this->hasOne('App\Team');
}
}
UserController.php
class UserController extends Controller
{
public function getTeams($userId) {
return User::find($teamId)->teams()->get();
}
}
This returns:
[
{
"id": 6,
"name": "P12",
"location": "Newbury",
"owner": 6,
"active": 1,
"created_at": "2017-12-20 10:18:00",
"updated_at": "2017-12-20 10:18:00",
"pivot": {
"team_id": 6,
"user_id": 6
}
},
{
"id": 4,
"name": "fwffewfffffff",
"location": "ffffffff",
"owner": 4,
"active": 1,
"created_at": "2017-12-19 19:56:27",
"updated_at": "2017-12-19 19:56:27",
"pivot": {
"team_id": 6,
"user_id": 4
}
}
]
However I would like to also include a list of other users in each of those 2 teams. With their names and emails (from the users table), not just the user_ids.
Is this possible without doing further seperate queries?
You would be able to eagerly load the relations and attach them to the model.
By using the dot notation teams.users, the output will include all users of all teams attached to user with id $userId.
I've added the findOrFail to make sure it will return a 404 when user can not be found.
class UserController extends Controller
{
public function getTeams($userId)
{
return User::with('teams.users')->findOrFail($userId);
}
}
This will return the User and attach the relations.
If you want to just return the teams, you can do something like this:
class UserController extends Controller
{
public function getTeams($userId)
{
$user = User::with('teams.users')->findOrFail($userId);
// $user->teams will hold a collection of teams with their users
return $user->teams;
}
}
Try using with() to retrieve the fields from different tables, for example
class UserController extends Controller {
public function getTeams($userId) {
return User::find($userId)->with('teams')->get();
// return User::find($userId)->teams()->get();
}
}
If you would like to select specific column from the team_members database, you could add function inside with, for example
class UserController extends Controller {
public function getTeams($userId) {
return User::find($userId)->with(['teams' => function($query) {
$query->select('id', 'name');
}])->get();
}
}
For now I have solved this with
class UserController extends Controller
{
public function getTeams($userId) {
$teamWithMembers = [];
$teams = User::find($userId)->teams()->get();
foreach($teams as $team) {
$team->members = Team::find($team->id)->users()->get();
$teamWithMembers[] = $team;
}
return $teamWithMembers;
}
}

Resources