0
votes

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.

3

3 Answers

0
votes

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')
0
votes

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

0
votes

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

  1. Create your migration
php artisan make:migtration create_book_user_table
  1. 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

  1. Create your migration
php artisan make:migtration create_book_book_shelf_table
  1. 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