I'm having trouble relating photos to tags using an intermediate table.
In the example below, how can I select all photos that belong to tag 1 with an Eloquent relationship method in Laravel?
I have these tables:
-Photos Table
| id | name | description |
1 photo1.png ....
2 photo2.png ....
3 photo3.png ....
-Tags Table
| id | name |
1 Aesthetic
2 Dark
-Tags Relations
| id | tag_id | photo_id |
1 1 3
2 1 2
3 2 1
First of, you need to ensure that both Photos and Tags table have the relationship defined.
Under the Photos model you should have the following function:
public function tags() {
return $this->belongsToMany(
Tag::class,
"photos_tags", // the name of the pivot table
"photo_id",
"tag_id"
);
}
Under the Tags model you should have the following function:
public function photos() {
return $this->belongsToMany(
Photo::class,
"tags_photos", // the name of the pivot table
"tag_id",
"photo_id"
);
}
Now to access all the tags that are related to the Photo of id 1, you can call the following:
Photo::findOrFail(1)->tags()->get();
And the same you can do for a specific tag to get all it's photos.
Tag::findOrFail(1)->photos()->get();
Hope this will lead you to what you wish.
Related
I have the following table structure:
Products
========
id | name
------|-------
1 | A
2 | B
Stocks
========
id | product_id | color_id | size | qty
------|------------|----------|----- |-----
1 | 1 | 1 | S | 37
2 | 1 | 1 | XL | 89
3 | 1 | 2 | S | 6
4 | 1 | 2 | L | 8
Colors
========
id | name | hex
------|-------|-------
1 | Red | #ff0000
2 | Green | #00ff00
What I want is to get the list of products with each of its available colors and sizes, so:
Product {
id: string
name: string
available_sizes: string[]
available_colors: Color[]
}
I looked up the Laravel documentation and figured that I need to use hasManyThrough (probably) to make a relation between Product <==> Color. But since my colors table doesn't have a stock_id, I am not understanding how can I connect them.
How can I achieve this?
As I understand, hasManyThrough works in a scenario that a product has many stocks and a stock has many colors. This is not the case.
You can simply eager load the relationship and present the data the way you want, using collection and higher order messages:
public function index()
{
$products = Product::with('stocks.color')->get();
return ProductResource::collection($products);
}
class ProductResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'available_sizes' => $this->stocks->map->size->unique()->toArray(),
'available_colors' => $this->stocks
->map->color
->map->name
->unique()
->toArray()
]
}
}
here you will use models
ProductModel
public function stocks()
{
return $this->belongsto(StockModel::class,'product_id');
}
StockModel
public function colors()
{
return $this->hasmany(ColorModel::class,'color_id');
}
Controller
$product=Product::with('stocks.colors')->find($id);
dd($product);
and check if the data are available.
or according to the documentation hasmanythrough
ProductModel:
public function colors(){
return $this->hasManyThrough(
Stock::class,
Color::class,
'product_id', // Foreign key on the product table...
'color_id', // Foreign key on the colors table...
'id', // Local key on the products table...
'id' // Local key on colors table...
);
}
Controller:
$product=Product::with('colors')->find($id);
If the bookstore does not have any books in stock, this bookstore can purchase books from the other bookstore it is contracted with.
book_transfers table is as follows.
ID | sender_bookstore_id | delivery_bookstore_id
1 | 2 | 3
2 | 1 | 2
3 | 3 | 1
books table
ID | store_name
1 | London Garden Book Store
2 | Englaland Cafe Book
3 | Domesday Book Store
Two-column sender_bookstore_id and delivery_bookstore_id belong to the same table. The name of this table books.
My Controller code is below:
$data = BookTransfer::join('books', 'books.id', '=', 'book_transfers.sender_bookstore_id')
->select(
'book_transfers.*',
'book_transfers.sender_bookstore_id as sender_id',
'book_transfers.delivery_bookstore_id as delivery_id',
'books.store_name as sender_store_name'
)
->groupBy('book_transfers.id')
->get();
return $data;
I can only get sender_store_name here. I need to add extra code for delivery_store_name in the above controller But I don't know how to get delivery_store_name.
Best Regards,
Using relation would be much more simpler.
// BookTransfer Model
public function sender(){
return $this->belongsTo(Book::class, 'sender_bookstore_id');
}
public function delivery(){
return $this->belongsTo(Book::class, 'delivery_bookstore_id ');
}
Your controller would look like this
$data = BookTransfer::with(['sender', 'delivery'])->get();
If you want to get the datas by join, you can join the same books table again, and alias it another name:
BookTransfer::join('books AS sender_books', 'sender_books.id', '=', 'book_transfers.sender_bookstore_id')
->join('books AS delivery_books', 'deliery_books.id', '=', 'book_transfers.delivery_bookstore_id')
->select(
'book_transfers.*',
'book_transfers.sender_bookstore_id AS sender_id',
'book_transfers.delivery_bookstore_id AS delivery_id',
'sender_books.store_name AS sender_store_name',
'delivery_books.store_name AS delivery_store_name'
)
My problem is I am trying to establish a many to many relationship between posts and genre. Here is what I have done so far.
class Post extends Model
{
public function genres()
{
return $this->belongsToMany('App\Genre');
}
}
class Genre extends Model
{
public function posts()
{
return $this->hasMany('App\Post');
}
}
genre Table
+----+--------+
| id | name |
+----+--------+
| 1 | News |
| 2 | Sports |
+----+--------+
post table
+----+----------------+
| id | title |
+----+----------------+
| 1 | Political News |
| 2 | Sport Update |
+----+----------------+
genre_post table
+----+---------+----------+
| id | post_id | genre_id |
+----+---------+----------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 2 |
+----+---------+----------+
When I am trying to acess genre list of a post, everything works fine.
Post::where('slug', '=', $id)->with("genres")->first(); // no problem
But when I tried the opposite it's not working.
$posts = Genre::where( "slug", "=", $id )->with("posts")->first();
I am getting the following error.
Column not found: 1054 Unknown column 'post.genre_id' in 'where
clause' (SQL: select * from post where post.genre_id
I understand the laravel is trying to aceess genre_id column from post table which doesn't exists since it's a many to many relation which means one post can contain more than one genre and one genre can contain more than one post.
Any idea how can I resolve this?
This is expected since hasMany itself an one to many relationship. Use belongsToMany instead.
class Genre extends Model
{
public function posts()
{
return $this->belongsToMany('App\Post');
}
}
hello, sorry for my english. I will try explain my problem.
For example, we have model Product.
Each product has some options:
product_1 : option_1 = 50, option_2 = 14, option_3 = 23
Whats is the right way?
First - create database, like
id | title | option_1 | option_2 | option_3
Second - create models and tables, like
ProductModel hasMany optionModel
OptionModel belongsToMany ProductModel
tables in databases: Product, Option, Product_Option_Relationships
Third - create some collection in function and table Product_Option_Relationships like
$options = collect([
['id' => '1', 'name' =>'option1'],
['id' => '2', 'name' =>'option2'],
]);
Table: id | product_id | option_id
Or maybe exist normal way, because first - its too big table, when you have 20 options, second - create new model only for information function, i dont now, its normal? Third - too difficult in view show options name.
Thank you, i hope you understand me.
Generally use the one-to-many, many-to-many relationships
And the benefit for that you can freely edit any record without modifying the whole column to apply that on your tables :
First we have products table which is going to require options foreach
so we should have the table options which is going to combine the options in general then we add a new table assignOptionsToProducts which is include keys for both options & products in this case you're going to have many-to-many or one-to-many relationship as you like
Products Table
id | Name
1 | Product A
2 | Product B
Options Table
id | Name
1 | Option A
2 | Option B
AssignOptionsToProducts Table
id | Product_id | Option_id
1 | 1 | 1
2 | 1 | 2
3 | 2 | 2
As you can see we assigned the same option many times
And when we want to modify any option you can without modifying each record in other tables and of course you can use each table many times easily
Use the second way. You won't have repeated options. For example:
products
id | name
---|------
1 | Car
2 | Bike
options
id | name
-----|------------
1 | Transport
option_product
option_id | product_id
------------|-------------
1 | 1
1 | 2
Using the other ways, you would have the option Transport twice.
You can use many-to-many relationship and can structure it like so:
Product Model
class Product extends Model {
...
public function options() {
return $this->belongsToMany('App\Product', 'product_options', 'product_id', 'option_id');
}
}
Options Model
class Option extends Model {
...
public function product() {
return $this->belongsToMany('App\Option', 'product_options', 'option_id', 'product_id');
}
}
You will need three tables for this to work:
Table products
id | name
-----------------
1 | iBeacon
2 | Intel Edison
Table options
id | name
----------
1 | Price
2 | Size
Table product_options
id | option_id | product_id
---------------------------
1 | 1 | 2
2 | 2 | 2
You can choose if you want to store a record with the option value in the options table or in the pivot table. I'd place them in the pivot table to keep the options table smaller.
Now you'll be able to assign options to your products like so:
...
// Assing options to a product
$product->options()->sync([$optionID, $optionID]);
// Get product's options
$product->options;
// Get products having certain option
$option->products;
...
I'm working on a laravel 5 application.
I have 3 tables:
table A: 'users'
id | name
————————————————
1 | Dave
2 | Robet
3 | Jimmy
4 | Daniel
and Table B classroom_users
user_id | classroom_id
1 | 5
4 | 5
3 | 6
4 | 5
I need to return all users 'name' for classroom 5
Please let me know what is the best way to do that. Trough model or Database join
Any help appreciated.
Assuming you have models and its relationships will be defined as below:
Classroom Model:
public function users(){
return $this->belongsToMany(User::class,'classroom_users','classroom_id','user_id');
}
User Model:
public function classrooms(){
return $this->belongsToMany(UserClassrooms::class,'classroom_users','user_id','classroom_id');
}
Now, in controller you can retrieve the users belonging to the classroom id 5 as below:
$classroom_id = 5 ;//change this to the id you want
$users = Classroom::find($classroom_id)->users()->get();
Laravel has introduced Pivot tables, which works like this
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
You can read more pivot on Official Docs