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

Have you tried the "hasOne" method? It's exactly the "one to one" relationship

Last updated 2 years ago.
0

Can that be used with a pivot table?

Last updated 2 years ago.
0

Use accessor for that and ordinary belongsToMany (this will let you eager load the addresses for multiple users):

The easy way:

// User model

public function getAddressAttribute()
{
  return $this->addresses->first(); // not addresses()->first(); as it would run the query everytime
}

public function addresses()
{
  return $this->belongsToMany('Address');
}

// then you can do this:
$user=User::find($someId);
$user->address; // automatically fetches the relation and stores it in the 'relations' array

A bit longer way, that will load address as an relation:

public function getAddressAttribute()
{
  if ( ! array_key_exists('address', $this->relations)) $this->loadAddress();

  return $this->getRelation('address');
}

public function loadAddress()
{
  $this->setRelation('address', $this->addresses->first());
}

public function addresses()
{
  return $this->belongsToMany('Address');
}

// accessing just like before:
$user=User::first();
$user->address;

// but now also this will be true:
$user->getRelations();
array(
  'addresses' => collection,
  'address' => Address model
)
Last updated 2 years ago.
0

Nice, @jarektkaczyk, thanks I'll try that.

Btw your code contains $this->categories->first(), I suspect it's supposed to say "addresses" instead?

Last updated 2 years ago.
0

torkiljohnsen said:

Nice, @jarektkaczyk, thanks I'll try that.

Btw your code contains $this->categories->first(), I suspect it's supposed to say "addresses" instead?

Sure, just copy-pasted some of my code. edited

Last updated 2 years ago.
0

Argh. Not quite there yet it seems.

My User model:

public function address()
{
    if (!array_key_exists('address', $this->relations)) $this->loadAddress();

    return $this->getRelation('address');
}

public function loadAddress()
{
    // dd($this->addresses->first()); // outputs the first address perfectly
    $this->setRelation('address', $this->addresses->first()); // This seems to fail?
}

public function addresses()
{
    return $this->belongsToMany('My\Namespace\Addresses', 'user_address', 'address_id', 'user_id');
}

Then, in my User repository class, I have the User model injected and stored as $this->model:

$user = $this->model->find($id); // works just fine
$user->address; // Does not work:

I get the following error:

LogicException
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation

The error comes from laravel/framework/src/Illuminate/Database/Eloquent/Model.php:

	protected function getRelationshipFromMethod($key, $camelKey)
	{
		$relations = $this->$camelKey();

		if ( ! $relations instanceof Relation) {
			...

In my case, $camelKey === 'address', so Code that is called is $relations = $this->address();. Looks correct.

When I dd($relations), the Address model is output. I guess this is because $this->addresses->first() returns a model instead of an object of type Relation.

Last updated 2 years ago.
0

You have wrong order of the keys in belongsToMany, it should be:

return $this->belongsToMany('My\Namespace\Addresses', 'user_address', 'user_id', 'address_id');

This might be the issue.

Last updated 2 years ago.
0

You are right. I modified the code slightly before pasting it here and messed it up. In my live code it is reversed and correct, and the problem remains.

If I do this, the address is output correctly, so the connection definitely works:

public function loadAddress()
{
    // dd($this->addresses->first()); // outputs the first address perfectly

Perhaps your code is based on an older version of the framework…? Relations have to be of type Relation, can't return a model it seems.

Last updated 2 years ago.
0

Well, of course! The problem was address() while it should be accessor getAddressAttribute(). Fix in the marked answer made.

Last updated 2 years ago.
0

Of course :) Thanks for the help!

Last updated 2 years ago.
0

Apologies for reviving this thread, but I'm facing a similar problem, but in a different context. I'd appreciate any insight into my situation.

I have a number of many-to-many relationships, wherein one (and only one) relation is "preferred" and the others are "alternative". Data about whether or not a relation is preferred is currently stored as a boolean column on the pivot table.

For example, I have an Artwork model with an images() method that returns a BelongsToMany relation to the Image model. I'd like to add e.g. prefImage() and altImages() methods, which filter output of images() by preferred.

That part is easy using wherePivot(), but I'd like for prefImage() to return only one item when it's accessed as an attribute, e.g. $this->prefImage->id. By itself, that part is also easy if I define a getPrefImageAttribute() method.

However, I'd really like to define an prefImage() method that returns an instance of Relation (not Model) and returns only one item when accessed as an attribute. That part is proving to be hard.

I'd like to do this the "right" way by extending the BelongsToMany class, or even the Relation class. I think I can get away with the former, and overwrite the getResults() method to call first() if multiple results are returned.

But I don't want to reinvent the wheel. I'm sure there's a ton I'm overlooking in terms of what it would take to do this properly. Has anyone already done something like this? And what would you call this sort of relationship?

Last updated 6 years ago.
0

Quick update. I solved it using the approach I described above. I had to overwrite the newBelongsToMany method on our base model to return an instance of the new relationship, rather than Laravel's BelongsToMany. I'm certain that it's transparent to all relation methods that don't care about the new functionality.

Here's the code, if anyone's interested. I'm linking to the most relevant commit:

https://github.com/art-institute-of-chicago/data-aggregator/commit/c9b5d6c49441b1ae957afe9540716687e38000b3

Edit: Updated link to commit after rebase.

Last updated 6 years ago.
0

Sign in to participate in this thread!

Eventy

Your banner here too?

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.