Many to many relationship with more than 2 tables in Laravel - php

I'm trying to implement a many-to-many relationship with more than two tables in Laravel.
Migration For Relationship Table
public function up()
{
Schema::create('lab_form_detail', function (Blueprint $table) {
$table->unsignedBigInteger('color_id');
$table->unsignedBigInteger('lab_form_id');
$table->unsignedBigInteger('pontics_design_id');
$table->unsignedBigInteger('teeth_design_id');
$table->integer('quantity');
$table->string('teeth_number');
$table->foreign('lab_form_id')->references('id')->on('lab_forms');
$table->foreign('color_id')->references('id')->on('colors');
$table->foreign('pontics_design_id')->references('id')->on('pontics_designs');
$table->foreign('teeth_design_id')->references('id')->on('teeth_designs');
});
}
Lab Form Model
class LabForm extends Model
{
use HasFactory;
public $timestamps = false;
public function colors()
{
return $this->belongsToMany(Color::class, 'lab_form_detail', 'lab_form_id', 'color_id')
->withPivot([
'pontics_design_id',
'teeth_design_id',
'quantity',
'teeth_number'
]);
}
public function ponticsDesigns()
{
return $this->belongsToMany(PonticsDesign::class, 'lab_form_detail', 'lab_form_id', 'pontics_design_id')
->withPivot([
'color_id',
'teeth_design_id',
'quantity',
'teeth_number'
]);
}
public function teethDesigns()
{
return $this->belongsToMany(TeethDesign::class, 'lab_form_detail', 'lab_form_id', 'teeth_design_id')
->withPivot([
'color_id',
'pontics_design_id',
'quantity',
'teeth_number'
]);
}
}
I also use belongsToMany for other related models.
When I try to insert a new row, I need to use functions for the relationship to insert.
For example:
$lf = LabForm::find(1);
$lf->colors()->attach(1, [
'pontics_design_id' => 2,
'teeth_design_id' => 1,
'quantity' => 10,
'teeth_number' => '30 to 40'
]);
As you can see, when inserting a new row, I need to use one of colors, teethDesigns, ponticsDesigns. Also, I need to insert values differently which depend on the functions that define the relationship.
My question is, can I combine related values into one function and insert them all at the same time? Like:
public function labFormDetail()
{
// insert all values at once
'lab_form_id', 'pontics_design_id', 'color_id', 'teeth_design_id','quantity', 'teeth_number'
}
Or should I create a model for this relationship table?
Or you may suggest your approach for this kind of relationship.

Related

How to get related records for a single record in Laravel 5.8?

I have a registration table where the situation is that I have a table for months and a table for years. The relationship between months and registration is one-to-many and same is the case with years and registration like below:
//Here is the registration migration
public function up()
{
Schema::create('registrations', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('father_name');
$table->string('contact1');
$table->string('contact2')->nullable();
$table->string('address')->nullable();
$table->integer('amount');
$table->integer('day');
$table->unsignedInteger('month_id');
$table->unsignedBigInteger('year_id');
$table->timestamps();
});
}
Below is the Registration model:
class Registration extends Model
{
protected $fillable =
['name', 'father_name', 'contact1', 'contact2', 'address', 'amount',
'day', 'month_id', 'year_id'
];
public function month()
{
return $this->belongsTo(Month::class);
}
public function year()
{
return $this->belongsTo(Year::class);
}
}
This is Month model:
class Month extends Model
{
public function registration()
{
return $this->hasMany(Registration::class);
}
}
This is Year model:
class Year extends Model
{
public function registration()
{
return $this->hasMany(Registration::class);
}
}
Now, when I want to show one registration record with its relationships like below, I get all the registration records instead of one.
public function show(Registration $reg)
{
$registration = $reg::with('month', 'year')->get();
return ['registration' => $registration];
}
When I used with function with modal name then I get all records correctly but when I use it even with the single instance of the modal, I still get all the related records which I don't want. I want to select the Month and Year related to the single instance of the registration.
Any help is appreciated in advance.
This is due to ->get();
in $reg you have one instance but then you do but then you make a new request with ->get(); and get displays all records
Do it like this
public function show($regId)
{
$registration = Registration::with('month', 'year')->findOrFail($regId);
return ['registration' => $registration];
}
You can do it like this:
public function show(Registration $reg)
{
$reg->load(['month', 'year']);
return ['registration' => $reg];
}
You can also remove model binding and use with() for eager loading.
public function show($id)
{
$registration = Registration::with(['year', 'month'])
->firstWhere('id', $id);
return ['registration' => $registration];
}
It think you don't have specified the foreign_key in relation.
Or you have to define the
foreign_key by
class Registration extends Model
{
protected $fillable = [
'name', 'father_name', 'contact1', 'contact2', 'address',
'amount','day', 'month_id', 'year_id'
];
public function month()
{
return $this->belongsTo(Month::class,'month_id');
}
public function year()
{
return $this->belongsTo(Year::class,'year_id');
}
}
May be it will solve your problem.
Actually the get() method will return an array of records that you can loop over and that's why you get them all. Have you tried using the first() method that will return exactly one record.
$registration = $reg::with('month', 'year')->first();

Laravel Why pivot table is empty?

I have a simple relationship in laravel eroquent
Here is the bidders table creation
public function up()
{
Schema::create('bidders', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('params_name');
$table->string('params_value');
$table->string('bidder_name');
$table->timestamps();
});
}
Here is bidder_parameter table creation
public function up()
{
Schema::create('bidder_parameters', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('bidder_id');
$table->foreign('bidder_id')->references('id')->on('bidders')->onDelete('cascade');
$table->timestamps();
});
}
Here is a bidder model
class Bidder extends Model
{
protected $table = 'bidders';
protected $fillable = [
"params_name",
"params_value",
"bidder_name"
];
public function page()
{
return $this->hasMany('App\Page');
}
public function parameters()
{
return $this->hasMany('App\BidderParameter');
}
}
and here is BidderParameter model
class BidderParameter extends Model
{
public function parameters()
{
return $this->belongsTo('App\Bidder');
}
}
Here is parameter controller for inserting data to database
public function store(Request $request){
// dd($request);
if($request->ajax())
{
$rules = array(
'params_name.*' => 'required',
'params_value.*' => 'required',
'bidders_name.*' => 'required'
);
$error = Validator::make($request->all(), $rules);
if($error->fails())
{
return response()->json([
'error' => $error->errors()->all()
]);
}
$params_name = $request->params_name;
$params_value =$request->params_value;
$bidders_name =$request->bidders_name;
for($count = 0; $count < count($params_name); $count++)
{
$data = array(
'params_name' => $params_name[$count],
'params_value' => $params_value[$count],
'bidders_name' => $bidders_name[$count],
);
$insert_data[] = $data;
// dd($insert_data);
}
Bidders:insert($insert_data);
return response()->json([
'success' => 'Data Added successfully.'
]);
}
}
Now when I submit data to the database 'bidders tablehave data saved into it butbidder_parameter` is empty
What is wrong with my code?
Many to many relationship contains 3 tables. 2 tables are main and 3rd table is combination of those 2 tables' primary keys.
For example
User can have many roles
and
Role can belong to many users
so that requires many to many relationship
So we need a database schema like this:
Users table -> (id, name ,...) e.g. Jonn Doe with id 1
Roles table -> (id, name, ...) e.g. SUPER_ADMIN with 1
role_user table (id, user_id, role_id) role_id is foreign key of roles table and user_id is foreign key of users table
Now in Model classes:
In User Model
public function roles(){
return $this->belongsToMany(Role::class,'role_user','user_id','role_id');
}
Now in Roles class
public function users(){
return $this->belongsToMany(User::class,'role_user','role_id','user_id');
//note keys are in opposite order as in roles() method
}
Now you can call function from User instance and Role instance where you want
$user->roles // as collection
$user->roles() // as eloquent instance
Also
$role->users // as collection
$role->users() // as eloquent instance
You can read more here

Data not Saving into pivot table using Laravel Api Resource

please I am new to Laravel, I want to use Laravel API Resource to store database inside database.
I have 3 Tables
Users
Category
Category_User (this is a pivot table)
In my Controller (OnboardsControllers), I have this to store data
public function store(Request $request)
{
$category_user = Category_User::firstOrCreate(
[
'user_id' => $request->user()->id,
'category_id' => $request->$category->id,
],
);
return new CategoryUserResource($category_user);
}
In my CategoryUserResource I have this
public function toArray($request)
{
return [
'user_id' => $this->user_id,
'category_id' => $this->category_id,
'created_at' => (string) $this->created_at,
'updated_at' => (string) $this->updated_at,
];
In my Pivot Table, I have this
public function up()
{
Schema::create('category_user', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('category_id')->unsigned();
$table->integer('user_id')->unsigned();
$table->timestamps();
});
}
In my model, I added this
protected $fillable = [
'user_id', 'category_id',
];
In my route/api
Route::post('/category_user', 'OnboardsController#onboardupdate');
I believe the error is here
'user_id' => $request->user()->id,
When I removed it, it didn't returned any error
When I tried to save to my database using Postman to test, it not saving. Please, I don't know what I am doing wrong.
Thanks
To be honest, you do not need a model Category_User (but you need a table).
In Laravel a more concise approach is used:
public function store(Request $request)
{
$user = User::find($request->input('user_id'));
$user->categories()->attach($request->input('category_id'));
}
Also I'm not sure if you have declared a method categories() in model User:
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
}
I also strongly doubt the existence of method user() of class Request.
If the request parameter has a name category_id, then you need to access it like this:
$request->input('category_id');

Laravel - Eloquent attach() not working for models

TL;DR
Trying to get three models to interact using eloquent for a rest api.
User - belongsToMany(pulls)
Pull - belongsToMany(user) && belongsToMany(boxes)
Box - belongsToMany(pulls)
The pull_user table is working perfectly, I can just attach a user after I save a pull. Saving a box works fine but the attach doesn't work/enter anything into the pivot table (I get no errors though).
The Problem
I can't get a pivot table that associates two of my models together to attach() after a save. I have the three models listed above, the pivot is working for pull_user but not for pull_box even though the save for box is working perfectly. I am able to save a box without an error but the association just never occurs (no error).
The Code
pull_box.php
class PullBox extends Migration
{
public function up()
{
Schema::create('pull_box', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('pull_id');
$table->integer('box_id');
});
}
public function down()
{
Schema::dropIfExists('pull_box');
}
}
Pull.php
class Pull extends Model
{
protected $fillable = ['from', 'to', 'runit_id', 'start_time', 'end_time', 'box_count', 'pull_status', 'audit_status', 'status', 'total_quantity', 'accuracy'];
public function users(){
return $this->belongsToMany('App\User');
}
public function boxes(){
return $this->belongsToMany('App\Box');
}
}
Box.php
class Box extends Model
{
protected $fillable = ['user_id','from', 'to', 'runit_id', 'start_time', 'end_time', 'pull_id', 'total_quantity', 'status', 'accuracy'];
public function pulls(){
return $this->belongsToMany('App\Pull');
}
}
BoxController.php
public function store(Request $request)
{
$this->validate($request, [
'user_id' => 'required|integer',
...
]);
$user_id = $request->input('user_id');
...
$box = new Box([
'user_id' => $user_id,
...
]);
$pull = Pull::whereId($pull_id)->first();
if($box->save()){
$pull->boxes()->attach($box->id);
$box->view_box = [
'href' => 'api/v1/box/' . $box->id,
'method' => 'GET'
];
$message = [
'msg' => 'Box created',
'box' => $box,
'pull' => $pull_id
];
return response()->json($message, 201);
}
$response = [
'msg' => 'Box creation error, contact supervisor',
];
return response()->json($response, 404);
}
The Solution
I need to know how I can get this association working. I am going to need to add a new layer in under the pull for Item, but I don't want to move one before I solve this. I think that my problem has to stem from a syntactical/logical error on my part but I can't see it. There are a bunch of questions on SO that are very close to giving me a solution, but after reading them I wasn't able to solve my problem.
Any help is appreciated.
Try renaming your pull_box table to box_pull, pivot tables on laravel must be in alphabetical order. If you want to use custom name on pivot table you have to extends your pivot, for example:
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class PullBox extends Pivot
{
protected $table = 'pull_box';
}
And your many to many relationships:
class Pull extends Model
{
protected $fillable = ['from', 'to', 'runit_id', 'start_time', 'end_time', 'box_count', 'pull_status', 'audit_status', 'status', 'total_quantity', 'accuracy'];
public function users(){
return $this->belongsToMany('App\User');
}
public function boxes(){
return $this->belongsToMany('App\Box')->using('App\PullBox');
}
}
class Box extends Model
{
protected $fillable = ['user_id','from', 'to', 'runit_id', 'start_time', 'end_time', 'pull_id', 'total_quantity', 'status', 'accuracy'];
public function pulls(){
return $this->belongsToMany('App\Pull')->using('App\PullBox');
}
}

Laravel Seeding using Factory for one to one relation

I want to create a test data using laravel seed. I can seed for one table. Here is :
ModelFactory.php:
/** #var \Illuminate\Database\Eloquent\Factory $factory */
$factory->define(App\User::class, function (Faker\Generator $faker) {
static $password;
return [
'email' => $faker->unique()->safeEmail,
'password' => $password ?: $password = bcrypt('secret'),
'remember_token' => str_random(10),
'type' => '0'
];
});
UsersTableSeeder.php :
<?php
use Illuminate\Database\Seeder;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run() {
factory(App\User::class, 50)->create()->each(function ($u) {
$u->posts()->save(factory(App\Post::class)->make());
});
}
}
I have two tables which are users and user_information. They have one-to-one relation. I want to seed user_information with users. But i have no idea, how i can do...
My models relation functions :
User.php
public function userInformation() {
return $this->hasOne('App\UserInformation', 'user_id', 'id');
}
UserInformation.php
public function users() {
return $this->belongsTo('App\User', 'user_id', 'id');
}
Laravel version is 5.3
I followed these docs :
https://laravel.com/docs/5.3/seeding
https://laravel.com/docs/5.3/database-testing#writing-factories
Answering because this came up in a Google search and might help others:
AFAIK the model hasn't got anything to do with seeding, that's used for query building and constraints.
What can do is create a user, every time you call on your factory that requires the user_id.
Credit to prodigy10 for this answer.
$factory->define(User_Information::class, function ($faker) use ($factory) {
return [
'name' => $faker->company(),
'user_id' => factory(User::class)->create()->id
];
});
For one-to-one relationships, its good practice to make the relationship methods singular
User.php
public function userInformation()
{
return $this->hasOne('App\UserInformation', 'user_id', 'id');
}
UserInformation.php
public function user()
{
return $this->belongsTo('App\User', 'user_id', 'id');
}
Above solution for Laravel 8, userInformation factory will have :
'user_id' => User::factory()->create()->id,
if you need to create userInformation table only when there's a corresponding user, the above solution will seed new user_ids into the userinformation table and you may end up with more userinformation rows than users. To fix this, you need to seed only UserInformation Factory seeder in DatabaseSeeder run method and not UserFactory. it will automatically seed the users table.
Alternatively, if you have the users table already defined and wants to seed userInformation table for your number of users.
For Laravel 8, its is shown below:
public function definition()
{
$users = User::all();
return [
'name' => $this->faker->company,
'user_id' => $this->faker->unique()->numberBetween(1, $users->count())
];
}
Since its a one to one relationship. You can generate the list of users and use unique and numberBetween functions to fill unique user_ids.
This works in Laravel 9. Amend your DatabaseSeeder.php such as:
public function run()
{
User::factory(10)->create()->each(function ($user) {
$phone = Phone::factory()->make();
$user->phone()->save($phone); // phone() is hasOne ralationship in User.php
});
}
You don't need to define user_id column in PhoneFactory.php. The Laravel automatically saves corresponding user_id in the database.

Categories