Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to join models in Laravel 5?

I have three tables, with a structure like this (they are in an MySQL DB) connected to my Laravel 5 API with Eloquent models:

# build_sets table
| id | title | description |

# parts table
| id | title | description | color |

# build_set_parts table
| id | build_set_id | part_id | amount | special_info |

At the moment I do a query like this:

$buildSets = array();

foreach(BuildSet::with('parts')->get() as $buildSet) {
    $temp = json_decode($buildSet);

    $temp->parts = $buildSet->parts;

    $buildSets[] = $temp;
}

return $buildSets;

And my models look like this:

class BuildSet extends Model
{
    protected $table = 'build_sets';
    protected $hidden = ['created_at', 'updated_at'];

    public function parts()
    {
        return $this->hasMany('App\Models\BuildSetPart');
    }
}

class Part extends Model
{
    protected $table = 'parts';
    protected $hidden = ['id', 'created_at', 'updated_at'];

    public function buildSets()
    {
        return $this->hasMany('App\Models\BuildSet');
    }
}

class BuildSetPart extends Model
{
    protected $table = 'build_set_parts';
    protected $hidden = ['id', 'build_set_id', 'part_id', 'created_at', 'updated_at'];

    public function buildSet()
    {
        return $this->belongsTo('App\Models\BuildSet');
    }

    public function part()
    {
        return $this->belongsTo('App\Models\Part');
    }
}

I get a result like this (JSON response):

[
  {
    "id": 1,
    "title": "Build set 1",
    "description": "This is a small build set.",
    "parts": [
      {
        "amount": 150,
        "special_info": ""
      },
      {
        "amount": 400,
        "special_info": "Extra strong"
      },
      {
        "amount": 25,
        "special_info": ""
      }
    ]
  },
  {
    "id": 2,
    "title": "Build set 2",
    "description": "This is a medium build set.",
    "parts": [
      {
        "amount": 150,
        "special_info": ""
      },
      {
        "amount": 400,
        "special_info": "Extra strong"
      },
      {
        "amount": 25,
        "special_info": ""
      },
      {
        "amount": 25,
        "special_info": ""
      },
      {
        "amount": 25,
        "special_info": ""
      }
    ]
  }
]

As you can see I'm missing the title, description and color in the "parts" array which is included in the build set. So my question is, how to I add the title and the color to my response? Can I do that by using Eloquent models, or do I have to make my own DB query where I get all build sets and then iterate them and get all the parts and build set parts and then merge that result and add it to the build set.

Any one got a good solution which will give me items in the parts array formatted like this:

[
  {
    "title": "Part 1",
    "color": "Red",
    "amount": 25,
    "special_info": ""
  },
  {
    "title": "Part 2",
    "color": "Green",
    "amount": 75,
    "special_info": ""
  }
]
like image 549
KungWaz Avatar asked Apr 10 '15 13:04

KungWaz


People also ask

What is hasMany in Laravel?

hasMany relationship in laravel is used to create the relation between two tables. hasMany means create the relation one to Many. For example if a article have comments and we wanted to get all comments of the article then we can use hasMany relationship .


3 Answers

From you model, You got Many to Many relationship between BuildSet and Part.

Therefore, In BuildSet, you can use hasManyThrough relationship.

Model BuildSet

public function parts()
{
    return $this->hasManyThrough('App\Models\Part', 'App\Models\BuildSetPart');
}

source: http://laravel.com/docs/5.0/eloquent#relationships

Anyway, if you want to keep the relationship between BuildSet and BuildSetPart.

I suggest you to use this instead.

public function buildSetPart()
{
   return $this->hasMany('App\Models\BuildSetPart');
}

note: right now, you got both parts() and buildSetPart() in BuildSet model

After you create the relationship, you can replace you code

$buildSets = array();

foreach(BuildSet::with('parts')->get() as $buildSet) {
    $temp = json_decode($buildSet);

    $temp->parts = $buildSet->parts;

    $buildSets[] = $temp;
}

return $buildSets;

with

return  BuildSet::with('parts')->get();

or, if you want the output in JSON format

return  BuildSet::with('parts')->toJson();

source: http://laravel.com/docs/5.0/eloquent#collections

like image 157
mininoz Avatar answered Sep 22 '22 09:09

mininoz


I found a solution that is almost as I wanted to do it, I tried mininoz solution and found out that I get some problems with the pivot table relation.

I now do this query:

return BuildSet::with('parts')->get();

And my models now looks like this, note the "->withPivot()" function I added at the end, it allows me to "access" the columns in my pivot table:

class BuildSet extends Model
{
    protected $table = 'build_sets';
    protected $hidden = ['created_at', 'updated_at'];

    public function parts()
    {
        return $this->belongsToMany('App\Models\BuildSetPart', 'build_set_parts')->withPivot('amount', 'special_info');
    }
}

class Part extends Model
{
    protected $table = 'parts';
    protected $hidden = ['id', 'created_at', 'updated_at'];

    public function buildSets()
    {
        return $this->belongsToMany('App\Models\BuildSet', 'build_set_parts')->withPivot('amount', 'special_info');
    }
}

class BuildSetPart extends Model
{
    protected $table = 'build_set_parts';
    protected $hidden = ['id', 'build_set_id', 'part_id', 'created_at', 'updated_at'];
}

I now get a extended parts response in my build set response (Note, I only show the parts data here, the build set data is as before with an "parts" array):

[
  {
    "title": "Part 1",
    "color": "Red",
    "pivot": {
      "build_set_id": 1,
      "part_id": 1,
      "amount": 25,
      "special_info": ""
    }
  },
  {
    "title": "Part 2",
    "color": "Green",
    "pivot": {
      "build_set_id": 1,
      "part_id": 2,
      "amount": 55,
      "special_info": ""
    }
  }
]

This response contains all I need, but I need to transform if I want to get it all on the same "level" in the JSON response hierarchy.

like image 27
KungWaz Avatar answered Sep 21 '22 09:09

KungWaz


foreach(BuildSet::with('parts')->get() as $buildSet) {

  $temp = json_decode($buildSet);

  $temp->parts = $buildSet->parts;

  $buildSets[] = $temp;

}

don't query in foreach

like image 30
Exchangetuts Avatar answered Sep 21 '22 09:09

Exchangetuts