Support the ongoing development of Laravel.io →
posted 9 years ago
Eloquent
Last updated 2 years ago.
0

Laravel relationships

class Item extends Eloquent  
{

  public function hotels()
    {
        return $this->hasMany('Bundle', 'parent_item_id', 'item_id');
    }
}

I'd generally use

public function hotels()
{
    return $this->hasMany('Hotel', 'foreign_key', 'local_key);
}

Having a Bundle model and referencing it as a hotel(s) is just calling for problems for you or someone else a few months down the road...

Last updated 2 years ago.
0

Thanks! The thing is that Hotels is a category_id of Items. So Items are bundled with other items...

But the challenge I am facing is that I cant use ->select() while doing ->hasMany().

Last updated 2 years ago.
0

Of course because you have to include the needed primary/foreign keys used by the relationship

Last updated 2 years ago.
0

I have batted my head against the same brick wall with a very similar legacy solution and have sympathy but I think this is a DB design issue. It does not conform to the standard relationships.

Last updated 2 years ago.
0

just to be clear....the ->hasMany() works fine...the issue i have is that the ->select(item_id, item_name) DOES NOT work.

What is happening right now is that i get a response like this...

{
"status": "OK",
"code": 200,
"data": {
        "item_id": 20,
        "category_id": 2,
        "name": "bla bla bla ",
	"description": " text text text picture text picutres",
        "created_at": ,
        "updated_at": ,
        "hotels": [
            {
        	"item_id": 21,
	       	"category_id": 2,
	        "name": "Hotel 1",
		"description": " text text text picture text picutres",
	        "created_at": ,
       		 "updated_at": ,
            },
            {
        	"item_id": 22,
	       	"category_id": 2,
	        "name": "Hotel 2 ",
		"description": " text text text picture text picutres",
	        "created_at": ,
       		 "updated_at": ,
            }
        ],
        "flights": [
            {
        	"item_id": 23,
	       	"category_id": 2,
	        "name": "YUL to JFK ",
		"description": " text text text picture text picutres",
	        "created_at": ,
       		 "updated_at": ,
            },
            {
        	"item_id": 24,
	       	"category_id": 2,
	        "name": "FRA to MOW ",
		"description": " text text text picture text picutres",
	        "created_at": ,
       		 "updated_at": ,
            }
        ]
}
}

but i want this...

{
"status": "OK",
"code": 200,
"data": {
        "item_id": 20,
        "category_id": 2,
        "name": "bla bla bla ",
	"description": " text text text picture text picutres",
        "created_at": ,
        "updated_at": ,
        "hotels": [
            {
        	"item_id": 21,
	        "name": "Hotel 1",
            },
            {
        	"item_id": 22,
	        "name": "Hotel 2 ",
            }
        ],
        "flights": [
            {
        	"item_id": 23,
	        "name": "YUL to JFK ",
            },
            {
        	"item_id": 24,
	        "name": "FRA to MOW ",
            }
        ]
}
}

as you can see in the second response, there is less data. I wanted to use ->select() but i get a empty array

Last updated 2 years ago.
0

You are going against MVC here. It seems like you are trying to decide what parts of the Model you want to display to the user at the model layer.

If I were implementing this I would take 1 of two solutions.

  1. Use a presenter object. Depending on how you are creating that response json you most likely can implement a toArray() method that spits out the correct format

Heres a trivial example.

public function show($id)
{
  $item = Item::find($id) //Probably fetched in another way
  return Response::json([
    'status': 'ok'
    'code': 200
    'data': with(new ItemPresenter($item))->toArray()
  ]);
}
  1. If your Model only ever concerns itself with the hotel and flights id and name, just overwrite Item::toArray(). Something like...
public function toArray()
{
  $attributes = $this->attributesToArray();
  $relationMapper = function($item) { return [
    'item_id': $item->id,
    'name': $item->name
  ]);

  return array_merge($attributes, [
    'hotels': $this->hotels->map($relationMapper)->toArray(),
    'flights': $this->flights->map($relationMapper)->toArray()
   ]);
}
Last updated 2 years ago.
0

As I mentioned earlier you should add all keys used in your select.

But your relationships methods are not ok, this is not a place to setup a join or a where clause. I fact i don't understand why you pour the join in your relationship method.

It looks like your items has many bundles that has many hotels, so define these relationships and then :

class Item extends Eloquent{

  public function bundles(){

    return $this->hasMany('Bundle', ... your keys here);

  }

}

class Bundle extends Eloquent{

  public function hotels(){

    return $this->hasMany('Hotel', ... your keys here);

  }

}

$item = Item::find($id);

foreach(item->bundles as $bundle){

  foreach($bundle->hotels as hotel){

    ...

  }

}
Last updated 2 years ago.
0
Solution

Thank you guys. I added the table structure and sample info for you to understand what this looks like.

Last updated 2 years ago.
0

There is still a problem with this. I do not consider this fixed. I ran into same issues that extjac had. for $this->hasMany('Model', 'foreign_key') -> indeed Eloquent is returning correct entities but if you try to apply a select condition it does not return anything.

Let's have an example:

  1. I have an album model
  2. I have an picture model
  3. an album can have multiple pictures and each picture have a name, size, extension and other info

When you try to get an album like http://domain.com/api/album/{album} -> you want also all pictures from that album like :

"id":"123", 
"name":"My Album", 
"pictures":[{
    "id":"1", 
    "name":"Pic1", 
    "size":"1000"
  },{
     "id":"2",
      "name":"Pic2",
      "size":"2000"
  }]

Above is the response when using:

$this->hasMany('App\Pictures', 'album_id');

But in this case i don't want size to be in the response and i don't want to fetch it as array and do extra work to remove my size.

For belongsTo condition we can use ->select('column1', 'columns2') option but when comes using it with hasMany, it simply does not work and as a result the returning data looks like this

"id":"123", 
"name":"My Album", 
"pictures":[]

Above is the response when using:

$this->hasMany('App\Pictures', 'album_id')->select('id', 'name');

Instead of :

"id":"123", 
"name":"My Album", 
"pictures":[{
    "id":"1", 
    "name":"Pic1"
  },{
     "id":"2",
      "name":"Pic2"
  }]
Last updated 9 years ago.
0

I don't know if this will help anyone, but I've just suffered with exactly the same issue, and managed to solve it with: http://stackoverflow.com/questions/30216763/laravel-many-to-many-unexpected-result-set-on-select/30218621#30218621 - Basically as it's already been stated, you must supply at least one selectable key that laravel can use to map the data together. See my answer on SO for the full details. Kept me thinking for a couple of hours! :)

0

Sign in to participate in this thread!

Eventy

Your banner here too?

extjac extjac Joined 14 Feb 2014

Moderators

We'd like to thank these amazing companies for supporting us

Your logo here?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2024 Laravel.io - All rights reserved.