У меня есть модель PRODUCT, которая имеет отношения один ко многим с моделью REVIEW.
в таблице REVIEW есть столбец «скорость», и я хочу, чтобы средняя ставка из таблицы REVIEW была связана с данными PRODUCT, когда я извлекаю все продукты.
Я попытался создать метод в модели PRODUCT для вызова 'rate' в команде ::with(), чтобы сделать это, и это код:
class Product extends Model{
public function rate(){
return $this->hasMany('App\Review')->select('rate')->avg('rate');
}
в контроллере:
return Product::with('rate')->get();
но я продолжаю заканчивать с ошибками ..
так это можно сделать в модели или есть другой способ? Любая помощь приветствуется.






Вы можете попробовать что-то вроде этого:
class Product extends Model{
public function review(){
return $this->hasMany('App\Review');
}
public function rate(){
return $this->review()->avg('rate'); // chain your query here
}
}
то вы можете получить к нему доступ, например:
$product = Product::find(1);
dd($product->rate()->get());
Laravel использует множество магических методов. Вы можете подключиться к этому с чем-то вроде этого.
class Product extends Model{
public function Reviews(){
return $this->hasMany(Review::class);
}
public functions getRatingAttribute(){
return $this->Reviews->avg('rate');
}
}
то вы можете получить доступ вот так...
Product::find(1)->rating;
В большинстве случаев лучше, чтобы ваши методы, определяющие отношения, были как можно более простыми.
public function reviews(){
return $this->hasMany('App\Review');
}
Теперь вы можете использовать это соотношение для получения скорости. Вы можете сделать это разными способами:
1. Добавляем его как добавленное свойство:
Вы можете добавить свойство, которое не является столбцом в вашей модели, в качестве добавленного свойства.
protected $appends = ['rate'];
а затем есть функция для присвоения значения:
public functions getRateAttribute(){
return $this->reviews->avg('rate') ?? 0;
}
Проблема в том, что добавленное свойство, как следует из названия, всегда добавляется к экземпляру модели.
Итак, если вы просто сделаете следующее:
$product = Product::first();
Даже если вам не нужен рейт, у laravel все равно будет готов $product->rate для вас и из-за чего он будет делать средний запрос.
2. Имея это как метод:
Это вариант, когда вы можете просто создать его как метод:
public functions getRate(){
return $this->reviews->avg('rate') ?? 0;
}
Преимущество этого в том, что вы можете использовать это, когда вам нужно. Это избавляет от множества ненужных запросов, которые выполняет функция добавленного свойства, даже если она вам не нужна.
Затем это можно использовать:
$product = Product::first();
// The query fo average will be done below only when you call `getRate` on `$product`
$rate = $product->getRate();
Я бы сделал это как вариант 2, если бы у меня был выбор.
Также обратите внимание на Laravel avg('column_name):
null, когда нет данных0.0, если вы усредняете нечисловой столбец3.9265, если присутствует значение, превышающее 2 десятичных знака (в основном 4)Таким образом, вы можете иметь свою функцию соответственно.
Все вышеупомянутые решения приводят к проблеме N+1. Вы можете попробовать ниже, чтобы добиться быстрой загрузки
namespace App;
use DB;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class Product extends Model
{
public function reviews()
{
return $this->hasMany('\App\Review');
}
public function ScopeAverageRating(Builder $query){
return $query->withCount(['reviews as average_rating' => function ($query) {
$query->select(DB::raw("avg(rate)"));
}]);
}
}
Затем в вашем контроллере вы можете использовать App\Product::AverageRating(), он вернет вам коллекцию, которую можно повторить, чтобы получить Average_rating.
Добро пожаловать в StackOverflow! Пожалуйста, опубликуйте здесь любые связанные ошибки, чтобы мы могли продолжить проверку. Спасибо.