how to create relationship between three table in laravel? - laravel

i have three table:
Suppliers
id | name | type
1 | Test1 | Seller
2 | Test2 | Manufacturer
Products
id | name
1 | product1
2 | product2
Prices
id | supplier_id | product_id | price
1 | 1 | 1 | 1000
2| 2 | 1 | 2000
each supplier have many products and each products belong to many suppliers.
Now each supplier can have a separate price for each product.
i have a question that how to create model and table for this scenario?

You will have two tables with models:
Product and Supplier
Within /Database/Migrations create_products table:
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
create_suppliers table:
Schema::create('suppliers', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('type');
$table->timestamps();
});
You will need a pivot table, so create it with Artisan command:
php artisan make:migration create_product_supplier_table
A quick note: Pivot names should be in alphabetical order to fit laravel name convention. 'P' letter comes first and then 'S' (product_supplier) in this case.
product_supplier table:
Schema::create('product_supplier', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('product_id');
$table->unsignedBigInteger('supplier_id');
$table->integer('price');
$table->timestamps();
});
Now, you will need the following relation declarations in two models
Within App\Product class:
protected $guarded = [];
public function suppliers()
{
return $this->belongsToMany(Supplier::class)->withPivot('price');
}
And also in App\Supplier class:
protected $guarded = [];
public function products()
{
return $this->belongsToMany(Product::class)->withPivot('price');
}
You set it up now. Let's give it a try:
$supplier = App\Supplier::create(['name' => 'supplier1', 'type' => 'seller']);
$product = App\Product::create(['name' => 'product1']);
$supplier->products()->attach($product, ['price' => 80]);
Call it back:
$supplier->products; // it will give you the products that attached to supplier.
So, the any supplier can have any product with any price tag.

Related

query data in four levels tables by laravel eloquent

I have this SQL structure to query schools and the number of student of male gender,
I am asking for help for converting it to laravel eloquent
SELECT *
FROM schools && count(students has(gender == 'male'))
JOIN grades ON (grades.schools = schools.school_id)
JOIN streams ON (stream.schools = schools.school_id)
JOIN students ON (student.schools = schools.school_id)
this is what i did in the schemas
school schema
Schema::create('schools', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('educationLevel');
$table->foreignId('ward_id')
->constrained('wards')
->onUpdate('cascade');
$table->timestamps();
});
grade
Schema::create('grades', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('school_id')->constrained('schools')
->onDelete('cascade');
$table->timestamps();});
stream
Schema::create('streams', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('grade_id')
->constrained('grades')
->onDelete('cascade');
$table->timestamps();
});
student
Schema::create('students', function (Blueprint $table) {
$table->id();
$table->string('student_name');
$table->string('gender');
$table->foreignId('stream_id')
->constrained('streams')
->onDelete('cascade');
$table->timestamps();
});
this is what i tried before
in school controller
$schools = School::select(['name'])->withCount('students')->where('students', function($query){
$query->where('gender', 'male');
})
->get();
in school model i did this below
public function grades()
{
return $this->hasMany(Grade::class);
}
public function students(){
return $this->hasManyThrough(Student::class, Stream::class, Grade::class);
}
the relationship of this models is one to many like below
school->has->grade->has->stream->student(gender = male or female)
You can leverage addSelect to get the desired output
School::query()
->addSelect([
/** Total no of students in school */
'count_students' => Student::selectRaw('count(*)')
->whereIn(
'stream_id',
Stream::select('id')->whereIn(
'grade_id',
Grade::select('id')->whereColumn('school_id', 'schools.id')
)
),
/** Total no of "gender = male" students in school */
'count_male' => Student::selectRaw('count(*)')
->whereRaw('gender = "male"')
->whereIn(
'stream_id',
Stream::select('id')->whereIn(
'grade_id',
Grade::select('id')->whereColumn('school_id', 'schools.id')
)
),
/** Total no of "gender = female" students in school */
'count_female' => Student::selectRaw('count(*)')
->whereRaw('gender = "female"')
->whereIn(
'stream_id',
Stream::select('id')->whereIn(
'grade_id',
Grade::select('id')->whereColumn('school_id', 'schools.id')
)
),
/** Total no of "gender = other" students in school */
'count_other' => Student::selectRaw('count(*)')
->whereRaw('gender = "other"')
->whereIn(
'stream_id',
Stream::select('id')->whereIn(
'grade_id',
Grade::select('id')->whereColumn('school_id', 'schools.id')
)
)
])->get();
You would need something approaching this:
$schools = School::Join('grades', 'grades.schools', '=', 'schools.school_id')
->Join('stream', 'stream.schools', '=', 'schools.school_id')
->Join('student', 'student.schools', '=', 'schools.school_id')
->where('gender', "male")
->get([
'name','yourdata'
]);
To get the count of data, you simply calculate with count method.
$schoolCount = count($schools);
Make sure your table and column names are appropriate, I am seeing a lot of inconsistencies in your model and table names, same for columns. also, using join you might run into issues like columns with same name. make sure to specify them separately in the get method.Like:
->get([
'id', 'student.schools as stud', 'stream.name as streamName',
]);

Laravel updateExistingPivot Keeps Returning false

I have the following with many to many relationship and pivot table. So I would like to update the user_id to 1 pivot table where trophy_id = 3 . However it keeps returning 0 or false.
Trophy.php
public function users()
{
return $this->belongsToMany('App\User', 'trophies_users', 'trophy_id', 'user_id');
}
User.php
public function trophies()
{
return $this->belongsToMany('App\Trophy', 'trophies_users', 'user_id', 'trophy_id');
}
Pivot Table
public function up()
{
Schema::create('trophies_users', function (Blueprint $table) {
$table->id();
$table->integer('trophy_id');
$table->integer('user_id');
$table->timestamps();
});
}
In My controller , I am trying to update the pivot's table
UserController#index
public function index(Request $request)
{
$user = User::find(1);
$trophyId = 3;
$attributes = ['user_id' => 1];
$res = $user->trophies()->updateExistingPivot($trophyId , $attributes);
dd($res); //Keeps returning 0 or false
}
You are passing attribute to updateExistingPivot which is for updating extra attribute on a pivot table.
For example, assume we want to store a color for each companion of user and trophy, we can achieve that by adding an extra column in pivot table and saving color data on it as below:
|---------------------|
| column |
|---------------------|
| id |
|---------------------|
| user_id |
|---------------------|
| trophy_id |
|---------------------|
| color |
|---------------------|
If you want to update a relationship on pivot table, use sync method.
$user=User::find(1);
$trophies_id=[1, 2, 3];
$user->trophies()->sync($trophies_id);

Laravel Many-to-Many Relationship Field Names

I have a users table and a permissions table. One user can have many permissions, and one permission can have many users:
USER ID | PERMISSION ID
1 | 1
1 | 2
2 | 1
2 | 1
There is a linking table called permission_user as defined by the Laravel spec for auto-inferring these tables.
If I define the following functions:
User Model:
public function Permissions()
{
return $this->belongsToMany('App\Permission');
}
Permission Model:
public function Users()
{
return $this->belongsToMany('App\User');
}
I get an error when calling App\User::first()->Permissions()->attach(App\Permission::first()); that says
Illuminate\Database\QueryException : SQLSTATE[42S22]: Column not found: 1054 Unknown column '' in 'field list' (SQL: insert into `permission_user` (``, `user_id`) values (3, 1))
The database migration file:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Carbon\Carbon;
use App\User;
use App\Permission;
use App\Http\Resources\User as UserResource;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('permissions', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('username')->unique();
$table->string('name')->unique();
$table->string('email')->unique();
$table->boolean('verified');
$table->timestamps();
});
Schema::create('permission_user', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('permission_id');
$table->unsignedBigInteger('user_id');
$table->timestamps();
$table->foreign('permission_id')->references('id')->on('permissions');
$table->foreign('user_id')->references('id')->on('users');
});
$this->add_permission('View Projects');
$this->add_permission('Edit Projects');
$this->add_permission('View Users');
$this->add_permission('Edit Users');
$user = new User();
$user->name = 'John Smith';
$user->email = 'john#smith.com';
$user->username = 'jsmith';
$user->verified = 1;
$user->save();
$user->permissions()->attach(Permission::where('name','View Users')->first()->id); // -> This line it can't tell that permission_user.permission_id is where the permission.id field goes;
$perms = $user->permissions()->get();
foreach($perms as $perm)
{
echo $perm->name . '\n';
}
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
Schema::dropIfExists('permission_user');
Schema::dropIfExists('project_user');
Schema::dropIfExists('permissions');
Schema::dropIfExists('deployment_log');
Schema::dropIfExists('branches');
Schema::dropIfExists('projects');
Schema::dropIfExists('users');
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
}
private function add_permission($permission_name)
{
DB::table('permissions')->insert(
array(
'name' => $permission_name,
)
);
}
}
It appears Laravel(5.8) is unable to disscern from the linking table that permission_id is the field for the foreign reference to the permission.id field even though the database migration reflects that user_id is a foreign reference to user.id and permission_id is a foreign reference to permission.id.
I can solve this by specifying the linking table name, field name, and foreign key name in the belongsToMany function, however, Laravel's own documentation states that this isn't needed when tables and fields are named appropriately, which mine are. Is this a bug in Laravel? Do I need to change the name of the permission_user.permission_id field? How do I solve this without having to specify these names in my models as it's time consuming and not needed according to Laravel(5.8)'s documentation.
According to laravel docs:
[...] Many users may have the role of "Admin". To define this relationship, three database tables are needed: users, roles, and role_user. The role_user table is derived from the alphabetical order of the related model names, and contains the user_id and role_id columns.
The linking table must contain only the foreign keys from each model. Otherwise, you need to specify which relationship table you are using and the primary key for each model of the relation, as specified on laravel documentation.
As i said in the comments section, if you create your permission_user table with only permission_id and user_id columns and with this columns as primary keys, it will work as expected:
Schema::create('permission_user', function (Blueprint $table) {
$table->unsignedBigInteger('permission_id');
$table->unsignedBigInteger('user_id');
$table->foreign('permission_id')->references('id')->on('permissions');
$table->foreign('user_id')->references('id')->on('users');
$table->primary(['permission_id', 'user_id']);
});
Here is a package that i have developed to handle user permissions and you can check the user_has_permissions table definition, which is, basically, a table that does exactly what your permission_user table does, by clicking this link.
Hope it helps.

Laravel 5.6 - relation one to many(invert) - doesn't work

I'm learning relations.
Where is the mistake in my student - gender relation?
Students table:
Schema::create('students', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->integer('gender_id')
->unsigned()
->references('id')
->on('genders')
->onDelete('cascade');
$table->timestamps();
});
Genders table:
Schema::create('genders', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
StudentsController
use App\Student;
use App\Gender;
class StudentsController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$students = Student::all();
return view('index', compact('students'));
}
Index.blade
<pre>{{var_dump($students[0]->gender_id->name)}}</pre>
error msg. There are no object in $students.
Trying to get property of non-object
Gender model:
public function students() {
return $this->hasMany('App\Student');
}
Student model:
public function gender() {
return $this->belongsTo('App\Gender');
}
You need to access the relationship using the relationship method/attribute, not the related field. So, you need to use gender, not gender_id.
<pre>{{var_dump($students[0]->gender->name)}}</pre>
Be aware that you will get the same error if the student doesn't have a related gender.
why do you create a new table for gender ? every student will have only one gender ( female / male ) so there is no need to save it in another table, just set a column for gender in the students table, if you need all males students you run this one :
male students :
$males = Student::where('gender', 'male')->get();
female students :
$females = Student::where('gender', 'female')->get();

Laravel Assigning One to Many (?), Many to Many(?)

I have two models
Stations
Operators
I am currently trying to "save" several Operators to a Station
but i want also to be able to "save" the same Operator to another Station.
Example:
+---------------------------------+
| Station | Operator(s) |
|---------------------------------|
| Munich | Lufthansa |
| | KLM |
| | Air Malta |
|---------------------------------|
| Berlin | Lufthansa |
| | KLM |
|---------------------------------|
|------- etc ---------------|
|---------------------------------|
My Stations Table:
Schema::create('stations', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 100);
$table->timestamps();
});
My Stations Model:
public function operators() {
return $this->hasMany(Operators::class);
}
My Operators Table:
Schema::create('operators', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 100)->unique();
$table->string('email', 100);
$table->boolean('notify')->default(false);
$table->timestamps();
});
My Operators Model:
public function stations() {
return $this->belongsTo(Stations::class);
}
Here i must say that i am creating the Station and trying to add the Operators:
In StationsController:
After receiving the Ids of the Operators and the Name of the Station:
$station = new Stations;
$station->name = request('name');
$station->save();
foreach (request('operators') as $operator) {
$tempOperator = Operators::find($operator);
$station->operators()->associate($tempOperator)->save();
}
The response is:
"Call to undefined method Illuminate\Database\Query\Builder::associate()"
I know there is something wrong with the relations but i cannot figure it out... Thank you in advance
Rollback your migration php artisan migrate:rollback
change your operators table migration like this,
Schema::create('operators', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 100)->unique();
$table->string('email', 100);
$table->boolean('notify')->default(false);
$table->timestamps();
});
you have to create a mapping table like,
Schema::create('station_operators', function (Blueprint $table) {
$table->increments('id');
$table->integer('stations_id')->unsigned();
$table->integer('operators_id')->unsigned();
$table->timestamps();
$table->foreign('stations_id')->references('id')->on('stations');
$table->foreign('operators_id')->references('id')->on('operators');
});
Run migrate php artisan migrate
Your Stations Model:
public function StationOperators() {
return $this->hasMany(StationOperators::class);
}
Your Operators Model:
public function StationOperators() {
return $this->hasMany(StationOperators::class);
}
Your StationOperators Model:
public function Stations() {
return $this->belongsTo(Stations::class);
}
public function Operators() {
return $this->belongsTo(Operators::class);
}
For associate,
$station = new Stations;
$station->name = request('name');
$station->save();
foreach (request('operators') as $operator) {
// $tempOperator = Operators::find($operator);
// $station->StationOperators()->associate($tempOperator)->save();
$data = [
'stations_id' => $station->id,
'operators_id' => $operator,
];
$stationOperator = new \App\StationOperators();
$stationOperator->save();
}
You need an intermediate StationsOperators table.
Then try updating your operators model like this:
public function stations() {
return $this->belongsToMany(Stations::class)->using(StationsOperators::class);
}
Remember:
All custom models used to represent intermediate tables of relationships must extend the Illuminate\Database\Eloquent\Relations\Pivot class.
This is a Many-Many relation. You have to use pivot table.
#ref: https://laravel.com/docs/5.5/eloquent-relationships#many-to-many
The associate method only available for belongsTo relationship, so it won't work for many to many relationship.
You can use the attach method for many to many, here is the example from laravel doc https://laravel.com/docs/5.5/eloquent-relationships#updating-many-to-many-relationships

Resources