Why does hasOneThrough display inappropriate data? - php

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');
}

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.

Laravel 8 - How to add items on list with relationship

I have 3 relational tables Cart, CartProducts and Products and in the query I have the low JSON result, but I want to add the product name and description information in cart_products.
Relationships:
Cart with CartProduct
public function cartProducts()
{
return $this->hasMany(CartProduct::class);
}
CartProduct with Product:
public function products()
{
return $this->belongsTo(Product::class);
}
And the query:
$data = Cart::with(['cartProducts'])->where('user_id', '=', $id)->orderBy('id')->get();
[
{
"id": 1,
"user_id": 4,
"amount": 55,
"cart_products": [
{
"id": 1,
"cart_id": 1,
"product_id": 6,
"price": 7.8,
"quantity": 1
},
{
"id": 3,
"cart_id": 1,
"product_id": 5,
"price": 66,
"quantity": 5
}
]
}
]
and this is the result I want
[
{
"id": 1,
"user_id": 4,
"amount": 55,
"cart_products": [
{
"id": 1,
"cart_id": 1,
"product_id": 6,
"name": "Product a"
"price": 7.8,
"quantity": 1
},
{
"id": 3,
"cart_id": 1,
"product_id": 5,
"name": "Product b"
"price": 66,
"quantity": 5
}
}
]
Tables
Cart: id|user_id|amount
--------------------------------------------------
CartProduct: id|cart_id|product_id|price|quantity
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/QDSRb.png
You can simply add an accessor and append it to your CartProduct model.
class CartProduct
{
protected $appends=[
'product_name'
];
public function getProductNameAttribute()
{
return $this->product->name;
}
}

Select * from a pivot table in laravel

I have a many to many relation with table users,items and the pivot table user_item and i need to call the query : Select * from user_item where user_id=$user->id in laravel and return the results in json format. I try with
$user=User::find(session('user_id'))->items()->get();
return response ()->json($user);
But it doesn't work. How can I do that?
class User extends Authenticatable {
public function items (){
return $this->belongsToMany ("App\Models\Item", "user_item", "user", "item");
}
}
class Item extends Models {
public function users (){
return $this->belongsToMany ("App\Models\User", "user_item", "item", "user");
}
}
You can define columns in your pivote table using withpivot method
public function items (){
return $this->belongsToMany ("App\Models\Item", "user_item",
"user", "item")->withPivot(['column1', 'column2','another_column']);
}
to get relation instead of using get(), you should use like below:
$user=User::find(session('user_id'))->items;
return response ()->json($user);
above will give below json result:
[{
"id": 4,
"name": "PC",
"pivot": {
"column1": 1,
"column2": 4,
"another_column": "2016-03-03"
}
},
{
"id": 5,
"name": "Phone",
"pivot": {
"column1": 1,
"column2": 4,
"another_column": "2016-03-03"
}
}]
you can also include pivot in use user model using with():
$user=User::with('items')->find(session('user_id'));
return response ()->json($user);
give json result something like:
{
"id": 1,
"name": "User Name",
"email": "email#user.com",
"created_at": null,
"updated_at": null,
"items": [{
"id": 4,
"name": "PC",
"pivot": {
"column1": 1,
"column2": 4,
"another_column": "2016-03-03"
}
},
{
"id": 5,
"name": "Phone",
"pivot": {
"column1": 1,
"column2": 4,
"another_column": "2016-03-03"
}
}]
}]

How to sum model relation with id

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.

Laravel pass parameter to its distant relationship (translate children)

I have categories with id, parent_id, slug , pivot table category_language which columns are id,category_id,language_id,value
As you can see I can translate parent category, but can't send desired $lang_id to children translations, so each children having all translations
here is what I get:
{
"id": 1,
"parent_id": 0,
"slug": "personal-computers",
"created_at": "2019-12-27 15:05:31",
"updated_at": "2019-12-27 15:05:31",
"children": [
{
"id": 3,
"parent_id": 1,
"slug": "accessories-for-pc",
"created_at": "2019-12-27 15:05:32",
"updated_at": "2019-12-27 15:05:32",
"translations": [
{
"id": 1,
"code": "en",
"name": "English",
"pivot": {
"category_id": 3,
"language_id": 1,
"value": "Acc for PC",
"id": 7
}
},
{
"id": 2,
"code": "ru",
"name": "Русский",
"pivot": {
"category_id": 3,
"language_id": 2,
"value": "Аксессуары для ноутбуков и ПК",
"id": 8
}
},
{
"id": 3,
"code": "ro",
"name": "Romana",
"pivot": {
"category_id": 3,
"language_id": 3,
"value": "aksessuari-dlya-noutbukov-i-pk-ro",
"id": 9
}
}
]
}
],
"translations": [
{
"id": 1,
"code": "en",
"name": "English",
"pivot": {
"category_id": 1,
"language_id": 1,
"value": "PC",
"id": 1
}
}
]
}
Controller:
return Category::with('children')
->with(array('translations'=>function($query) use ($lang_id){
$query->where('language_id',$lang_id);
}))
->where('parent_id',0)->first();
Model
class Category extends Model
{ ..
public function translations()
{
return $this->belongsToMany('App\Models\Translation','category_language', 'category_id' ,'language_id' )->withPivot('value','id');
}
public function children()
{
return $this->hasMany( 'App\Models\Category' , 'parent_id' , 'id' )->with('translations');
}
}
you can add condition in children method
public function children()
{
return $this->hasMany( 'App\Models\Category' , 'parent_id' , 'id' )->with('translations')->where('language_id', 1);
}
Dry7 answer was close to the one I've implemented later, so I upvoted him.
Finally in model I've added: ...->where('language_id',helper_SetCorrectLangIdForQuery());
and function helper_SetCorrectLangIdForQuery is using global helper of Laravel request()->lang . If lang=enz, than it takes default language from another helper.

Categories