Multi JOIN Table in Laravel - php

I have 4 tables below :
track
+----+-----+----------------+-------+
| ID | TID | TITLE | ALBUM |
+----+-----+----------------+-------+
| 1 | AAA | Yesterday | 1 |
| 2 | BBB | Happy | 2 |
| 3 | CCC | Gangname Style | 3 |
+----+-----+----------------+-------+
album
+----+-----+---------+-------+
| ID | AID | TITLE | COVER |
+----+-----+---------+-------+
| 1 | AAA | Album A | 1.jpg |
| 2 | BBB | Album B | 2.jpg |
| 3 | CCC | Album C | 3.jpg |
+----+-----+---------+-------+
track_artist
+----+-----+-----------+
| ID | TID | ARTIST_ID |
+----+-----+-----------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
+----+-----+-----------+
artist
+----+--------+--------+
| ID | NAME | AVATAR |
+----+--------+--------+
| 1 | Taylor | 1.JPG |
| 2 | T-ara | 2.JPG |
| 3 | M2M | 3.JPG |
+----+--------+--------+
I want to get track.TITLE, album.TITLE and artist.NAME base on track.TID (where TID = $XXX) in 4 table above. It's easy when I use raw INNER JOIN MySQL code, but I want to use Eloquent ORM in Laravel. How I can do this ? Thank you.
I have analyzed :
One track belongsTo one Album
One track hasMany track_artist
One track_artist hasOne artist
One track hasMany artist

You can try this
The table should be like this, you need 4 tables
albums(id, title, cover)
artist(id, name, avatar)
tracks(id, album_id, title)
track_artists (id, artist_id, track_id) ManyToMany with tracks
Models
class Album extends Model{
function tracks(){
$this->hasMany(Track::class);
}
}
class Artist extends Model{
function tracks(){
$this->belongsToMany(Track::class, 'track_artists'); //here track_artists table is as pivot table
}
}
class Track extends Model{
function album(){
$this->belongsTo(Album::class);
}
function artist(){
$this->belongsToMany(Artist::class, 'track_artists'); //here track_artists table is as pivot table
}
}
Fetch Data
$track = Track::with('album','artists')->find($trackId);
echo $track->title." - ". $track->album->title;
//now print all artist of this track
foreach($track->artists as $artist){
echo $artist->name;
}
Note: You don't need to create model for track_artists because it is a pivot table for tracks and artists tables. You can insert and update in pivot table using Track or Artist laravel eloquent model. Though if you want you can create it extending Pivot.
Save Data in pivot table
$track = Track::find(1);
$artist = Artist::create(['name' => 'Katy Perry', 'avatar' => 'Katy_Perry.jpg']);
$track->artists()->save($artist); //this save track and artist relation in track_artists pivot table
For details https://laravel.com/docs/5.6/eloquent-relationships#many-to-many

First, your relations ( in my opinion ) need some changes.
One track belongs to one album and one album has many tracks (one to many)
One track has one artist and one artist has multiple tracks (one to many)
One album has multiple artist and one artist has multiple albums(many to many)
You will need a table for the many to many relation with the album id and the artist id
This is an example considering the above points
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class track extends Model{
function album(){
$this->belongsTo(album::class);
}
function artist(){
$this->belongsTo(artist::class);
}
}
class album extends Model{
function track(){
$this->hasMany(track::class);
}
function artist(){
$this->belongsToMany(artist::class, 'my_many_to_many_table');
}
}
class artist extends Model{
function track(){
$this->hasMany(track::class);
}
function album(){
$this->belongsToMany(album::class, 'my_many_to_many_table');
}
}

Related

Laravel Relationship - Additional fields in pivot table linking to additional models

I have simplified the problem to get to the point. I have three tables, users, roles, account.
Normally I would set up the User model to have a many to many relationships with roles but I want those roles to be specific to each account. So I have added an additional field to the pivot table. Here are the tables and fields that I have;
‘users’ table
|—————————
| id | name |
|—————————
| 1 | Bob |
| 2 | Jim |
| 3 | Fred |
|—————————
‘roles’ table
|—————————
| id | title |
|—————————
| 1 | Administrator |
| 2 | Manager |
| 3 | Approver |
|—————————
‘accounts’ table
|—————————
| id | name |
|—————————
| 1 | ABC Company |
| 2 | XYZ Shipping |
| 3 | KLM Transport |
|—————————
I then have the pivot table role_user with an additional pivot field for the account;
|—————————
| role_id | user_id | account_id
|—————————
| 1 | 3 | 1
| 2 | 2 | 1
| 3 | 2 | 3
| 3 | 1 | 2
|—————————
I have used the withPivot function on the belongsToMany function when setting up the many to many relationships. This allows me to get the information using $user->roles->pivot->account_id but what I need is to be able to get the name of that company. All it’s passing to the blade template is the id from the pivot table and not linking that to an actual Account model.
Is there a way with Eloquent to get this entire model in the same way as the original relationship?
Create a Custom Pivot Model
use Illuminate\Database\Eloquent\Relations\Pivot;
class RoleUserAccountPivot extends Pivot
{
public function user()
{
return $this->belongsTo(User::class);
}
public function role()
{
return $this->belongsTo(Role::class);
}
public function account()
{
return $this->belongsTo(Account::class);
}
}
Update your belongsToMany relationships
Bellow is an example with the User::roles relationship
class User //extends...
{
public function roles()
{
return $this->belongsToMany(Role::class, /*other parameters*/)->using(RoleUserAccountPivot::class)->withPivot('account_id');
}
}
Usage
$user->roles->first()->pivot->account // returns Account model
Hope it helps.
Reference links:
Laravel doc on custom pivots

Laravel ORM many to many relationship acts like one to many

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');
}
}

How to have multiple layers of hasMany() relation for Laravel 5?

Let say I have tables like the following:
order
order_id | customer_name | delivery_address
1 | David | ABC Road, DEF district
...........Some other record ..............
order_dish
order_id | dish_id | dish_name
1 | 1 | chicken wing
1 | 2 | meat pie
1 | 3 | onion ring
...........Some other record ..............
dish_ingredient
dish_id | ingredient
1 | chicken wing
1 | salt
1 | oil
2 | pork meat
2 | flour
3 | onion
...........Some other record ..............
In Laravel 5, if I want to get all dishes of an order, I can do:
$order = Order::hasMany('App\OrderDish', 'order_id')->get();
Is there a way I can get all ingredients needed for an order in one line?
Yes. The "has-many-through" relationship provides a convenient shortcut for accessing distant relations via an intermediate relation. You define it using hasManyThrough() method of your model.
First, define the relation in your Order model:
class Order extends Model {
public function ingredients() {
return $this->hasManyThrough('App\DishIngredient', 'App\OrderDish', 'order_id', 'dish_id');
}
}
With such a relation defined, you'll be able to get ingredients for given order with:
$ingredients = $order->ingredients;

Laravel Eloquent how to return one to many to many collection

Is it possible to get an eloquent relation based on a One->Many->Many relationship? In the below example, I would want to get all of the gallery_votes for a given user.
users
+----+---------------+
| id | user_name |
+----+---------------+
| 1 | bob |
| 2 | sam |
+----+---------------+
galleries
+----+---------------+-----------+
| id | gallery_name | user_id |
+----+---------------+-----------+
| 1 | Alaska Pics | 1 |
| 2 | Texas Pics | 1 |
| 3 | California | 2 |
| 4 | Cars | 2 |
+----+---------------+-----------+
gallery_votes
+----+---------------+--------+
| id | gallery_id | vote |
+----+---------------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 1 |
| 3 | 1 | 1 |
| 4 | 2 | 1 |
+----+---------------+--------+
The relationships are setup as follows.
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {{
...
public function galleries()
{
return $this->hasMany('App\Gallery');
}
}
and
class Gallery extends Model {
....
public function votes()
{
return $this->hasMany('App\GalleryVote');
}
}
$user->galleries returns a collection of associated galleries, so that works correctly. and $gallery->votes returns a collection of associate votes, so that also works correctly. But $user->galleries->votes gives
Undefined property: Illuminate\Database\Eloquent\Collection::$votes on line 1
Is what I'm trying to do even possible with straight Laravel? Or do I need to write the SQL directly?
The "has many through" relation provides a convenient short-cut for accessing distant relations via an intermediate relation.
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {{
...
public function galleries()
{
return $this->hasMany('App\Gallery');
}
public function votes()
{
return $this->hasManyThrough('App\GalleryVote', 'App\Gallery');
}
}
Now $user->votes will return all votes for that user. Remember you need to create the GalleryVote eloquent model as well.
You can read more about that type of relation, as well as some example usage in the documentation.

Laravel getting results from model belongsToMany relationship back to controller

I'm using Laravel 5 and Eloquent, I have 3 tables setup:
photos
+----+---------------+------------------+
| id | photo_name | photo_location |
+----+---------------+------------------+
| 1 | kittens.jpg | C:\kittens.jpg |
| 2 | puppies.jpg | C:\puppies.jpg |
| 3 | airplanes.jpg | C:\airplanes.jpg |
| 4 | trains.jpg | C:\trains.jpg |
+----+---------------+------------------+
photo_set (pivot table)
+------------+----------+
| set_id | photo_id |
+------------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
| 2 | 4 |
+------------+----------+
sets
+----+----------------+
| id | description |
+----+----------------+
| 1 | cute animals |
| 2 | transportation |
+----+----------------+
I created a belongsToMany relationship in my photos and sets models to link these two together.
class Photo extends Model {
public function sets()
{
return $this->belongsToMany('App\Set');
}
}
and
class Set extends Model {
public function photos()
{
return $this->belongsToMany('App\Photo');
}
}
However I'm trying to reference the $Sets->photos() model function in my controller, so that given a set of id=1, I can return the photo_location value for each row [C:\kittens.jpg,C:\puppies.jpg], but I don't know how to access it..
Also, I can "sort of" access this information in the view with:
#foreach($sets as $set)
{{$set->images}}
#endforeach
but it looks like it only iterates through once and returns a json (?) of the necessary information, though I'm not sure how to parse that to regular HTML either.
So in short, I'm having trouble accessing this data (photo_location) from both the controller and the view
Use Collection::lists()
$locations = Set::find(1)->photos->lists('photo_location');
It will return an array ['C:\kittens.jpg', 'C:\puppies.jpg']
http://laravel.com/api/4.2/Illuminate/Database/Eloquent/Collection.html#method_lists
In your controller try this :
$result = PhotoSet::where('set_id', $set_id)
->with('sets')
->with('photos')
->get();
$photo_location = $result->photos->photo_location;
here PhotoSet is the model for photo_set table, because it is a pivot table we will be able to access both sets and photos table from it.

Categories