Laravel - Seeding Many-to-Many Relationship - laravel

I have a users table and a roles table that has a many-to-many relationship. These two tables are connected to a junction table called role_user.
This is a model of the tables and its connections.
Below are the Models in my Laravel project:
User
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
Role
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
public function users()
{
return $this->belongsToMany('App\User');
}
}
Below is the Factory file in the Laravel project:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => $password ?: $password = bcrypt('secret'),
];
});
$factory->define(App\Role::class, function (Faker\Generator $faker) {
return [
'role' => $faker->realText($maxNbChars = 2),
'description' => $faker->realText($maxNbChars = 20),
];
});
Below is the Seed file in the Laravel project:
public function run()
{
factory(App\User::class, 50)->create()->each(function ($u) {
$u->roles()->save(factory(App\Role::class)->make());
});
factory(App\Role::class, 20)->create()->each(function ($u) {
$u->users()->save(factory(App\User::class)->make());
});
}
This should populate the users table and the roles table but how do I go about populating the role_user table? (I don't have a Model file for the junction table.)
I'm very new at this so any help would be appreciated. Thanks.

You can use attach() or sync() method on a many-to-many relationship.
There are multiple ways you can approach this. Here one of them:
// Populate roles
factory(App\Role::class, 20)->create();
// Populate users
factory(App\User::class, 50)->create();
// Get all the roles attaching up to 3 random roles to each user
$roles = App\Role::all();
// Populate the pivot table
App\User::all()->each(function ($user) use ($roles) {
$user->roles()->attach(
$roles->random(rand(1, 3))->pluck('id')->toArray()
);
});

Another way is to use saveMany() function
public function run()
{
factory(App\User::class,3)->create();
$roles = factory(App\Role::class,3)->create();
App\User::All()->each(function ($user) use ($roles){
$user->roles()->saveMany($roles);
});
}

A much cleaner method can be: after you define the factory for App\User and App\Roles you can call the afterCreating method like this:
$factory->define(App\User::class, function ...);
$factory->define(App\Role::class, function ...);
$factory->afterCreating(App\User::class, function ($row, $faker) {
$row->roles()->attach(rand(1,20));
});
Then in Seeds you first create the roles, then the users
public function run()
{
factory(App\Role::class, 20)->create();
factory(App\User::class, 50)->create();
}
Now you have 50 users each of them with one role attached.

Just for a seeder you can use something like this:
for ($i = 0; $i < 50; $i++) {
$user = factory(App\User::class)->create();
$role = factory(App\Role::class)->create();
DB::table('role_user')->insert([
'user_id' => $user->id,
'role_id' => $role->id
]);
}
But normally you need to define relation like has many through https://laravel.com/docs/5.4/eloquent-relationships#has-many-through
Then you will be able to use:
$user->roles()->save($role);

Better to use this structure:
App\Role::factory()->count(20)->create();
// Populate users
App\User::factory()->count(50)->create();
// Getting all roles and saving them to variable is not too good idea.
// Instead, get count of rows.
$rolesCount = App\Role::count();
// Populate the pivot table
App\User::all()->each(function ($user) use ($rolesCount) {
$user->roles()->attach(
App\Role::all()->random(rand(1, $rolesCount))->pluck('id')->toArray()
);
});

Related

Laravel cannot get relationship data of a selected profile

I just created a profile for users, and wish to show the education details related to the logged-in user or another selected user.
for this, I created an education model for users and gave a proper relationship to it both the sides. im not able to get any data from the education table of loged in user or another user. i used foreach tag in blade file. please revview my code. thanks.
Education Model
class Education extends Model
{
use HasFactory;
protected $primaryKey = 'education_id';
public function myusers()
{
return $this->belongsTo('App\Models\User','user_id','education_id');
}
}
User Model
public function myeducation()
{
return $this->hasMany('App\Models\Education','education_id','user_id');
}
Profile Controller
public function index()
{
$user = Auth::user();
return view('candidate.profile',['user'=>$user,]);
}
Blade file
#foreach ($user->myeducation as $education)
<div>
{{ $education->school }}
</div>
#endforeach
Table Structure of Education and Users
**Education Table**
{
Schema::create('education', function (Blueprint $table) {
$table->bigIncrements('education_id');
$table->bigInteger('user_id');
$table->string('school');
$table->string('degree');
$table->string('fieldOfStudy');
$table->date('startDate');
$table->date('endDate');
$table->string('grade');
$table->string('activities');
$table->string('description');
$table->timestamps();
});
}
user table.
$table->increments('user_id');
$table->bigInteger('role_id');
$table->bigInteger('membership_id')->nullable();
$table->string('firstname');
$table->string('lastname');
there is no error message, but just blank
Table entries
DB::table('education')
'user_id' => '2',
'school' => 'University of Bedfordshire',
'degree' => 'MBA',
]);
DB::table('users')->insert([
'user_id' => '1',
'role_id' => '1',
'firstname' => 'Mohammed',
'lastname' => 'Sabeel',
.......
]);
DB::table('users')
' user_id' => '2'
'role_id' => '2',
'firstname' => 'zahida',
'lastname' => 'sabeel',
.......
]);
the problem is in your relationship second and third argument. you are passing keys in wrong way.
in Education model use code like
public function myUser()
{
return $this->belongsTo('App\Models\User', 'user_id');
}
you need not to pass the third argument if you use the primary key for relationship. though you can pass the third argument to define which column to use to join the tables
public function myUser()
{
return $this->belongsTo('App\Models\User', 'user_id', 'user_id');
// second argument user_id is from your education model while the third argument that is user_id is the primary key of your user model
// i have used singular name for the relationship name with camel case
}
now in User model
public function myEducations()
{
return $this->hasMany('App\Models\Education', 'user_id');
// user_id is the user_id of education model
// and this is a has many relation i used plural form
}
read more about relationship in laravel doc
Before we begin, make sure that you have Education associated with that logged in User.
Try eager loading you relationship. Sometime this worked for me.
In your profile controller,
public function index()
{
$user = Auth::user()->load('myeducation');
return view('candidate.profile',['user'=>$user,]);
}
Even if it didn't work, Please share your table structure and table entries. So that we can examine clearly.

insert relationship with save method using Laravel

I have 2 models with a many to many relationship using Laravel and I want to insert into the relationship table upon using the save method.
I have save method creating the new row for the child and I have access to the parent id.
I need to insert a new row into the relationship table using the parent id that I have access to already along with the id created by the create function within the controller
MODELS
class Objective extends Model
{
public function subjects() {
return $this->belongsToMany('App\Models\Subject');
}}
class Subject extends Model
{
public function objectives() {
return $this->belongsToMany('App\Models\Objective');
}}
The function subject receives the id of the parent from the url and the create function should takes in this id too.
CONTROLLER
class ObjectiveController extends Controller
{
public function subject($id)
{
$subjects = Subject::find($id);
$objectives = DB::table('objectives')->get();
return view('admin.objectives.subject',['objectives' => $objectives],['subjects' =>
$subjects],compact('objectives','subjects'));
}
public function create($id)
{
$post = new Objective;
$post->name = 'NEW OBJ';
$post->save();
return redirect('objectives');
}
}
ROUTE
Route::get('objectives/create/{id}', [
'uses' => 'App\Http\Controllers\ObjectiveController#create',
'as' => 'admin.objectives'
]);
VIEW
<a class="card-header-action" href="{{url('objectives/create', ['id' => $subjects->id]) }}"><small class="text-muted">Add New</small></a>
You can use attach to add to the pivot table:
...
$post->save();
$post->subjects()->attach($id);

Laravel Polymorphic Relationships with order by

Laravel version 7.2.5.
I am using Polymorphic Relationships to store the access logs (login, logout) for multi-role application.
The data storing part is working completely fine. Now, I need to show the list of records in desc format with pagination. But, It's loading the data in the asc format
SomeModel.php
class SomeModel extends Model
{
/**
* Polymorphic Relationships
*/
public function loginLog()
{
return $this->morphMany(LoginLog::class, 'employable');
}
public function show($token)
{
return self::where([
'token' => $token,
])->with([
'loginLog:employable_id,ip,created_at,updated_at'
])->first() ?? null;
}
}
I did find the solution. But, somehow it doesn't feel the appropriate way to solve this issue.
Here is the link Laravel order results by column on polymorphic table
Try this
class SomeModel extends Model
{
/**
* Polymorphic Relationships
*/
public function loginLog()
{
return $this
->morphMany(LoginLog::class, 'employable')
->latest();
}
}
I found another way to solve this issue...
class SomeModel extends Model
{
/**
* Polymorphic Relationships
*/
public function loginLog()
{
return $this->morphMany(LoginLog::class, 'employable');
}
public function show($token, $pagination = 0)
{
return self::where([
'token' => $token,
])->with([
'loginLog' => function ($query) use ($pagination) {
return $query->select([
'employable_id',
'ip',
'created_at',
'updated_at',
])
->skip($pagination)
->take(\Common::paginationLimit())
->orderBy('id', 'DESC');
}
])
->orderBy('id', 'DESC')
->first('id') ?? null;
}
}
Since I do not need the base table's parameters, therefore, I am fetching only id from it.
Also, with this way I am able to use the pagination too (I am using loadmore pagination).

Get all relations through manyToMany relation

I have User, Account and Item models.
Account has many User relations, and User has many Account relations. These are defined as many to many.
An Account has many Item relations, and I am wondering how to fetch all Item on all Account relations for a User.
// User.php:
public function accounts()
{
return $this->belongsToMany( Account::class );
}
// Account.php
public function users()
{
return $this->belongsToMany( User::class );
}
// Item.php
public function account()
{
return $this->belongsTo( Account::class );
}
Any idea on how to do a call like auth()->user()->items or auth()->user()->accounts()->items ?
as per your relationship, you can fetch every accounts of a user with every items on each of those accounts as follows:
auth()->user()->accounts()->with('items')->get();
for the above statement to work, you need to define items relationship on your Account model as follows:
//Account.php
public function items()
{
return $this->hasMany( Item::class );
}
You can define a direct BelongsToMany relationship by "skipping" the accounts table:
class User extends Model
{
public function items()
{
return $this->belongsToMany(
Item::class,
'account_user',
'user_id', 'account_id', null, 'account_id'
);
}
}
$items = auth()->user()->items;
If you just want a list a items with those conditions, start from the item model
$userId = auth()->id();
$items = Item::whereHas('account', function($account) use ($userId) {
$account->whereHas('users', function($user) use ($userId) {
$user->where('id','=', $userId);
});
})->get();

Laravel getting users from multiple messages

I've got offers table with id's
Messages table with columns offer_id and from
and Users table with id's
I want to get users starting with offer $this in OfferResource
My goal is to get users that have replied to the offer with at least one message.
I started to configure Offer model with function messages to get messages
public function messages(){
return $this -> hasMany('App\Message');
}
so I'm able to get all messages (starting from offer resource):
'users' => $this->messages
How should I now configure messages model to get all users instead of messages?
I tried to write in Message model :
public function fromContact()
{
return $this->hasOne(User::class, 'id', 'from');
}
and then:
'users' => $this->messages->fromContact
but i've got error: "message": "Property [fromContact] does not exist on this collection instance.",
How should I correct my code to make this work?
I am assuming the from field on the Messages table is populated using user ID. Then you could establish belongsToMany relationship between the Offer and User models. Since this is actually a many-to-many relation with a pivot table messages.
In the Offer model define
public function users()
{
return $this->belongsToMany('App\User', 'messages', 'offer_id', 'from');
}
Then from the OfferResource you could load the offers data like this—
$offers = App\Offer::with('users')->get();
Then loop over the $offers like this:
foreach ($offers as $offer) {
dd($offer->users); // one offer will have multiple users as a Collection
}
Similarly for an $offer of ID 1 you could do this
$offer = App\Offer::with('users')->find(1)
Then to get the users that commented on this offer just use $offer->users
See the official documentation for defining many-to-many relationship.
In the Message model you have to spesify the column that refer to the user :
public function fromContact()
{
return $this->hasOne(User::class, 'from');
}
And then after geting the messages loop over them to get the user like this :
foreach ($this->messages as $message) {
$user = $message->fromContact;
// do somthing with the user :)
}
Your messages table has from field that is referencing User model and offer_id field which referencing Offer model that means you have ManyToMany relations between Offer and User.
Offer Model
public function users(){
return $this->belongsToMany(User::class, 'messages', 'offer_id', 'from')->using('App\Message');
}
Message Pivot
class Message extends Pivot {
protected $table = 'messages';
}
User model
public function offers(){
return $this->belongsToMany(Offer::class, 'messages', 'from', 'offer_id')->using('App\Message');
}
OfferResource
public function toArray($request)
{
$users = $this->users;
return [
'id' => $this->id,
... //add your offer fields here
'users' => $users->toArray(), // here $users is a laravel Collection
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
Access from Controller or Route
Route::get('/offer', function () {
return new OfferResource(Offer::with('users')->find(1)); //eager load users
});

Resources