The rewritten title is: What causes a "possible infinite loop" when accessing another model attribute in an appended attribute of a model?
P粉786432579
2023-09-04 17:47:10
<p>My <strong>Laravel 9</strong> application has two models: the <code>brand</code> model and the <code>product</code> model. Each <code>product</code> belongs to one <code>brand</code>, and one <code>brand</code> belongs to multiple <code>products</code> (1: n relationship). The <code>product</code> model should provide a "calculated" (additional) property called <code>title_medium</code> that concatenates the brand title and product title upon request. </p>
<p>Once I try to access <code>$this->brand</code> in the product model's <code>getTitleMediumAttribute()</code> method, <code>xdebug</code> The <code>possibleInfiniteloop</code> exception will be thrown and execution will be canceled (after N iterations). I think this has something to do with relationships and load sequences (eager loading) but I can't find a solution so far. </p>
<h2>Brand Model</h2>
The <p><code>brand</code> model has an attribute <code>title</code> and has a number of <code>products</code> belonging to <code>brand</code>. </p>
<pre class="brush:php;toolbar:false;">namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Str;
class Brand extends Model
{
use HasFactory;
/*** Additional attributes for this model*/
protected $appends = [
'prices'
];
protected $fillable = [
'title'
];
/*** The "booted" method of the model.
*
* @return void*/
protected static function booted()
{
static::creating(function ($brand) {
$brand->slug = Str::slug($brand->title, '-', 'de');
});
}
/*** Returns all products for a brand
*
* @return HasMany*/
public function products(): HasMany
{
return $this->hasMany(Product::class);
}
}</pre>
<h2>Product model number</h2>
<p>Each <code>product</code> belongs to a <code>brand</code>. The additional attribute <code>title_medium</code> should concatenate the brand title and product title.</p>
<pre class="brush:php;toolbar:false;">namespace App\Models;
class Product extends Model
{
use HasFactory, Searchable, Filterable;
protected $fillable = [
'title',
'brand_id',
'image'
];
/*** Additional attributes for this model*/
protected $appends = [
'title_long',
'lowest_price',
'highest_discount_percent_price',
'latest_price_date',
'price_count'
];
/*** The "booted" method of the model.
*
* @return void*/
protected static function booted()
{
static::creating(function ($product) {
$product->slug = Str::slug($product->title_long, '-', 'de');
});
}
/*** Product belongs to one brand*/
public function brand(): BelongsTo
{
return $this->belongsTo(Brand::class);
}
/*** Get the combined title from product and brand*/
public function getTitleMediumAttribute(): string
{
// THIS CAUSES A "POSSIBLE INFINITE LOOP EXCEPTION" in xdebug
return $this->brand->title . ' ' . $this->title;
}
}</pre></p>
Try using attribute instead of getTitleMediumAttribute, like this and tell me if you still get the same error (using this method instead of `getTitleMediumAttribute):
Attribute
is\Illuminate\Database\Eloquent\Casts\Attribute