We have a user who can learn in several ways. In each direction, the user has some level. There are three models: user, direction, level. They need to be linked in such a way that the user can get a list of its directions, and in each direction its level. For example, $user->directions[0]->level.
Once again regarding the relationship. A user may have many directions, and a direction many users (many to many). Direction can have many levels, but the level is only one direction (one to many).
I use Laravel 5.8.
Since a User has many Directions and a Direction has many Users you should use belongsToMany (Eloquent relationship which related to many-to-many relationship of database.) relationship.
And a Direction has many Levels but a Level has only one Direction you should use hasMany and belongsTo on Direction and Level respectively.
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function directions()
{
return $this->belongsToMany(Direction::class);
}
}
use Illuminate\Database\Eloquent\Model;
class Direction extends Model
{
public function levels()
{
return $this->hasMany(Level::class);
}
public funciton users()
{
return $this->belongsToMany(User::class);
}
}
use Illuminate\Database\Eloquent\Model;
class Level extends Model
{
public function direction()
{
return $this->belongsTo(Direction::class);
}
}
UPDATE
In case you haven't defined database tables. This is how you may do it.
You can use Laravel migrations to create database tables.
User Table
Schema::create('users', function (Blueprint $table) {
$table->increment('id')->primary();
// all the other columns goes here.
});
Directions Table
Schema::create('directions', function (Blueprint $table) {
$table->increment('id')->primary();
// all the other columns goes here.
});
Directions Users Table
Schema::create('direction_user_table', function (Blueprint $table) {
$table->unSignedInteger('user_id');
$table->unSignedInteger('direction_id');
// all the other columns goes here.
});
Levels Table
Schema::create('levels', function (Blueprint $table) {
$table->increment('id')->primary();
$table->unSignedInteger('direction_id');
// all the other columns goes here.
});
You can use relationships like below in laravel model.
For User model
public function directions()
{
return $this->hasMany('App\Direction','user_id');
}
For Direction model
public function users()
{
return $this->hasMany('App\User','id','user_id');
}
public function levels()
{
return $this->hasMany('App\Level','direction_id');
}
For Level model
public function direction()
{
return $this->hasOne('App\Direction','id','direction_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
);
}
}
if instead of having a users table where one user can follow many users. I would have a cows table where each cow has a single father and a single mother, where the parents can have many children. do I require an external table to store that or can I just add in my cows table the fields cow_father_id and cow_mother_id?
-referring to making 2 eloquent relationships of cows table with same cows table
and what this migration would look like?
You could do this. I've tested as well.
Migration
Schema::create('cows', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('father_id')->nullable();
$table->integer('mother_id')->nullable();
$table->timestamps();
});
Model
class Cow extends Model
{
use HasFactory;
public function father()
{
return $this->belongsTo(self::class, 'father_id');
}
public function mother()
{
return $this->belongsTo(self::class, 'mother_id');
}
public function children()
{
return $this->hasMany(self::class, 'father_id')->orWhere('mother_id', $this->id);
}
}
I am trying to select the picture with the most likes within a specific category from my database. I have a database table storing the pictures and a table storing the likes. The pictures table is related to likeable table through a hasMany polymorphic relationship.
Like model:
public function likeable()
{
return $this->morphTo();
}
Picture model:
public function likes()
{
return $this->morphMany('App\Like', 'likeable');
}
Home Controller:
Picture::where('picture_type', 'food')->orderBy(likes(), 'desc')->first();
Likeable Migration:
public function up()
{
Schema::create('likeable', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->integer('likeable_id');
$table->string('likeable_type');
$table->timestamps();
});
}
So far, the above line of code, in my home controller returns an error. From what I understand, the likes method has to be called on a specific instance. However, I do not know how to implement this all the while keeping my where clause.
Thank you for your help.
I assume you are wanting to order pictures by the most likes.
In which case, you want something like this:
Picture::withCount('likes')->where('picture_type', 'food')->orderBy('likes_count', 'desc')->first();
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 2 tables A and B.
I make a many to many Relationship with a pivot table called A_B
Now I need to make other ManyToMany Relationship between A_B and User ( U )
So I have another table A_B_U
Now, What I need and can't do it, is writing a relationship from A to A_B_U.
I set U to a vale, so A_B -> A_B_U is now a belongsTo Relationship, I should be able to access it easily, but I can't write it, I can write the first relationship, but then, I can't write the second one.
I don't know neither how to pass a param in ManyToMany Rel.
In fact, it shoud be a belongsTo, not ManyToMany so I'm a bit lost in all that....
Any Idea how to do it?
What it looks like
category_tournament_user
/ \
category_tournament users
/ \
tournaments categories
Migrations
Schema::create('tournaments', function(Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
Schema::create('categories', function(Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
Schema::create('category_tournament', function(Blueprint $table) {
$table->increments('id');
$table->integer('category_id');
$table->integer('tournament_id');
$table->timestamps();
});
Schema::create('category_tournament_user', function(Blueprint $table) {
$table->integer('category_tournament_id');
$table->integer('user_id');
});
Models
class Tournament extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
public function categoryTournaments()
{
return $this->hasMany(CategoryTournament::class);
}
}
class Category extends Model
{
public function tournaments()
{
return $this->belongsToMany(Tournament::class);
}
public function categoryTournament()
{
return $this->hasMany(CategoryTournament::class);
}
}
class CategoryTournament extends Model
{
protected $table = 'category_tournament';
public function category()
{
return $this->belongsTo(Category::class);
}
public function tournament()
{
return $this->belongsTo(Tournament::class);
}
public function users()
{
return $this->belongsToMany(User::class, 'category_tournament_user');
}
}
class User extends Authenticatable
{
public function categoryTournaments()
{
return $this->belongsToMany(CategoryTournament::class, 'category_tournament_user');
}
}
Get all users of a tournament
$tournament = App\Tournament::with('categoryTournaments.users')->find(1);
foreach($tournament->categoryTournaments as $categoryTournament) {
// To get the category of the tournament, this will return an App\Category
$category = $categoryTournament->category;
// If you have a name column on your categories table...
$categoryName = $category->name;
// To get the tournament.
$tournament = $categoryTournament->tournament;
// To get the name of the tourament (if there is a name column)
$tournamentName = $tournament->name;
$tournament->created_at; // etc...
// To get all the people participating in this tournament category
foreach($categoryTournament->users as $user) {
echo $user->email;
}
}
If you need to figure out how to traverse the relationships (getting all tournaments and categories a user is participating in, etc...) please feel free to ask.
Starting at the User
// This will list all tournament and category names a user belongs to.
$user = App\User::with('categoryTournaments.tournament', 'categoryTournaments.category')->find($userId);
foreach($user->categoryTournaments as $categoryTournament) {
echo 'Category Name: '.$categoryTournament->category->name;
echo 'Tournament Name: '.$categoryTournament->tournament->name;
echo '<br>';
}
Edit
users and category_tournament make a belongsToMany relationship.
categories and tournaments make a belongsToMany relationship.
category_tournament also has a belongsTo to both tournaments and categories
There is a possibility we can further simplify this if you remove the tables tournaments and categories and drop all their columns right into category_tournament. That way, you end up with only one belongsToMany relationship to manage.