2
votes

There are two tables

  • products
  • pictures ( has product_id , priority)

In my Product model I can get all pictures with:

public function pictures(){
    return $this->hasMany('App\Picture');
}

Now i want to create a method in product to get the main pictures url (lowest priority value with product_id of picture), and if there is no result return a default value. I tried:

public function mainpicture(){
    $picture = $this->pictures()->orderBy('priority','desc')->first();
    if($picture == null)
        return 'default/default.png';
    return $picture->url;
}

This gives the error:

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

When returning a value it's never of the type Illuminate\Database\Eloquent\Relations\Relation but just an url. How can I make a method to get the mainpicture or a default value?

The reason I want to create this function is: because I need it in alot of views and don't want to paste the same code in every view. Currently in all the views where I need the mainpicture I use:

@if(isset($product->pictures[0]))
    {!! Html::image('/images/product/'.$product->id.'/thumb/'.pictures[0]->url, 'Product image', array('class'=> ' img-responsive')) !!}
@else
    {!! Html::image('/images/product/default/default.png', 'No picture found', array('class'=> ' img-responsive')) !!}
@endif

This is very spaghetti like and a function would be much better.

I could create a global helper function, but im curious if i could add it to my model.

2

2 Answers

5
votes

I know you already have a working solution by Kota. But may I suggest an alternative approach:

In Laravel you can define custom logic for fetching attributes.

They are defined like this:

public function getCustomAttribute() 
{
     return 'foo';
}

So in your case:

public function getMainpictureAttribute()
{
    $picture = $this->pictures()->orderBy('priority')->first();
    return ($picture) ? $picture->url : 'default/path.jpg';
}

In your view you now can access it with $product->mainpicture

Now how clean is that? And your saving an additional query for each product.

-1
votes

I suggest you use Query Scope. For example:

class Product extends Model
{
    public function scopeMainpicture($query)
    {
        return $query->orderBy('priority','desc');
    }

}

Then in the controller, you can use this as:

$main_picture_url = $product->mainpicture()->first()->url;

//send it to the view
return ('myview').with('main_picture_url',$main_picture_url);

Then in the view, just do this:

{{!! $main_picture_url !!}}