Reputation: 2463
I have a table accounts:
act_id,
act_name,
act_address
And I have a table addresses:
add_id,
add_street1,
<other fields you'd expect in an address table>
accounts.act_address is a foreign key to addresses.add_id. In Laravel, I have my Account model:
use LaravelBook\Ardent\Ardent;
use Illuminate\Database\Eloquent\SoftDeletingTrait;
class Account extends Ardent
{
use SoftDeletingTrait;
protected $table = 'accounts';
protected $primaryKey = 'act_id';
public static $rules = array
(
'act_name' => 'required|unique:accounts'
);
protected $fillable = array
(
'act_name'
);
public function address()
{
return $this->hasOne('Address', 'add_id', 'act_address');
}
}
As you can see, I have my one-to-one relationship setup here. (Of course, the Address model has a 'belongsTo' as well). This all works.
The thing is, the address foreign key is nullable, as accounts don't require addresses. So, if I try to access the Account->address when it doesn't have one, I'll get a 'trying to access property of non-object' error.
What I'd like to do is set Account->address to a new Address object (all fields empty), if the account record doesn't have one set.
What I've been able to do is either create a second method in the model:
public function getAddress()
{
return empty($this->address) ? new Address() : $this->address;
}
Or, add it on the fly:
if (empty($account->address))
$account->address = new Address();
The first solution is really close, but I'd really like to keep the functionality of accessing address as a property instead of a method.
So, my question is:
How can I have Account->address return new Address() if Account->address is empty/null?
Oh, and I tried overriding the $attributes like so:
protected $attributes = array
(
'address' => new Address()
);
But that throws an error.
Upvotes: 2
Views: 10424
Reputation: 81157
Use accessor:
Edit: Since it is belongsTo
not hasOne
relation, it is a bit tricky - you can't associate a model to non-existing one, for the latter has no id:
public function getAddressAttribute()
{
if ( ! array_key_exists('address', $this->relations)) $this->load('address');
$address = ($this->getRelation('address')) ?: $this->getNewAddress();
return $address;
}
protected function getNewAddress()
{
$address = $this->address()->getRelated();
$this->setRelation('address', $address);
return $address;
}
However, now you need this:
$account->address->save();
$account->address()->associate($account->address);
which is not very convenient. You can alternatively save newly instantiated address in getNewAddress
method, or override Account save
method, to do the association automatically. Anyway for this relation I'm not sure if it makes sense to do it.. For hasOne
it would play nice.
Below is the way how it should look like for hasOne
relation:
public function getAddressAttribute()
{
if ( ! array_key_exists('address', $this->relations)) $this->load('address');
$address = ($this->getRelation('address')) ?: $this->getNewAddress();
return $address;
}
protected function getNewAddress()
{
$address = $this->address()->getRelated();
$this->associateNewAddress($address);
return $address;
}
protected function associateNewAddress($address)
{
$foreignKey = $this->address()->getPlainForeignKey();
$address->{$foreignKey} = $this->getKey();
$this->setRelation('address', $address);
}
You could do all this in single accessor, but this is the way it 'should' look like.
Upvotes: 5