Hello Laracast community, I'm trying to refactor my Model Class into it's own Service Class to separate Logic from the Models and make the Controller easier to read and maintain. On top of that, I know that my controller sucks big time and needs a lot of improvement to make it more "eloquentish". No question about that.
I'm having this relation in my ItemModel (besides many other HasOne or BelongstTo relations on other tables):
public function price(): HasMany
{
return $this->hasMany(Price::class);
}
public function currentPrice(): HasOne
{
return $this->hasOne(Price::class)->ofMany([
'published_at' => 'max',
'id' => 'max',
], function(QueryBuilder $query) {
$query->where('published_at', '<', now());
});
}
Obviously there is a item(): BelongsTo method on the Price Model.
Now, I want to keep the price(): HasMany relation on the Item Model but want to move the currentPrice() method to the Service Class (or in other words: having that login as part of the eloquent query itself) and call that one method from the controller. I tried and failed miserably doing this:
class ItemService
{
public function getItems()
{
$data = Item::with([
'retailer' => function ($q) {
$q->where('active', 1);
},
'label',
'price' => function ($query) {
$query->where('published_at', 'max');
$query->where('id', 'max');
},
'weight'
])->paginate();
if ($data->count() <= 0) {
throw new \Exception('No items available');
}
return $data;
}
}
I also tried Item::query() with whereRelation() and many more without much luck.
You not gonna like what you see now: the controller. I by myself can't stand it, so please don't judge as I'm still learning. Ohh, and there's so much wrong with it like pagination, sorting, more complex search queries incl. filters etc. ... I know and am working on it.
private function getItems()
{
$data = Cache::remember('item', 0, function () {
$data = Item::with(['retailer', 'label', 'price', 'weight'])->paginate(4);
return $data->map(function ($item) {
return [
'id' => $item->id,
'name' => $item->article_name,
'info' => $item->short_description,
'image' => env('ASSET_URL') . '/images/articles/' . $item->cat_d_id . '/' . $item->article_image_url,
'weight' => ItemHelper::weightOrAmount($item->weight->last()->weight, $item->weight->last()->amount),
'price_weighted' => ItemHelper::getWeightedPrice($item->weightOrAmount->last()->weight, $item->weightOrAmount->last()->amount, $item->currentPrice->price),
'original_url' => $item->article_url,
'retailer_name' => $item->retailer->name,
'retailer_logo' => env('ASSET_URL') . '/images/logos/' . $item->retailer->logo,
'label_name' => $item->label->name,
'label_logo' => ($item->label->logo != NULL) ? env('ASSET_URL') . '/images/labels/' . $item->label->logo : env('ASSET_URL') . '/images/globals/null.png',
'current_price' => Number::currency($item->currentPrice->price),
'min_price' => $item->minPrice->price,
'max_price' => $item->maxPrice->price,
'upcoming_price' => $item->upcomingPrice->price,
'delivery_info' => ($item->delivery_date_info != NULL) ? __('global.available_until') . ' ' . new LangCountry()->dateNumbers(Carbon::parse($item->delivery_date_info)) : null,
];
});
});
$sorted_by_price = collect($data)->sortBy('current_price');
$data = $sorted_by_price->toArray();
return [
'data' => $data,
'pagination' => Item::with(['retailer', 'label', 'price', 'weight'])->paginate(4)
];
}
So, if anyone would like to give me a hand on how to refactor the currentPrice() method on the Item Model into the Item Service Class its "getItems()" method I would highly appreciate it.
Thanks,
Michael