How to sum model relation with id - php

I'm new to Laravel 7. I faced the problem when summing the total and group by id. Actually I able to produce the sum value however I wish to keep other objects at the same time.
Model
class Invoices extends Eloquent {
public function payments()
{
return $this->hasMany('Payments');
}
}
class Payments extends Eloquent {
public function invoices()
{
return $this->belongsTo('Invoices');
}
}
Expected result
"items": [
{
"sale_id": 1,
"item_id": 1,
"code": "9789814820791",
"qty" :1,
"total": "20.00",
},
{
"sale_id": 1,
"item_id": 2,
"code": "9789814820790",
"qty" :1,
"total": "20.00",
},
"total_amount" : 40,
]
Code
$results = Invoices::with([
'payments' => function ($qry) {
$qry->select(['sale_id', app('db')->raw('sum(total) AS total_amount')])->groupBy('sale_id');
}
])
->get();
With the code above, I'm able to get sale_id and total_amount however the other result was missing. I did tried added $qry->select(['sale_id','item_id', 'code', 'qty' 'total']); but it does not work.

Related

Laravel is there a way to have relation belongsTo based on two columns?

I have the following tables and want to get all units (base unit, and sub units) of the product
Units
id
name
multiplier
base_unit_id
1
Piece
1
null
2
Dozen - 12
12
1
Products
id
name
cost
price
unit_id
1
product1
10
14
1
1
product2
10
14
1
Relationship inside Product Model
public function units()
{
return $this->belongsTo(Unit::class, 'unit_id', 'id'); // Or where base_unit_id = product unit id
// I have tried this
// $this->belongsTo(Unit::class, 'unit_id', 'id')->orWhere('base_unit_id', $this->unit_id)
// Does not work
}
I like to get the products like so
$products = Product::with('units')->get();
Expected output is
[
{
"id": 1,
"name": "Product 1",
"unit_id": 1,
"cost": 10,
"price": 14,
"units": [
{
"id": 1,
"name": "Piece",
"multiplier": 1,
"base_unit_id": null
},
{
"id": 1,
"name": "Dozen - 12",
"multiplier": 12,
"base_unit_id": 1
}
]
},
{
"id": 1,
"name": "Product 2",
"unit_id": 1,
"cost": 10,
"price": 14,
"units": [
{
"id": 1,
"name": "Piece",
"multiplier": 1,
"base_unit_id": null
},
{
"id": 1,
"name": "Dozen - 12",
"multiplier": 12,
"base_unit_id": 1
}
]
}
]
I want the units in one list how can I do this?
This is the solution that I came up with after looking for a while.
Product Model
public function baseUnit()
{
return $this->belongsTo(Unit::class, 'unit_id', 'id');
}
Unit Model
public function subUnits()
{
return $this->hasMany(Unit::class, 'base_unit_id', 'id');
}
And you can call it like
$product = Product::with(['baseUnit.subUnits'])->get();
Which gets you this output
[
{
"id": 1,
"name": "Product 1",
"unit_id": 1,
"cost": 10,
"price": 14,
"base_unit": {
"id": 1,
"name": "Piece",
"multiplier": 1,
"base_unit_id": null,
"sub_units": [
{
"id": 1,
"name": "Dozen - 12",
"multiplier": 12,
"base_unit_id": 1
}
]
}
}
]
And you can make a helper function inside the Product Model to format the units like this.
public function units()
{
$baseUnit = $this->baseUnit->replicate(); // Clone it so we don't modify the original
$baseUnit->id = $this->baseUnit->id; // Set the id to the original unit id
$result = collect();
$baseUnit->subUnits->each(function ($unit) use (&$result) {
$result->push($unit);
});
$baseUnit->offsetUnset('subUnits');
$result->push($baseUnit);
unset($baseUnit);
return $result;
}
Output
[
{
"id": 1,
"name": "Dozen - 12",
"multiplier": 12,
"base_unit_id": 1
},
{
"id": 1,
"name": "Piece",
"multiplier": 1,
"base_unit_id": null
}
]
Thanks to everyone who took time to answer me.
You will need to implement many-to-many relation between Product and Unit models via a pivot table
Basically it's like: Product record can have many Unit records
and Unit record can have many Product records - Many-to-Many
public function up()
{
Schema::create('product_unit', function(Blueprint $table) {
$table->foreignId('product_id')->constrained('products')->onDelete('cascade');
$table->foreignId('unit_id')->constrained('units')->onDelete('cascade');
$table->timestamps();
$table->primary(['product_id', 'uint_id']);
});
}
public function down()
{
Schema::dropIfExists('product_unit');
}
Next you need to define relationships
//Product model
public function units()
{
return $this->belongsToMany(Unit::class)->withTimestamps();
}
//Unit model
public function products()
{
return $this->belongsToMany(Product::class)->withTimestamps();
}
Laravel Docs - Eloquent Relationships - Many-to-Many
Laravel Docs - Eloquent Relationships - Updating Many-to-Many relationships
There's no easy way to do this.
My solution is using getAttribute:
public function getUnitsAttribute()
{
Unit::where('id', $this->unit_id)->orWhere('base_unit_id')->get();
}
But anyway, you can't use relation methods (like with() or load()) and you can't use it like a query builder.
Also, you can try this: https://stackoverflow.com/a/29775608/19262677.

Why does hasOneThrough display inappropriate data?

In this example, I get a valid array, but with a nested array that has inconsistencies
{ "order_id": 1, "status": 20,
"product_id": [
{ "id": 1, "order_id": 1, "product_id": 8, "quantity": 1, "price": 141 },
{ "id": 2, "order_id": 1, "product_id": 30, "quantity": 2, "price": 509 },
{ "id": 3, "order_id": 1, "product_id": 21, "quantity": 1, "price": 399 } ],
"name": { "id": 1, "name": "Product_1", ... "laravel_through_key": 1 }
since there are three products in the first order, in the second order I already get "name": "Product_4" i.e. the interval is correct, and the order is in the order of the prods table, not in the order_products table:
{ "order_id": 2, "status": 10,
"product_id": [
{ "id": 4, "order_id": 2, "product_id": 1, "quantity": 3, "price": 320 },
{ "id": 5, "order_id": 2, "product_id": 11, "quantity": 2, "price": 953 },
{ "id": 6, "order_id": 2, "product_id": 20, "quantity": 1, "price": 911 } ],
"name": { "id": 4, "name": "Product_4", ... "laravel_through_key": 2 }
How can I make the product names correspond to product_id in order_products?
here is my table diagram:
orders
id - integer
status - integer
order_products
id - integer
order_id - integer
product_id - integer
quantity - integer
price - integer
prods
id - integer
name - string
model Order
class Order extends Model
{
public function order_product()
{
return $this->hasMany('App\Order_product');
}
public function prod()
{
return $this->hasOneThrough('App\Prod', 'App\Order_product', 'order_id', 'id', 'id' );
}
model Prod
class Prod extends Model
{
public function order()
{
return $this->hasMany('App\Order', 'order_products');
}
модель Order_product
class Order_product extends Model
{
protected $fillable = ['order_id', 'product_id', 'quantity', 'price'];
}
Resource Order
class Order extends JsonResource
{
public function toArray($request)
{
return [
'order_id' => $this->id,
'status' => $this->status,
'product_id' => $this->order_product,
'name' => $this->prod,
];
}
controller Order
class OrderController extends Controller
{
public function index()
{
$orders = Order::with(['order_product', 'prod'])->get();
return OrderResource::collection($orders);
}
}
As per your DB design, it suppose to be belongsToMany between order, and product.
Order model
public function products()
{
return $this->belongsToMany('App\Prod', 'Order_product', 'order_id','product_id');
}
Product Model
public function orders()
{
return $this->belongsToMany('App\Order', 'Order_product','product_id', 'order_id');
}

Laravel get multiple tables

I'm trying to build something like product variants, so far so good. But there is one problem.
I'm getting product and variables as separate json
Example:
[
{
"id": 1,
"panelgameid": 71,
"name": "Counter-Strike 1.6",
"active": 1,
"minslots": 12,
"maxslots": 32,
"slotincreament": 2,
"order": 1,
"gameid": 1,
"location": "Serbia",
"price": 0.6
},
{
"id": 2,
"panelgameid": 71,
"name": "Counter-Strike 1.6",
"active": 1,
"minslots": 12,
"maxslots": 32,
"slotincreament": 2,
"order": 1,
"gameid": 1,
"location": "Germany",
"price": 0.4
}
]
And I have my games and my prices. But I want to show them like this.
[
{
"id": 1,
"panelgameid": 71,
"name": "Counter-Strike 1.6",
"active": 1,
"minslots": 12,
"maxslots": 32,
"slotincreament": 2,
"order": 1,
"gameid": 1,
"prices": [
{
"location": "USA",
"price": 0.6
},
"location": "Germany",
"price": 0.4
]
}
]
This is my current code as CONTROLLER:
$games = DB::table('games')
->join('prices', 'games.id', '=', 'prices.gameid')
->get();
return $games;
Create the Game and Price models by make:model artisan command:
php artisan make:model Game
php artisan make:model Price
You will find them at your project /app folder. There define the relationships.
Game Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Game extends Model
{
// define the relationship with the Price model, one game has many prices
public function prices()
{
return $this->hasMany(Price::class, 'gameid'); // you will have to make explicit the foreign key. Looking at the query in your question, I assume that it's gameid
}
}
Price Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Price extends Model
{
// define the relationship to the Game model, one price belongs to a Game
public function game()
{
return $this->belongsTo(Game::class, 'gameid');
}
}
Then you can query the games with the prices in your Controller:
$games = Game::with('prices')->get();
You can read more about Eloquent ORM in the docs: Eloquent: Getting Started, and Eloquent: One To Many Relationships.

how to get proper nested json format?

I want to display the result of join query in nested JSON format but it will not work I am getting wrong input. I used two tables as
category_type={category_type_id,category_type_name,category_icon};
main_category={main_category_id,category_type_id,category_name}
I want display nested JSON result but not getting plz need solution.
Controller:
$data= DB::table('table_main_category')
->join('table_category_type','table_main_category.category_type_id','=','table_category_type.category_type_id')
->select('table_category_type.*','table_main_category.*')
->get();
return Response::json(array(
'success' => '1',
'data' => $data),
200
);
json output:
{
"success": "1",
"data": [
{
"category_type_id": 2,
"category_type": "Sports",
"category_icon": "http://192.168.1.132:8000/images/category/game.svg",
"main_category_id": 1,
"category_name": "Popular Sports"
},
{
"category_type_id": 2,
"category_type": "Sports",
"category_icon": "http://192.168.1.132:8000/images/category/game.svg",
"main_category_id": 2,
"category_name": "Team Sports"
}
]
}
required json:
"success": "1",
"data": [{
"category_type_id": 2,
"category_type": "Sports",
"category_icon": "http://192.168.1.132:8000/images/category/game.svg",
"main_category": {
"main_category_id": 1,
"category_name": "Popular Sports"
}
},
{
"category_type_id": 2,
"category_type": "Sports",
"category_icon": "http://192.168.1.132:8000/images/category/game.svg",
"main_category": {
"main_category_id": 2,
"category_name": "Team Sports"
}
}
]
A workaround could be to update the data before you send the Response:
foreach ($data->toArray() as $elem) {
$elem['main_category'] = [];
$elem['main_category']['main_category_id'] = $elem['main_category_id'];
unset($elem['main_category_id']);
// ... and so on
}
use this code to restructure your data :
$data= DB::table('table_main_category')
->join('table_category_type','table_main_category.category_type_id','=','table_category_type.category_type_id')
->select('table_category_type.*','table_main_category.*')
->get();
return Response::json(array(
'success' => '1',
'data' => $data.map( i => {
const {main_category_id ,category_name , ...j } = i ;
return ({...j , main_category: {
main_category_id,
category_name }
});
}) ,
200
);
You can use Eloquent relationship. First create your Models.
use Illuminate\Database\Eloquent\Model;
class MainCategory extends Model
{
public $table = 'table_main_category';
}
class CategoryType extends Model
{
public $table = 'table_category_type';
public function mainCategory()
{
return $this->belongsTo('App\MainCategory', 'category_type_id', 'category_type_id');
}
}
Then in your controller you can query it like
$data = CategoryType::with('mainCategory')->get();
This will load category types with their corresponding main categories

Laravel fetch only pivot columns in many to many relationship

I have a User model that relates to a Section model through a pivot model UserTrainingSection. I have a pivot table that stores the foreign keys for both tables called section_user.
I have 2 additional pivot table columns called completed and completed_date.
The problem I am having is that when I fetch my data it returns all the columns from the User model along with the additional pivot columns.
class Section extends Model
{
public $table = "sections";
public $fillable = [
'id',
'name',
'description',
'parent',
'position',
'completion_percentage'
];
public function users()
{
return $this->belongsToMany(User::class, 'section_user')->using('App\Models\UserTrainingSection')->withPivot('completed', 'completed_date');
}
}
In my API service I fetch my data like this:
Section::with(['users' => function ($q) {
$q->where('users.id', Auth::user()->id);
}])->first();
How do I only return the pivot table columns and exclude the columns from the user table?
At the moment it returns something like this:
"sections": [
{
"id": 2,
"name": "Subsection 1 training",
"description": null,
"parent": 1,
"position": 2,
"completion_percentage": null,
"created_at": "2018-05-04 09:54:09",
"updated_at": "2018-05-11 09:14:59",
"users": [
{
"id": 1,
"name": "Test",
"email": "test#test.com",
"created_at": "12-04-2018 14:51:42",
"updated_at": "2018-04-19 14:14:36",
"pivot": {
"section_id": 2,
"user_id": 1,
"completed": 1,
"completed_date": "31/05/2018",
"expires": "31/05/2019"
},
}
]
}
]
What I would like to return is something like this:
"sections": [
{
"id": 2,
"name": "Subsection 1 training",
"description": null,
"parent": 1,
"position": 2,
"completion_percentage": null,
"created_at": "2018-05-04 09:54:09",
"updated_at": "2018-05-11 09:14:59",
"users": [
{
"pivot": {
"section_id": 2,
"user_id": 1,
"completed": 1,
"completed_date": "31/05/2018",
"expires": "31/05/2019"
}
}
]
}
]
So I basically get rid of the user data I don't want and only return the pivot data.
As per your expected output i guess you need data from junction model only, If that is the case i suggest you to define direct mapping of Section and UserTrainingSection
class Section extends Model
{
public function training_users()
{
return $this->hasMany('App\Models\UserTrainingSection', 'section_id');
}
public function users()
{
return $this->belongsToMany(User::class, 'section_user')->using('App\Models\UserTrainingSection')->withPivot('completed', 'completed_date');
}
}
In query you can simply do
Section::with('training_users')->first();
In your with statement you are fetching all User data by definition.
You can add a select statement to only get the results you want. You can add that either in the relationship in your model, or where you access the model in your API.
Example 1:
Section::with(['users' => function ($q) {
$q->where('users.id', Auth::user()->id)->select('completed', 'completed_date');
}])->first();
Example 2:
public function users()
{
return $this->belongsToMany(User::class, 'section_user')->using('App\Models\UserTrainingSection')->withPivot('completed', 'completed_date')->select('completed', 'completed_date');
}
(Not tested)
See the documentation

Categories