Have you tried the "hasOne" method? It's exactly the "one to one" relationship
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
)
Nice, @jarektkaczyk, thanks I'll try that.
Btw your code contains $this->categories->first()
, I suspect it's supposed to say "addresses" instead?
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
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.
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.
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.
Well, of course! The problem was address()
while it should be accessor getAddressAttribute()
. Fix in the marked answer made.
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?
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:
Edit: Updated link to commit after rebase.
Sign in to participate in this thread!
The Laravel portal for problem solving, knowledge sharing and community building.
The community