Laravel Eloquent Relationship Many to Many Naming Convention - laravel

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

Related

Laravel database relationship in migration vs in class - Confused - Both of them are need?

I am confused about Laravel database relationship (unsigned id, foreign references, cascade).
Do I have to use relationship in class (like hasMany, hasOne) and in table migration (like foreign, references); for both of them too?
I had read some articles but they are not clear for me. what is the best way for best developing on an example?
As an example for category and blog post; how should it be or your best example please?
create_categories_table migration:
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug');
$table->timestamps();
});
}
create_posts_table migration:
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('slug');
$table->longText('description');
$table->timestamps();
});
}
and Post Model:
public function category()
{
return $this->belongsTo(Category::class);
}
thank you.
Technically you don't have to define relationships between related data in your database, however, it is good practice to do so and provides benefits such as data integrity and cascading. There are also tools that can reverse engineer your database to generate a visual representation of its structure including relationships if they have been defined.
Adding relationships in your migration files creates that relationship at the database level, informing the engine that there is a logical relationship between data. You can define a foreign key constraint in your migrations in many ways, however, as of Laravel 7.x there is a forieignId method and a constrained method providing a simplified way of defining relationships from the previous method.
So for a basic example, to define a simple one-to-one relationship you might do:
public function up()
{
Schema::create('child', function (Blueprint $table) {
$table->foreignId('parent_id')->constrained();
});
}
Similarly, using Eloquent relationships to define relationships at the application level is not required. However, these helper methods provide a consistent and simplified implementation for managing related data that would otherwise require you to write additional code.
At least one but ideally both of the above would be used.
Update
if we need set a foreign key as a different key the using is just like
->constrained('privateName_categories'). two basic questions: * how Laravel understands that the reference table is 'categories' on your
example without writing its name?
The argument provided to constrained is not the name of the foreign key field but the name of the table the foreign key references. This is used in instances that either your foreign key or the table it references differ from the standard Laravel naming conventions and therefore cannot be inferred.
Laravel uses naming conventions to make connections between things. For foreign keys that convention has the format {table}_{id}. Laravel inspects the foreign key name and then uses the {table} element of the convention to create the relationship behind the scenes. If you're not using conventions you need to inform Laravel.
The following uses conventions to automatically create a relationship between the posts and categories table based on the value of the foreignId method argument.
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->foreignId('category_id')->constrained();
});
}
This example doesn't use a standard foreign key naming convention and so the related table needs to be provided to constrained.
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->foreignId('another_field')->constrained('categories');
});
}
Similarly, for scenarios in that you're not using conventions, you might need to specify the related column names in your Eloquent Model relationship. The exact syntax for this differs based on the type of relationship (hasOne, hasMany, manyToMany, etc.). You can read up on the different syntaxes for each relationship in Laravels defining relationships documentation.
you mention about reverse engineering tools
DataGrip by JetBrains can inspect the schema of your database and produce a visual representation. Others are also available, just do a search of the internet.
The role of migrations is to build your database schema (INSERT TABLE, ALTER TABLE, FOREIGN KEYS, DROP COLUMN, etc...).
The role of relations in Eloquent Model is to make Model aware of relations with other models. It provides a convenient way to query related models.
Relations will not create foreign keys for you.
For example if a post can be in only one category and a category contains many posts :
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug');
$table->timestamps();
});
}
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('category_id');
$table->string('title');
$table->string('slug');
$table->longText('description');
$table->timestamps();
$table->foreign('category_id')->references('id')->on('categories');
});
}
Then, in your Post model :
public function category()
{
return $this->belongsTo(Category::class);
}
If you need you can also add this to Category Model :
public function posts()
{
return $this->hasMany(Post::class);
}
This will allow you to easily retrieve all posts of a category for example :
public function getPostsByCategory(int $categoryId)
{
$posts = Category::find($categoryId)->posts()->get();
return $posts;
}
EDIT :
I think the main interest is when you want to eager load relations.
For example you have posts in a blog.
Each post have many categories. (a post can be in many categories).
Each category have many posts.
Each post have many comments
In homepage I want to display last 5 posts with for each post their categories and comments.
If I build well designed relations, I can do this:
public function getLatestPosts()
{
return Post::query()
->with(['categories', 'comments'])
->orderBy('created_at', 'desc')
->limit(5)
->get();
}
It will get last 5 posts and it will attach categories and comments to each Post model.

laravel hasMany polymorphic relationship in where clause

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();

Eloquent: Difference between defining relations in model and in migration

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),
...
);

Eloquent Relationships Not Working as Expected

I am trying to understand what I am missing here.
Apps migration
Schema::create('apps', function (Blueprint $table) {
$table->increments('id');
$table->integer('show_id')->unsigned()->index();
$table->string('name');
$table->integer('provider_id')->unsigned()->index();
$table->timestamps();
});
Show migration
Schema::create('shows', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
So I create an apps model that has the following function
public function Show() {
return $this->hasOne(Show::class);
}
But in php artisan tinker when I do $app->Show; I get the following error:
Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 1 no such column: shows.app_id (SQL: select * from "shows" where "shows"."app_id" = 1 and "shows"."app_id" is not null limit 1)'
Am I mis-understanding the relationships?
Your relation should be as:
Apps model:
public function show() {
return $this->hasOne(Show::class, 'id', 'show_id');
}
Or it can be:
public function show() {
return $this->belongsTo(Show::class);
}
Docs
You do not have an app_id in your shows migration.
edit: Taking from the Laravel Docs and changing it to fit your situation
Eloquent determines the foreign key of the relationship based on the model name. In this case, the show model is automatically assumed to have a app_id foreign key.
A one-to-one relationship consists of a hasOne and a belongsTo. The table that contains the foreign key field must be on the belongsTo side of the relationship.
Since your apps table contains the show_id field, it is stated that apps belong to shows, and shows has one (or many) apps.
Given this, you need to change your Show relationship on your Apps model to use the belongsTo relationship.
public function Show() {
return $this->belongsTo(Show::class, 'show_id');
}
Unless you rename your relationship method so that it is lowercase (function show()), the second parameter is required. If you renamed the relationship, Laravel could build the proper key name and you could leave off the second parameter:
public function show() {
// For belongsTo, second parameter defaults to {function_name}_id.
return $this->belongsTo(Show::class);
}
In your apps model :
public function Show() {
return $this->belongsTo('yourmodelnamespace\Show','id','show_id');
}
And you need create Show model too ..
Hope it will works~~
You can use relation like this
public function Show() {
return $this->hasOne(Show::class, 'id','id');
}

Many to Many relationship works one way

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.

Resources