What is the difference between defining the relation between 2 tables within the two models and defining it in the proper migration file?
For example, I want a many to one relationship between the table cars and the table persons.
Option 1: I define the relation in the models
Model Person:
class Person extends Model
{
public function cars()
{
return $this->hasMany('App\Car');
}
}
Model Car:
class Car extends Model
{
public function persons()
{
return $this->belongsToMany('App\Person');
}
}
Option 2: I define the relation in the migration
class CreateCarsTable extends Migration
{
public function up()
{
Schema::create('cars', function (Blueprint $table) {
$table->increments('id');
$table->integer('person_id')->references('id')->on('person');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('cars');
}
}
Thank you for your help.
From the docs
Eloquent relationships are defined as methods on your Eloquent model classes. Since, like Eloquent models themselves, relationships also serve as powerful query builders, defining relationships as methods provides powerful method chaining and querying capabilities
What you mentioned about migrations is:
1.FOREIGN KEYS just ensure your data are consistent.
If we apply on delete cascade to the foreign key definition,referencing row will delete automatically when parent row will delete.
If we apply on Update Cascade to the foreign key definition,Child row will update automatically when parent row will update
This is not the same thing.
The hasOne(), hasMany(), belongsToOne() and belongsToMany() functions are merely query builders that return the results of a SQL query.
For example, the cars() method of the Person model with id is 1 in your example returns the results of the query SELECT * FROM cars WHERE person_id=1.
However, the foreign key has the same purpose than in a SQL script to create the cars table. For example:
CREATE TABLE cars (
...
person_id INT REFRENCES person(id),
...
);
Related
I have a new Laravel application that I'm hooking up to a preexisting database. The relationships in this database are pretty much all pivot tables.
Similar to this:
Schema::create('customers', function (Blueprint $table) {
$table->id();
$table->string('name');
});
Schema::create('vehicles', function (Blueprint $table) {
$table->id();
$table->string('name');
});
Schema::create('customer_vehicle', function (Blueprint $table) {
$table->id();
$table->int('customer_id');
$table->int('vehicle_id');
});
I've configured models/pivot tables and set up a Many To Many relationship for the customers and vehicles.
Similar to this:
class Customer extends Model
{
public function vehicles()
{
return $this->belongsToMany(
Vehicle::class,
CustomerVehiclePivot::class,
'customer_id',
'vehicle_id'
);
}
}
class Vehicle extends Model
{
public function customers()
{
return $this->belongsToMany(
Customer::class,
CustomerVehiclePivot::class,
'vehicle_id',
'customer_id'
);
}
}
So far this is working, but it doesn't feel quite right. $customer->vehicles() returns the expected results, but a vehicle should only belong to one customer, and the way I'm doing that at the moment is by doing $vehicle->customers()->first().
The actual relationship should be a One To Many. A customer can have many vehicles, but a vehicle should only belong to one customer.
Is there a way to configure the relationship as a One To Many when using a pivot table in order to be able to fetch a vehicle's customer with $vehicle->customer?
Based on #chuck's suggestion, I now have the following for my Vehicle customer method.
class Vehicle extends Model
{
public function customer()
{
return $this->hasOneThrough(
Customer::class,
CustomerVehiclePivot::class,
'vehicle_id',
'id',
'id',
'customer_id'
);
}
}
I can now perform the following and get the expected results.
$vehicle->customer; // Returns the vehicle's customer
$customer->vehicles; // Returns the customer's vehicles
I'm now trying to figure out how to use factories with this configuration.
I thought I could do Vehicle::factory()->for(Customer::factory())->create() but I get the error...
Call to undefined method Illuminate\Database\Eloquent\Relations\HasOneThrough::getOwnerKeyName()
So I'm not quite sure how you can create Vehicles for users.
I was successfully able to create users with attached vehicles by using hasAttached.
Customer::factory()
->hasAttached(
Vehicle::factory()->count(3)
)
->create()
I was able to figure out how to use factories to create vehicles for users.
Customer::factory()->create()->vehicles()->attach(Vehicle::factory()->count(3)->create());
Yes, you can use the hasOneThrough() relationship in Laravel to set up a one-to-many relationship between customers and vehicles using a pivot table. Here is an example of how you can set this up:
class Customer extends Model
{
public function vehicles()
{
return $this->hasMany(Vehicle::class);
}
}
class Vehicle extends Model
{
public function customer()
{
return $this->hasOneThrough(
Customer::class,
CustomerVehiclePivot::class,
'vehicle_id', // Foreign key on the pivot table
'id', // Local key on the customers table
'id', // Local key on the vehicles table
'customer_id' // Foreign key on the pivot table
);
}
}
I am using Laravel 5 and am trying to get my head round relationships.
I have 2 tables, students and rooms. Each room can have many students, and each student can have many rooms.
Would this be a Many to Many relationship in both directions? Would I need a pivot table to achieve this?
Yes, what you describe is a many-to-many relationship, and for that you'll need a pivot table.
Your three tables would look like this: (this might look different from your actual tables)
students
id (primary key)
name
age
rooms
id (primary key)
room_size
bedroom_count
Note: by default, the name of the pivot table is composed of both model names (in singular form), in alphabetical order. So in this case: Student + Room = room_student.
room_student (pivot table)
room_id (foreign key to 'rooms')
student_id (foreign key to 'students')
Pivot table migration:
class CreateRoomStudentTable extends Migration
{
public function up()
{
Schema::create('room_student', function (Blueprint $table) {
$table->unsignedInteger('room_id');
$table->unsignedInteger('student_id');
// Primary key
$table->primary(['room_id', 'student_id']);
// Both foreign keys
$table->foreign('room_id')
->references('id')
->on('rooms')
->onDelete('cascade');
$table->foreign('student_id')
->references('id')
->on('students')
->onDelete('cascade');
});
}
// ...
Room and Student models:
class Room extends Model {
// ...
public function students()
{
$this->belongsToMany(App\Student::class);
}
}
class Student extends Model {
// ...
public function rooms()
{
$this->belongsToMany(App\Room::class);
}
}
Would this be a Many to Many relationship in both directions?
yes you need to Many to Many relationship in both directions
Would I need a pivot table to achieve this?
yes
you can see more about Many to Many relationship here
Yes you can implement many to many relationship and you will definitely need pivot table for implementing many to many relationship.
In this case you can create pivot table like room_student and add the room_id and student_id columns in it. then just use define relationship using belongsToMany method in your models and remember to use the attach method whereever you want to attach the functionality.
You need a pivot table.
About that, you can find out more from here:
ManyToManyRelationship
I am creating a purchased table in my application. So I have 2 tables : User and Product. Its a many to many relationship.
I know we have to create a new table for this. The naming convention for table is plural as users and products.
How would we call this purchase table? user_product or users_products?
Also I think I would need a model for this correct? If I do need a model should the naming convention for this model be User_Product?
From the documentation:
As mentioned previously, to determine the table name of the relationship's joining table, Eloquent will join the two related model names in alphabetical order. However, you are free to override this convention. You may do so by passing a second argument to the belongsToMany method
In your case, Laravel assumes that your joining table would be named product_user. No extra model is needed:
User.php
class User extends Model
{
//...
public function products()
{
return $this->belongsToMany(Product::class);
}
//...
}
Product.php
class Product extends Model
{
//...
public function users()
{
return $this->belongsToMany(User::class);
}
//...
}
And your schemas would look like so:
users table migration
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
//...
});
products table migration
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
//...
});
product_user table migration
Schema::create('product_user', function (Blueprint $table) {
$table->integer('product_id');
$table->integer('user_id');
//...
});
About the naming Convention, thats just something that will make your code more readable i think, so you can name it as you like(in case you are new and learning , my opinion is that its better to avoid being stuck in conventions at first , im still learning my self)
anyway a pivot model is not required, unless you simply need some custom behaviour
I think this would help you
class User extends Model
{
/**
* The products that belong to the shop.
*/
public function products()
{
return $this->belongsToMany('App\Products');
}
}
you can do this : $user->products or to query $product->users, or both.
Now, with such declaration of relationships Laravel “assumes” that pivot table name obeys the rules and is user_product. But, if it’s actually different (for example, it’s plural), you can provide it as a second parameter:
return $this->belongsToMany('App\Products', 'products_users');
If you want to know how to manage these you can find more in here
I have a User and a Quiz models. I have many-to-many relationship defined between them in the following way:
User model
public function subscriptions()
{
return $this->belongsToMany(Quiz::class, 'subs_users', 'user_id', 'quiz_id')->withTimestamps()->withPivot('accepted');
}
Quiz model
public function subscribers()
{
return $this->belongsToMany(User::class);
}
Pivot table
Schema::create('subs_users', function (Blueprint $table) {
$table->integer('user_id')->unsigned();
$table->integer('quiz_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('quiz_id')->references('id')->on('quizzes')->onDelete('cascade');
$table->primary(['user_id', 'quiz_id']);
$table->boolean('accepted')->index();
$table->timestamps();
});
When I call $quiz->subscribers, it returns a collection of users as expected. However, $user->subscriptions always returns an empty array. Why is that?
Edit
It seems, that replacing this line in Quiz
return $this->belongsToMany(User::class);
with
return $this->belongsToMany(User::class, 'subs_users', 'quiz_id', 'user_id')->withTimestamps()->withPivot('accepted');
Solves the issue, but I still can't understand why the first variant does not work.
Look at this:
public function subscriptions()
{
return $this->belongsToMany(Quiz::class, 'subs_users', 'user_id', 'quiz_id')->withTimestamps()->withPivot('accepted');
}
You mixed the foreign key with other key: user_id and quiz_id.
Remember when doing many to many relation that: first of foreign key's declared in belongsToMany is a key related to the current model.
Replacing belongsToMany() relationship in Quiz model with following:
return $this->belongsToMany(User::class, 'subs_users');
Solves the issue. It seems, that when a non-standard name is used for the pivot table, both sides must explicitly state it. In other words, 'subs_user' pivot table name must be present in belongsToMany() relationship declarations in both models.
I thought that eloquent would (by default) enforce uniqueness accross the two id columns when generating a simple pivot (many-to-many) table?
Or am I doing something wrong?
Appointment model
public function client()
{
return $this->belongsToMany('App\Client')->withTimestamps();
}
Client model
public function appointment()
{
return $this->belongsToMany('App\Appointment')->withTimestamps();
}
Migration
$table->integer('appointment_id')->unsigned();
$table->foreign('appointment_id')->references('id')->on('appointments')->onDelete('cascade');
$table->integer('client_id')->unsigned();
$table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
$table->timestamps();
If eloquent doesn't do it by default, I'll have to add the index manually:
$table->unique(array('appointment_id', 'client_id'));
Eloquent can't enforce uniqueness accross the two id columns, because Eloquent is not responsible for the database schema/configuration.
Migrations are responsible for this stuff, so you have to define uniqueness in a migration.