ncarreno's avatar

How to get only one result from hasMany relationship

Hi everyone, In my ecommerce I have a Product Model I use to deal with the products of the shop. This ecommerce is multilingual and I also have a ProductLang Model to deal with the translations of all products in all languages. I have these relationships:

//Product Model
    public function lang()
    {
        return $this->hasMany('App\ProductLang');
    }
//ProductLang Model
    public function product()
    {
        return $this->belongsTo('App\Product');
    }

I have a controller where I get my products and pass the data todo the view

        $products=Product::where('stock', '>', 0)
                    ->where('active', '=', 1)
                    ->get();

In order to get the data I need from $products->lang I have to do a loop and then only use one of the result but I think this is not the best way to deal with it. I would like to know if there is any way to get the translation data I need when I get the products. How can I improve my code to get products and their proper translation in only one query? is it posible?

0 likes
5 replies
Snapey's avatar

you say you have many Lang records per product. how do you decide which is the proper one?

ncarreno's avatar

Yes, for example I have a Product A in my Products table and then I have the translation of the name and description of this products in english, spanish and french in my ProductsLang table so each product has three records in this table.

What I am doing is this but I think this might not be the best way:

                    <ul class='my-list-of-products'>
                        @foreach($products as $product)
                            @foreach($product->lang as $translation)
                                @if($translation->lang==App::getLocale())
                                <? 
                                    $name=$translation->name;
                                    $description=$translation->description;
                                ?>
                                @endif 
                            @endforeach
                        <li>
                            <p class="listitems-title">
                                <a href="#">{{ $name }}</a>
                            </p>    
                        </li>
                        @endforeach
                    </ul>
developeritsme's avatar
Level 38

@ncarreno Try with scope on App\ProductLang like...

public function scopeCurrent($query)
{
        return $query->whereLang(\App::getLocale())->first();
}

Then you can access it like $product->lang()->current() this will always return Model || null

The other solution is to set Accessor on App\Product like

public function getLanguage($lng = null)
{
    $lng = $lng ?: \App::getLocale();
        return $this->lang()->whereLang($lng)->first();
}

//Optional you can set
$appends = ['language'];

There is more options but you'll get the idea ;)

1 like
pmall's avatar

@developeritsme Scopes are expected to return a query builder, because the purpose of a scope is it can be chained with other scopes. So you should not call first() in a scope. You should do this instead for example :

# In your model
public function scopeCurrent($query)
{
        return $query->whereLang(\App::getLocale());
}

public function getCurrent()
{
    return $this->current()->first();
}
# In your controller
$current_lang = ProductLang::getCurrent();
1 like

Please or to participate in this conversation.