In my case, I have two table like users table and rating table.
In user table, I'm storing user's personal details like name, email etc,
In rating table, I'm storing user_id and rating(rating will be in numbers like 1,2,3,4 and 5)
I have created relationship two tables
here is the relation
//User Model
public function ratings()
{
return $this->hasMany(Rating::class);
}
//Rating model
public function user()
{
return $this->belongsTo(Consultant::class);
}
I can able to display get data with eager loading
$data = User::with('ratings')->get();
The Response I'll get from eager load is
[
{
"id": 1,
"cunsultant_name": "Quincy Jerde",
"contact_number": "882-904-3379",
"ratings": [
{
"user_id": 1,
"rating_for_user": 3
},
{
"user_id": 1,
"rating_for_user": 5
},
{
"user_id": 2,
"rating_for_user": 3
}
]
},
{
"user_name": "Alene Dicki",
"contact_number": "247.604.8170",
"ratings": [
{
"id": 4,
"user_id": 3,
"rating_for_user": 3
}
]
}
]
So how can I get an average rating for every user with eager loading?
To get the average rating with eager loading you can do
$user->ratings()->avg('rating_for_user');
This will always append average_rating field in product. I use morph relation for ratings but you can use any relation appropriate for your situation.
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Product extends Model
{
protected $guarded = [];
protected $appends = ['average_rating'];
public function ratings()
{
return $this->morphMany(Rating::class, 'rateable');
}
public function getAverageRatingAttribute()
{
return $this->ratings()->average('value');
}
}
You can do it like this,
$data = User::with('ratings')
->join('Rating table','user.id','=','Rating table.user_id')
->select('user.*',DB::raw('avg(rating_for_user)'))
->get();
Modify the code as per your need.
I hope it help.
If you want to get ratings of multiple users you can do like this.
$users = User::where('type', 'instructor')->get();
foreach ($users as $user) {
$user['ratings'] = $user->ratings()->avg('rate');
}
return $users;
You can get avg rating like this,
$product=Products::where('id',$productid);
$data=$product->with('ratings')->get();
foreach($data as $d) {
return $d->ratings->avg('rating');
}
I have added code for product avg rating where two model like below:
Product Model:
<?php
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\URL;
class Products extends Model {
protected $table = "products";
public $timestamps = false;
public function ratings()
{
return $this->hasMany("App\Model\Reviews","p_id");
}
}
Review Model:
<?php
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
class Reviews extends Model
{
protected $table = "product_reviews";
public $timestamps = false;
//Rating model
public function products()
{
return $this->belongsTo("App\Model\Products");
}
}
Related
I have three tables:
collections which has id, name
genre_collection which has id, genre_id, collection_id
genres which has id, name
I want to retrieve data from collections with generes.
Collections Model
class Collections extends Model{
public function genres(){
return $this->hasMany('App\Models\GenreCollectionRelationships', 'genre_id' , 'id');
}
}
generic_collection
class GenreCollectionRelationships extends Model{
public function genre(){
return $this->hasOne('App\Models\Genres', 'id', 'genre_id');
}
}
Search Controller
class SearchController extends Controller{
$collection->genres;
foreach($collection->genres as $item){
$item->genre;
}
}
This code is working fine. And the output is
Actual
"genres": [{
"id": 1,
"genre_id": 1,
"collection_id": 1,
"created_at": "2019-02-07 17:13:36",
"updated_at": "2019-02-07 17:13:36",
"genre": {
"name": "Action",
"meta": null
}
}]
Is there any way i could directly get the output as shown below
Expected
"genres": [ {
"name": "Action",
"meta": null
}]
I tried hasManyThrough, belongsToMany but nothing worked out.
Note. I am on laravel 5.7
Thanks in advance.
You could build your own query to achieve what you are looking for. Try this:
$collection = Collection
::join('genres', 'genre.id', '=', 'collections.genre_id')
->select('collections.*', 'genres.name','genre.meta')
->get();
I find your code a bit hard to follow...
Let me try and see if I understood it correctly...
You basically have two models:
Model Collection saved in table collections
Model Genre saved in table genres
Since you have a many to many relationship between them, you need a third table to link the both of them together.
By naming convention, Laravel expects you to name it based on the two models, ordered alphabetically. So to create a link between collections and genres, you would need to create a table collection_genre which has a collection_id as a reference to the collections table, and likewise a genre_id to identify the linked genre.
You can then define your relationships as follows:
class Collection extends Model {
public function genres() {
$this->belongsToMany(\App\Models\Genre::class);
}
}
and
class Genre extends Model {
public function collections() {
$this->belongsToMany(\App\Models\Collection::class);
}
}
Now, I'm not sure what your controller looks like as the question has some invalid code to it, but I suspect you want to search the genres for a given collection.
Your code could like like this:
Class CollectionController extends Controller {
function getGenres(Collection $collection) {
return $collection->genres;
}
}
This would return the genres for the given collection.
If you want to format this, you could create an Eloquent Resource for this:
Class CollectionResource extends Resource {
public function toArray() {
return [
'name' => $this->name,
'meta' => $this->meta
];
}
}
In your controller you can then do:
Class CollectionController extends Controller {
function getGenres(Collection $collection) {
return CollectionResource::collection($collection->genres);
}
}
in your collection model
class Collections extends Model
{
protected $table='collections';
public $primaryKey='id';
protected $fillable = ['name'];
public function genres()
{
return $this->belongsToMany('App\Model\Genres','genre_collection','collection_id','genre_id')->withTimestamps();
}
}
in your genres model
class Genre extends Model {
protected $table='genres';
public $primaryKey='id';
protected $fillable = ['name'];
public function collections()
{
return $this->belongsToMany('App\Model\Collections','genre_collection','genre_id','collection_id')->get();
}
}
You are creating many to many relationship between collections and genre using genre_collection pivot table. In that case, belongsToMany is appropriate. And you don't need any model for genre_collection table.
Collections model
class Collections extends Model
{
public function genres(){
return $this->belongsToMany('App\Models\Genres', 'genre_collection', 'genre_id', 'collection_id');
}
}
Genres model
class Genres extends Model
{
public function collections(){
return $this->belongsToMany('App\Models\Collections', 'genre_collection', 'collection_id', 'genre_id');
}
}
SearchController
class SearchController extends Controller
{
foreach($collection->genres as $item){
$item->genre; // get genre info
}
}
I'm assuming that you want to access Generic directly from collection . If this is the case you can define a many-to-many relationship in collection model directly to generic model to access it . Please refer this : https://laravel.com/docs/5.7/eloquent-relationships#many-to-many . Sorry if I'm wrong
I am creating a Rest API project that I have an Artist that have a Musical Genre object as an attribute, and I created these two classes like this:
Artist:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Artist extends Model
{
public $table = 'artist';
public $timestamps = false;
protected $fillable = [
'name', '...'
];
//...
public function musical_genre() {
return $ this-> belongsTo (MusicalGenre::class, 'musical_genre');
}
}
Musical Genre:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class MusicalGenre extends Model
{
public $table = 'musical_genre';
public $timestamps = false;
protected $fillable = [
'name', '...'
];
// ...
function parent_genre(){
return $this->hasMany(MusicalGenre::class, 'parent_genre');
}
}
As you can see, Genre Musical have an auto relationship, because an Genre Musical can have an Parent Genre. This works nice, but the response I get is like this:
{
"artist": [
{
"id": 14,
"name": "Britney Spears",
"musical_genre": {
"id": 4,
"name": "electropop",
"parent_genre": 1
}
}
]
}
But I wish it could be like this:
{
"artist": [
{
"id": 14,
"name": "Britney Spears",
"musical_genre": {
"id": 4,
"name": "electropop",
"parent_genre": {
"id:" 1,
"name": "pop"
}
}
}
]
}
In controller I have this line to call Artist with Genre:
$artist= Artist::with(['...', 'musical_genre'])->get();
I already tried to call it with musical_genre.parent_genre but I got a empty parent.
There is a way to make the response like I wished in Laravel, with all atributes and not only the id? I didnt found how can I do this in Laravel docs.
EDIT:
I already tried using
$artist->load('parent_genre');
too.
EDIT 2:
My last try was in show() method inside the controller, like this:
public function show($id)
{
$artist = Artist::with(['login', 'musical_genre'])->findOrFail($id);
$musical_genre = MusicalGenre::findOrFail($artist->musical_genre);
$parent_genre = MusicalGenre::findOrFail($musical_genre->parent_genre);
if ($parent_genre){
$musical_genre->parent_genre = $parent_genre;
}
$artist->musical_genre = $musical_genre;
return response()->json(['artist' => $artist], 200);
}
And I got the same response, just with the "parent_genre": 1. I tried to get only $musical_genre->parent_genre and I got this body:
"musical_genre": {
"id": 4,
"name": "electropop",
"parent_genre": {
"id:" 1,
"name": "pop"
"parent_genre": null
}
}
like I wished. Maybe it's some Laravel limitation, not showing artist->musical_genre->parent_genre full body?
Add a $with attribute to the MusicalGenre model like so:
class MusicalGenre extends Model
{
public $table = 'musical_genre';
public $timestamps = false;
protected $with = ['parent_genre:id,name'];
.....
}
See: https://laravel.com/api/5.6/Illuminate/Database/Eloquent/Model.html#property_with
When you call the MusicalGenre model, it loads the parent_genre automatically. The 'id,name' in it limits the retrieved model to the two fields, if you want to to load all, just use only 'parent_genre' only.
Simply call it this way and you have it loaded with the response:
$artist= Artist::with('musical_genre')->get();
Please try this for nested eager loading:
$artist= Artist::with(['musical_genre'=> function ($query) {
$query->with(['parent_genre']);
}])->get();
My Message model has many MessageAttachment
Following code:
$message = Message::find(1);
return $message->attachments;
Outputs:
[
{
"id": 1,
"message_id": 1,
"attachable_id": 1,
"attachable_type": "Item",
},
{
"id": 2,
"message_id": 1,
"attachable_id": 1,
"attachable_type": "Photo",
}
]
Now, while this is all great, I'd like a way to fetch the model, the MessageAttachment is referring to (i.e. Photo, or Item). Something like:
return $message->attachments[0]->attachable; // outputs the Item 1 model
This is where I get stuck. How would you do that, in a clean and simple way, using above structure, or something similar?
this should work:
$messages = Message::find(1);
foreach($messages as $key => $value){
$model = $value->attachable;
$array[$key] = $model::find($value->attachable_id);
}
dd($array);
Simple. The solution:
class Message extends \Eloquent {
public function attachments() {
return $this->hasMany('MessageAttachment');
}
}
class MessageAttachment extends \Eloquent {
public function attachable() {
return $this->morphTo();
}
}
Doing:
$message = Message::find(1);
$message->attachments[0]->attachable; // returns the model
Best practice is to fetch the items directly with the realtions. You can do this like this:
Messages::with('attachments', 'attachments.photo')->get();
Here photo is a relation of attachments. All relations will be fetched. If you have a single message you can access the relation by:
$message->attachments->first()->photo
I have two tables:
product: id | name | category
productimage: id | imagename | productid
I want to take results in this form:
[{
"id":1,
"name":"abc",
"category":1,
"imagename":[
{
"id":1,
"imagename":abc.jpg
},
{
"id":2,
"imagename":abc1.jpg
},
{
"id":3,
"imagename":abc2.jpg
}]
}]
and I have two models:
call:
= product
= productimage
<?php
class Product extends \Eloquent {
//protected $fillable = [];
protected $table = 'product';
public function productimage(){
return $this->hasMany('productimage');
}
}
<?php
class Productimage extends \Eloquent {
//protected $fillable = [];
protected $table = 'productimage';
public function product(){
return $this->belongsTo('product');
}
}
This is taken directly from the Laravel documentation:
Define the relationship in the model, i assume that one product can only have one product image:
class Product extends Eloquent {
public function productimage()
{
return $this->hasOne('Productimage');
}
}
Get product image for the product with ID of 1:
$image = Product::find(1)->productimage;
I'm building a service API and would like to return a calculated date called 'expire_at' in my JSON response for 'subscriptions' relation below. The date will be calculated based on knowing 'activated_at', 'interval' and 'interval_type'. This can of course be calculated on the front-end, but I would like to provide this conveniently in my API response. I can't figure out where to put the logic though.
I could put it in the Plan model using the $appends property, but that would only work when requesting plans from a Customer. Putting it in the CustomerController would require looping through each subscription for each customer.
Where should I put the logic of calculating this date for each subscription in the customer result? Are above places my only two options?
Customer JSON result: (with 'expire_at' added)
{
id: 4327,
name: "Test Company",
created_at: "2014-05-29 21:12:37",
updated_at: "2014-05-29 21:12:37",
subscriptions: [
{
id: 93754,
name: "Test standard plan",
description: "An example subscription plan",
...
created_at: "2014-05-29 21:12:37",
updated_at: "2014-05-29 21:12:37",
activated_at: "2014-05-29 00:00:00",
active: true,
renewal_active: false,
// expire_at: "2014-06-28 00:00:00",
interval: 1,
interval_type_name: "Monthly"
}
]
}
Plan model:
class Plan extends Eloquent {
protected $guarded = array('id');
protected $hidden = array('pivot');
public function customers()
{
return $this->belongsToMany('Customer');
}
}
Customer model:
class Customer extends Eloquent {
protected $guarded = array('id');
public function users()
{
return $this->hasMany('User');
}
public function subscriptions()
{
return $this->belongsToMany('Plan')->withPivot('activated_at as activated_at', 'active as active', 'renewal_active as renewal_active');
}
}
Customer Controller
public function show($id)
{
$customer = Customer::with('subscriptions')->find($id);
return Response::json($customer);
}
You could store the logic to produce the expire_at attribute in the pivot table.
To achieve this you need to create the pivot model with the logic and than tell Laravel how to use it.
Pivot model:
class Subscription extends Eloquent
{
protected $table = 'customer_plan'; // point this to your pivot table
protected $appends = array('expires_at');
public function getExpiresAtAttribute()
{
return 'tomorrow';
}
public function customer()
{
return $this->belongsTo('Customer');
}
public function plan()
{
return $this->belongsTo('Plan');
}
}
Now we tell Laravel that Customer and Plan should use the Subscription model.
Customer model:
class Customer extends Eloquent
{
public function subscriptions()
{
return $this->hasMany('Subscription');
}
}
Plan model:
class Plan extends Eloquent
{
public function subscriptions()
{
return $this->hasMany('Subscription');
}
}
The query to fetch the data changes slightly:
$customer = Customer::with('subscriptions.plan')->find($id);
return Response::json($customer);