Eloquent pivot model relationships - php

I'm a little confused about how to define a relationship in a Laravel pivot model with extra fields. Here are the table migrations ...
Schema::create('users', function($table) {
$table->increments('id');
$table->string('password', 60);
$table->string('first_name', 50);
$table->string('last_name', 50);
$table->string('email', 255)->unique();
$table->timestamps();
});
Schema::create('accounts', function($table) {
$table->increments('id');
$table->integer('user_id')->unsigned()->unique();
$table->boolean('active')->default(1);
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('restrict')->onUpdate('cascade');
});
Schema::create('roles', function($table) {
$table->increments('id');
$table->string('name');
});
Schema::create('role_user', function($table) {
$table->integer('role_id')->unsigned();
$table->integer('user_id')->unsigned();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
});
Schema::create('account_role', function($table) {
$table->integer('account_id')->unsigned();
$table->integer('user_id')->unsigned();
$table->integer('role_id')->unsigned();
$table->timestamps();
$table->unique(array('account_id','user_id'));
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade')->onUpdate('cascade');
});
Now you might be wondering "what the heck, why is there a role_user table and a account_role table?" I'll explain.
I have users (free) and accounts (paid). Every account is owned by a user. I also have roles that are site-wide like "admin" in the RolesUsers table. Our business logic requires that account-holders can assign roles to other users for working with the assets owned by the account. In other words if I have a paid account and I want my friend Joe to be able to have some capability to assist with managing my account, I as the account owner can grant him access by assigning him a role on my account. That's what the AccountRoles model is for. As such the account_role table has three fields, user_id, role_id, account_id.
Where I'm stuck is that I can't seem to access the Account model from the pivot. I can access the Account ID. Here are the relevant models:
class User extends Eloquent implements UserInterface, RemindableInterface {
protected $table = 'users';
protected $hidden = array('password');
protected $guarded = array('id');
public function Account() { return $this->hasOne('Account', 'user_id'); }
public function AccountRoles() { return $this->belongsToMany('Role', 'account_role')->withPivot('account_id')->withTimestamps(); }
public function Roles() { return $this->belongsToMany('Role', 'role_user')->withTimestamps(); }
}
class Account extends Eloquent {
protected $table = 'accounts';
public function User() { return $this->belongsTo('User', 'user_id'); }
public function AccountRoles() { return $this->belongsToMany('Role', 'account_role')->withPivot('user_id')->withTimestamps(); }
}
class AccountRole extends Eloquent {
protected $table = 'account_role';
protected $fillable = array();
public function Account() { return $this->belongsTo('Account', 'account_id'); }
}
Here's what works:
$user = User::find($id);
foreach($user->AccountRoles as $account_role)
{
echo 'Account ID: ' . $account_role->pivot->account_id . '<br/>';
echo 'Role: ' . $account_role->name . '<br/>';
}
When I ...
var_dump($account_role->pivot->Account);
... I get "null".
I have also tried ...
$account_role->pivot->Account;
$account_role->pivot->Account();
$account_role->Account;
$account_role->Account();
I have also tried hasOne() rather than belongsTo(). I'm totally lost.
I need $account_role->pivot->Account to return the Account object. Clearly I'm not totally messed up because $account_role->pivot->account_id returns the correct account ID.
What am I missing or where did I mess up? How do I correctly define the relationship in the pivot model to return the Account model???
Thanks in advance for any help you have to offer!

Related

Laravel: Order a model by "Has One Of Many" relationship

I have a customer model that has many contacts. I defined a relationship to get the most recent contact of the customer using the "Has One Of Many" relationship in Laravel 8:
Models
class Customer extends Model
{
use HasFactory;
public function contacts()
{
return $this->hasMany(Contact::class);
}
public function latestContact()
{
return $this->hasOne(Contact::class)->ofMany('contacted_at', 'max')->withDefault();
}
}
class Contact extends Model
{
use HasFactory;
protected $casts = [
'contacted_at' => 'datetime',
];
public function customer()
{
return $this->belongsTo(Customer::class);
}
}
Migration (contact model)
class CreateContactsTable extends Migration
{
public function up()
{
Schema::create('contacts', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->softDeletes();
$table->foreignID('customer_id');
$table->string('type');
$table->dateTime('contacted_at');
});
}
}
In my view, I want to show all customers and order them by their latest contact. However, I can't figure out how to do that.
I tried to achieve it via the join method but then I obviously get various entries per customer.
$query = Customer::select('customers.*', 'contacts.contacted_at as contacted_at')
->join('contacts', 'customers.id', '=', 'contacts.customer_id')
->orderby('contacts.contacted_at')
->with('latestContact')
Knowing Laravel there must be a nice way or helper to achieve this. Any ideas?
I think the cleanest way to do this is by using a subquery join:
$latestContacts = Contact::select('customer_id',DB::raw('max(contacted_at) as latest_contact'))->groupBy('customer_id');
$query = Customer::select('customers.*', 'latest_contacts.latest_contact')
->joinSub($latestContacts, 'latest_contacts', function ($join){
$join->on([['customer.id', 'latest_contacts.customer_id']]);
})
->orderBy('latest_contacts.latest_contact')
->get();
More info: https://laravel.com/docs/8.x/queries#subquery-joins
I suspect there is an issue with your migration, the foreign key constraint is defined like this:
Check the documentation:
https://laravel.com/docs/8.x/migrations#foreign-key-constraints
Method 1: define foreign key constraint
public function up()
{
Schema::create('contacts', function (Blueprint $table) {
$table->id();
$table->foreignId('consumer_id')->constrained();
$table->string('type');
$table->dateTime('contacted_at');
$table->timestamps();
$table->softDeletes();
});
}
Method 2: define foreign key constraint
public function up()
{
Schema::create('contacts', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('customer_id');
$table->foreign('customer_id')->references('id')->on('customers');
$table->string('type');
$table->dateTime('contacted_at');
$table->timestamps();
$table->softDeletes();
});
}

Eloquent many to many relationship is always empty

I know this question has been asked a lot but all answers didn't seem to work for me - or at least the questions I found were about the pivot table.
I have a many to many relationship (User - Appointment) which is joined by the pivot table "apointment_user", see migrations below.
Schema::create('appointment_user', function (Blueprint $table) {
$table->unsignedInteger('user_id')->nullable();
$table->foreign('user_id')->references('id')->on('users');
$table->unsignedInteger('appointment_id')->nullable();
$table->foreign('appointment_id')->references('id')->on('appointments');
$table->primary(['user_id','appointment_id']);
$table->timestamps();
});
Schema::create('appointments', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->dateTime('date');
$table->string('location');
$table->dateTime('departure');
$table->timestamps();
});
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->date('last_login')->nullable();
$table->rememberToken();
$table->timestamps();
$table->softDeletes();
});
class User extends Model {
protected $with = ['appointments'];
public function appointments() : BelongsToMany {
return $this->belongsToMany(Appointment::class);
}
}
class Appointment extends Model {
public function users() : BelongsToMany {
return $this->belongsToMany(User::class);
}
}
I have a user with the ID 1 and about 10 appointments, which I do attach to the relationship in a seeder. The pivot table has 10 records, as intended (User ID is always 1).
However, if I dump my User object using dd(User::find(1)), the relationship is always an empty collection. However, a 1:n relationship (between a role works well).
Does anybody see what I'm missing? Any help is appreciated.
Many thanks and kind regards
Edit
I just tried some other kind of dumping. I've simply returned my User-Object as JSON-response and there the relationship is filled with 10 appointments... strange.
Though it seems that your table and column names are as Laravel would guess, have you tried expliciting the names?
class User extends Model {
protected $with = ['appointments'];
public function appointments() : BelongsToMany {
return $this->belongsToMany(Appointment::class, 'appointment_user', 'user_id', 'appointment_id');
}
}
class Appointment extends Model {
public function users() : BelongsToMany {
return $this->belongsToMany(User::class, 'appointment_user', 'appointment_id', 'user_id');
}
}

Laravel one to many relationship results

Hello i am trying to create one to many relationship. One user from user table could have many companies, on other side company could have only one user.
my migration for company table is
public function up()
{
Schema::create('companies', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('address');
$table->string('city');
$table->string('state');
$table->string('contact_person');
$table->string('phone');
$table->string('industry');
$table->string('website');
$table->integer('id_user')->unsigned();
$table->foreign('id_user')->references('id')->on('users')->onUpdate('cascade')->onDelete('cascade');
$table->timestamps();
});
}
my user model is
/**
* Get the posts for the user.
*/
public function companies()
{
return $this->hasOne('App\Company','user_id');
}
my company model is
public function users()
{
return $this->belongsTo('App\User','id');
}
i am trying to get all companies for specific user
try with whereHas but no data in relation object
$results = Company::whereHas('users', function ($query) {
$query->where('users.id',1);
})->get();
Where is my error?
First thing you should change is companies() relation. It has to be hasMany, not hasOne:
public function companies()
{
return $this->hasMany('App\Company');
}
Then get user with all his companies:
$result = User::where('id', $id)->with('companies')->first();
Or, if you want to use Company model like in your example and get only companies:
$result = Company::where('user_id', $id)->get();
Also, you're using id_user in migration. Change it to user_id.

How to define relation in laravel 5.3 eloquent model?

I have tables in my database schema as follows,
Is it pivot table?
How can I define relationship in eloquent model?
class Role extends Model {
public $timestamps = false;
public function permissions() {
return $this->hasMany('App\Models\RolePermission', 'permissions_id');
}
}
Is this correct way to define relationship? Please help me to understand.
and
class RolePermission extends Model {
public $timestamps = false;
public function role() {
return $this->belongsTo('App\Models\Role', 'roles_id');
}
}
The problem is that you need to name your table permission_role to follow the design pragma.
Role Schema
Schema::create('roles', function(Blueprint $table){
$table->increments('id');
$table->string('name');
$table->timestamps();
});
Permission schema
Schema::create('permissions', function(Blueprint $table){
$table->increments('id');
$table->string('name');
$table->timestamps();
});
Then you just need the permission_role table:
Permission_Role schema
Schema::create('permission_role', function(Blueprint $table){
$table->increments('id');
$table->integer('permission_id')->unsigned();
$table->foreign('permission_id')->references('id')->on('permissions')->onDelete('cascade');
$table->integer('role_id')->unsigned();
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
});
Then you just set up your models like this:
class Role {
public function permissions() {
return $this->hasMany(App\Permission::class);
}
}
Then of course your permission class
class Permission {
public function role() {
return $this->belongsToMany(App\Role::class);
}
}
its a many to many relationship bond together by a pivot table Permission_Role. there for each table belongsToMany not hasOne or hasMany
class Role {
public function permissions() {
return $this->belongsToMany(App\Permission::class);
}
}
class Permission {
public function role() {
return $this->belongsToMany(App\Role::class);
}
}

Laravel 5 pivot table project<->user

I find the pivot tables pretty complicated and I don't see what to do next or what I am doing wrong, I've found some tutorials but didn't help me in my needs.
I have a projects and users with a many-to-many relation.
One project hasMany users and One user hasMany projects.
What I have now leaves projects without a relationship to a user.
This is what I have so far:
Projects table
class CreateProjectsTable extends Migration {
public function up()
{
Schema::create('projects', function(Blueprint $table)
{
$table->increments('id');
$table->string('name');
$table->date('completion_date');
$table->integer('completed')->default(0);
$table->integer('active')->default(0);
$table->timestamps();
});
}
Users table
class CreateUsersTable extends Migration {
public function up()
{
Schema::create('users', function(Blueprint $table)
{
$table->increments('id');
$table->integer('company_id');
$table->integer('project_id');
$table->integer('usertype_id')->default(0);
$table->string('username');
$table->string('password');
});
}
Project User table (pivot)
class CreateProjectUsersTable extends Migration {
public function up()
{
Schema::create('project_users', function(Blueprint $table)
{
$table->increments('id');
$table->integer('project_id')->references('id')->on('project');;
$table->integer('user_id')->references('id')->on('user');;
});
}
User model
public function projects() {
return $this->belongsToMany('App\Project', 'project_users', 'user_id', 'project_id');
}
Project model
public function users() {
return $this->belongsToMany('App\User', 'project_users', 'project_id', 'user_id');
}
Project controller
public function index(Project $project)
{
$projects = $project->with('users')->get();
dd($projects);
$currenttime = Carbon::now();
//return view('project.index', array('projects' => $projects, 'currenttime' => $currenttime));
return view('user.index', compact('projects'));
}
The relationship in your User model is not correct. You have to swap the keys.
public function projects() {
return $this->belongsToMany('App\Project', 'project_users', 'user_id', 'project_id');
}
Edit regarding latest comment:
Don't think about the pivot table, as long as you have your relations setup correctly, which I believe they are, Laravel handles all of that for you.
Now $projects->users does not make any sense because projects does not have users. projects is just a collection of Project. Each Project within that collection will have a users relation. You would have to iterate through the collection to view each Project's users.
foreach($projects as $project) {
foreach($project->users as $user) {
echo $user;
}
}

Categories