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);
Related
I have a newsletter table with this structure:
newsletter table:
id, title, title_color_id, background_color_id, description
And this table will have only one record.
I have a Nova resource to allow to create this initial record:
class Newsletter extends Resource{
public function fields(Request $request)
{
return [
ID::make(__('ID'), 'id')->sortable(),
BelongsTo::make('Bg Color', 'Color', \App\Nova\Color::class),
BelongsTo::make('Text Color', 'Color', \App\Nova\Color::class),
Text::make('Title')->sortable(),
Text::make('Description')->sortable(),
];
}
}
My doub is how to proprly set the relationships on the Newsletter model, because I have two fields that should have an id of a colors table record, but I cannot of course have 2 method colors on the model with those 2 different ccolumns like below. Do you know how to handle this scenario?
The Newsletter model:
class Newsletter extends Model
{
use HasFactory;
protected $table = 'newsletter';
public function color()
{
return $this->belongsTo(Color::class, 'title_color_id');
}
public function color()
{
return $this->belongsTo(Color::class, 'background_color_id');
}
}
This code will not work, you can not have 2 different methods in same class with same name.You need to rename at least one of them.
Relations should look like:
class Newsletter extends Model
{
use HasFactory;
protected $table = 'newsletter';
public function titleColor()
{
return $this->belongsTo(Color::class, 'title_color_id');
}
public function backgroundColor()
{
return $this->belongsTo(Color::class, 'background_color_id');
}
}
I have a scenario where User has a belongsToMany relation with PortalBreakdown, PortalBreakdown has a belongsTo relation with Portal. Portal has order column in it. I have a method listing_quota($id) in UserController which returns all breakdowns of the user. I want to sort these breakdowns based on order column of the portal. Below are the code of classes and a method I have tried.
class User extends Model {
protected $table = 'user';
public function listing_quota() {
return $this->belongsToMany('App\PortalBreakdown', 'user_listing_quota')->withPivot(['quota']);
}
}
class PortalBreakdown extends Model {
protected $table = 'portal_breakdown';
public function portal() {
return $this->belongsTo('App\Portal');
}
}
class Portal extends Model {
protected $table = "portal";
protected $fillable = ['name', 'description', 'order'];
}
Below is the method where I am trying to return sorted by order. I tried few things some of which can be seen in commented code but not working.
class UserController extends Controller {
public function listing_quota($id)
{
$user = User::with(['listing_quota' => function ($query) use ($id) {
// $query->sortBy(function ($query) {
// return $query->portal->order;
// });
}, 'listing_quota.portal:id,name,order'])->findOrFail($id);
// $user = User::with(['listing_quota.portal' => function ($q) {
// $q->select(['id', 'name',order']);
// $q->orderBy('order');
// }])->findOrFail($id);
return $this->success($user->listing_quota);
}
}
I also tried chaining orderBy directly after relation in Model class but that's also not working from me. Thank you in advance.
NOTE: I am using Laravel Framework Lumen (5.7.8) (Laravel Components 5.7.*)
I have two models in which I need to relate to, a Users model and a Prices model. In my Prices model there is a JSON object which holds an ID of a user and I was wondering if I could relate to my Prices table using the ID which is in the Prices model?
I know you could use an getAttribute and then return the user like that, but I was wondering if there is a $this->hasOne() method you could use?
e.g.
JSON
{user_id: 1, other_values:"in the object"}
Prices Model
class Prices extends Model {
/* Prices has the column 'object' which has the JSON object above */
protected $casts = ['object' => 'array'];
public function user(){
return $this->hasOne("App\User", $this->object->user_id, "id"); /* ! Example ! */
}
}
I created a package with JSON relationships: https://github.com/staudenmeir/eloquent-json-relations
Since the foreign key is in the Prices model, you should use a BelongsTo relationship:
class Prices extends Model {
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
protected $casts = ['object' => 'array'];
public function user() {
return $this->belongsTo(User::class, 'object->user_id');
}
}
class User extends Model {
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
public function prices() {
return $this->hasMany(Prices::class, 'object->user_id');
}
}
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");
}
}
So using Laravel 4, I have a Sales table that has a many to many relationship with a Products table, and it also has a one to many relation with a Customers table.
I set up my models as follows:
class Sale extends Eloquent {
...
public function products(){
return $this->belongsToMany('Product');
}
public function customers(){
return $this->belongsTo('Customer');
}
}
class Product extends Eloquent {
...
public function sales(){
return $this->belongsToMany('Sale');
}
}
class Customer extends Eloquent {
...
public function sales(){
return $this->hasMany('Sale');
}
}
What I want to do is return the data of all sales, including the data of each product included in each sale and the data of the customer that bought it.
In my SalesController I'm using eager loading to query my data like this:
public function index()
{
return Sale::with('products', 'customers')->get();
}
It returns an object with the Sale data, the Product data, but the Customer data is null.
How can I achieve this using Eloquent (or a custom query)?
EDIT
This is the object string it returns:
[{"id":1,"customer_id":1,"date":"2013-11-21","status":1,"created_at":"0000-00-00 00:00:00","updated_at":"0000-00-00 00:00:00","products":[{"id":1,"name":"Monitor","price":50,"status":1,"created_at":"0000-00-00 00:00:00","updated_at":"0000-00-00 00:00:00","pivot":{"sale_id":1,"product_id":1,"custom_price":25,"order":1}}],"customers":null}]
Try changing your customers relationship to singular:
class Sale extends Eloquent {
...
public function products(){
return $this->belongsToMany('Product');
}
public function customer(){ // <- here
return $this->belongsTo('Customer');
}
}
(Moved from comments to answer)