i'm trying to understand laravel by creating a messaging application. User should be able to send message to each other. i have made a similar application using core php.
I'm done with login/authentication and migration and now stuck at defining relationship in models;
i have created 3 tables using migrations:
users
conversations
conversations_reply
This is the Schema of:
users table (For storing detail of users)
$table->increments('id');
$table->string('username', 50);
$table->string('password', 50);
$table->string('name', 50);
$table->string('email', 254);
$table->timestamps();
conversations table(For storing conversation between users)
$table->increments('id');
$table->integer('user_one'); //foreign key of one friend from users table
$table->integer('user_two'); //foreign key of second friend from users table
$table->string('ip');
$table->timestamps();
conversations_reply table(For storing Conversation text)
$table->increments('id');
$table->text('reply');
$table->integer('user_id');
$table->integer('conversation_id'); //foreign key of conversations table
$table->string('ip');
$table->timestamps();
Now, i'm trying to define relationships in models as:
User model wil have hasMany relationship with both Conversation and ConversationReply model.
Conversation will have belongsToMany relationship with User model and hasMany relationship with ConversationReply model.
ConversationReply model will have belongsToMany relationship with both User and Conversation model.
Now i'm stuck at defining relationship in the first model(User)and unable to proceed further because i need to define local and foreign key, but i'm unable to do so because conversations table will have 2 foreign keys(of 2 users) and i can define only one foreign key.
Edit: There should be only two members in a conversation and and two users should have only one conversation(like facebook). Their new messages should be added to their old conversations. In conversations table, ip is the ip address of the user who would start the conversation and in the conversations_reply table, ip is the respective ip of the user
There seems to be a little flaw in your abstraction. You have actually designed user1 and user2 as attributes of the Conversation entity, but they are not attributes. Also, what is the IP of a conversation?
Attributes of a Conversation may be topic, start time, end time, amount of messages and things like that.
And then a conversation has members. Not exactly two but many. So you could just create an entity / model ConversationMembers that connects User and Conversation:
conversation_members table:
$table->increments('id');
$table->integer('conversation_id');
$table->integer('user_id');
$table->string('ip');
$table->string('nickname');
and change the conversations table accordingly:
$table->increments('id');
$table->boolean('public);
// other attributes you're interested in
$table->timestamps();
Now you can define the relationships on your models:
Conversation:
public function members()
{
return $this->hasMany('ConversationMember');
}
public function messages()
{
return $this->hasMany('ConversationReply');
}
ConversationMember:
public function user()
{
return $this->belongsTo('User');
}
public function conversation()
{
return $this->belongsTo('Conversation');
}
User:
public function conversations()
{
return $this->hasManyThrough('Conversation', 'ConversationMember');
}
public function replies()
{
return $this->hasMany('ConversationReply');
}
I hope this helps.
Related
I want to create a relationship between the three models. My models are
Users
-- Database Structure
id
name
---
Books
-- Database Structure
id
name
BookShelf
-- Database Structure
id
name
1: User has many books and book belongs to many users
2: User's book belongs to one BookShelf and user's BookShelf has many books.
How can I define the relationship between these three models? I am building an application something like Goodreads.
You have a many to many relationship between your User and Book, as well as a many to many between your Book and BookShelf.
This type of relationship is managed by a pivot table behind the scenes, so you will require two pivot tables.
Pivot between User and Book
Create your migration
php artisan make:migtration create_book_user_table
Define your relationship
public function up()
{
Schema::create('book_user', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->foreignId('user_id')->constrained();
$table->foreignId('book_id')->constrained();
});
}
Pivot between Book and BookShelf
Create your migration
php artisan make:migtration create_book_book_shelf_table
Define your relationship
public function up()
{
Schema::create('book_user', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->foreignId('book_id')->constrained();
$table->foreignId('book_shelf_id')->constrained();
});
}
With the pivot tables create you can add the relationships to your models.
User
public function books()
{
return $this->belongsToMany(Book::class);
}
Book
public function users()
{
return $this->belongsToMany(User::class);
}
public function bookshelves()
{
return $this->belongsToMany(BookShelf::class);
}
BookShelf
public function books()
{
return $this->belongsToMany(Book::class);
}
Now you have your relationships, you can access them on your User. As an example:
Route::get('/', function () {
$user = User::where('id', 1)->with(['books', 'books.bookshelves'])->first();
return view('user', compact('user'));
});
user.blade.php
<h3>{{ $user->name }}</h3>
<h4>Books</h4>
#foreach ($user->books as $book)
<h5>{{ $book->name }}</h5>
<p>Can be found on #choice('shelf|shelves', $book->bookShelves()->count())</p>
#foreach ($book->bookShelves as $shelf)
<li>{{ $shelf->name }}</li>
#endforeach
#endforeach
The above would iterate over each Book for the $user and then iterate over each of the BookShelves that Book is related to.
Update
If a Book can only belong to one Bookshelf, you need to alter your books table.
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->string('name');
$table->foreignId('bookshelf_id')->constrained('book_shelves');
});
}
You will also need to alter the relationship on your Book model:
public function bookshelf()
{
return $this->belongsTo(BookShelf::class);
}
This way a Book can now only belong to one Bookshelf.
If a User can only have one Bookshelf, again you need to alter your book_shelves table:
public function up()
{
Schema::create('book_shelves', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->string('name');
$table->foreignId('user_id')->constrained();
});
}
Then add the relationship to your User.
public function bookshelf()
{
return $this->hasOne(BookShelf::class);
}
You should then be able to access the Bookshelf for a User directly.
$user->bookshelf->books
1: User has many books and book belongs to many users
User has a Many To Many relationship with Book
// User model
public function books()
{
return $this->belongsToMany(Book::class, 'book_user');
}
Book has a Many To Many relationship with User
// Book model
public function users()
{
return $this->belongsToMany(User::class, 'book_user');
}
2: User's book belongs to many BookShelf and BookShelf has many books.
Book has a Many To Many relationship with Bookshelf
// Book model
public function bookshelves()
{
return $this->belongsToMany(Bookshelf::class, 'book_bookshelf');
}
Bookshelf has a Many To Many relationship with Book
// Bookshelf model
public function books()
{
return $this->belongsToMany(Book::class, 'book_bookshelf');
}
You'd need two pivot tables.
book_user
column
migration
id
$table->id()
book_id
$table->foreignId('book_id')->constrained('books')
user_id
$table->foreignId('user_id')->constrained('users')
book_bookshelf
column
migration
id
$table->id()
book_id
$table->foreignId('book_id')->constrained('books')
bookshelf_id
$table->foreignId('bookshelf_id')->constrained('bookshelves')
Let's start from first task:
1: User has many books and book belongs to many users
In this case it is many-to-many relationships. To implement this setup you need users table like this:
Users: id, name, someotherdata
then you need books table:
books: id, name, bookdata
and then you need the third table to connect them many to many:
users_books: id, book_id, user_id
In this case when you need all books which belongs to one user you can select from users_books where user_id=someuserid and join it with books table and you will receive all books which belongs to some user.
Visa versa you can receive all users who posses some particular book.
2: User's book belongs to one BookShelf and user's BookShelf has many books.
For this you would have to create table bookshelfs: id, name
And add foreign key to books table as bookshelf_id
Then your books table will look like this:
books: id, name, bookdata, bookshelf_id.
So when you need to get all books from shelf just filter select from books where bookshelf_id = someid
I'm building a rudimentary CRM app using Laravel 6.0. Users can freely create accounts, but to get any functionality out of the app, they need to set up a SubscriptionAccount (or join an existing one), which will then allow them to create/manage customer Accounts, add Users, etc. (each is a one to many).
The User model's relationship to SubscriptionAccount model is giving me issues. For example:
$user = User::find(1);
$user->subscription()->create(['name' => 'Test Subscription']);
$user = $user->fresh();
dd($user->subscription); // returns null
I suspected it had to do with the belongsTo relationship in the User model, but the odd thing is that it actually creates and persists a new SubscriptionAccount while using that relationship (second line above), though if you access users relationship from the new SubscriptionAccount it also returns null.
Here are the models:
// User.php
class User
{
public function subscription()
{
return $this->belongsTo(SubscriptionAccount::class, 'subscription_account_id');
}
}
// SubscriptionAccount.php
class SubscriptionAccount extends Model
{
public function users()
{
return $this->hasMany(User::class, 'subscription_account_id');
}
}
The only thing out of the ordinary is shortening the name of the relationship to subscription from SubscriptionAccount, but that should have been taken care of by specifying the foreign key in both relationships. Here's the migrations:
Schema::create('subscription_accounts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->uuid('uuid')->unique();
$table->string('name');
$table->timestamps();
});
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->uuid('uuid')->unique();
$table->bigInteger('subscription_account_id')->unsigned()->index()->nullable();
$table->string('name');
...
$table->timestamps();
$table->foreign('subscription_account_id')
->references('id')
->on('subscription_accounts');
});
If I create the user from a SubscriptionAccount (i.e. $subscriptionAccount->users()->create([...]); it sets the correct subscription_account_id on the users table, but doesn't work vice versa.
This is a known issue (feature?) with the belongsTo relationship:
https://github.com/laravel/framework/issues/29978
To work around it you can associate the models manually:
$user = User::find(1);
$sub = Subscription::create(['name' => 'Test Subscription']);
$user->subscription()->associate($sub);
$user->save();
So instead of using belongsTo because a subscription account does not belongs to one user, it can belong to many, you might want to use the hasOne relationship instead:
public function subscription()
{
return $this->hasOne(SubscriptionAccount::class, 'id', 'subscription_account_id');
}
It will belongTo one User if you had a user_id within the subscription_accounts table.
Let me know if it makes sense and if it works :)
This app has a Bug model and a User model. Users can "follow" Bugs. This seems like a pivot table. What's the simplest way to implement this with a migration and Model relationship? I'm thinking along the lines of a followers table:
Schema::create('followers', function (Blueprint $table) {
$table->increments('id');
$table->uuid('bug_id');
$table->uuid('user_id');
$table->index(['bug_id', 'user_id']);
$table->timestamps();
$table->softDeletes();
});
Where I'm mostly stumped is the followers relationship in the Bugs Model. This is where I'm currently at:
public function followers()
{
return $this->belongsToMany(User::class, 'followers', 'bug_id', 'user_id');;
}
This really doesn't seem right. New to eloquent pivot tables, so really appreciate the help!
You are looking for a many-to-many relationship which requires an intermediary table bug_follower just as you created one and you also need to create a relationship from Bug to Follower as you did, but also create the relationship from Follower to Bugs. You are on the right track.
Overall you will have 2 models: Bug, Follower.
You will also have 3 tables: bugs, followers, bug_follower
And 2 relationships from Bug and Follower to each other
EDIT: This is how your relationship needs to look like:
public function followers()
{
return $this->belongsToMany(User::class, 'followers', 'bug_id', 'user_id');
}
You can check more about subject in the docs.
I would like to a user be able to only have one distrito (it means district in Portuguese). A distritocan have more than one user. It's a fairly simple relationship but the problem comes when you have a third table called distritosthat it's only job is to translate numbers into strings (I don't know the name of such tables).
Distrito table
$table->increments('id');
$table->string('distrito');
e.g.
ID DISTRITO
1 - Aveiro
2 - Beja
3 - ...
User tables
$table->string('name');
$table->string('last_name');
$table->string('profile_picture')->default('default.jpg');
$table->string('userProfile')->default('');
$table->float('stars')->default(0);
distrito_user table this table will reference where the user live
$table->integer('user_id'); //id of the user
$table->integer('distrito_id'); //Id of the district where the user lives
User's model
public function distritoUtilizador(){
return $this->belongsTo(DistritoUser::class);
}
DistritoUser's model
protected $table = 'distrito_user';
Calling the eloquent model
$user = Auth::user();
dd($user->distritoUtilizador);
It returns null.
First question: Is this the correct way to solve my problem (the user lives somewhere and I need to fill that location)?
If this is the right way to solve my problem: Why it's not returning the desired results?
For a simple one to many, just store the foreign key to distrito on the user model, no need for intermediate tables.
$table->increments('id')->unsigned();
$table->string('name');
$table->string('last_name');
$table->string('profile_picture')->default('default.jpg');
$table->string('userProfile')->default('');
$table->float('stars')->default(0);
$table->integer('distrito_id')->unsigned();
$table->foreign('distrito_id')->references('id')->on('distrito');
and get rid of the distrito_user table altogether. now use has this method:
public function distrito(){
return $this->belongsTo(Distrito::class, 'id');
}
while distrito has:
public function users(){
return $this->hasMany(User::class);
}
also change distrito migration to:
$table->increments('id')->unsigned();
I have following tables:
users (classic laravel table)
events
user_events
in user_events I insert assigned users to events, the table structure is so:
Schema::create('user_events', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users')->onUpdate('cascade')->onDelete('cascade');
$table->integer('event_id')->unsigned();
$table->foreign('event_id')->references('id')->on('events')->onUpdate('cascade')->onDelete('cascade');
$table->timestamps();
});
I want to create a page for auth user with his events.
To get Id's I use following relation in Model User:
public function userEvents() {
return $this->hasMany('App\Models\UserEvent');
}
Trought controller I get a list of Event id's.
My previous sources are:
Laravel relation between 3 models
Laravel 5 relation between 3 tables
Laravel Eloquent Relation between 3 table
My question is, what is here to do to get throught this event id's event names?
You could pluck the name from collection returned by $user->userEvents :
$user->userEvents->pluck('name');
Hope this helps.
By looking at your schema the relation should be many-to-many instead of one-to-many.
So your relation in User model looks as:
public function events()
{
return $this->belongsToMany('App\Models\Event');
}
Then in your controller, get all the events of authenticated user by:
$events = auth()->user()->events; // returns collection of Event model