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.
Related
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.
I'm trying to pluck field from nested relations.
My class structure is:
User hasMany GroupUser
GroupUser belongsTo Group
Group belongsToMany Promotion
So this way I can get fine get Promotions from an User
$user->groupUsers()->with('group')->with('group.promotions')->get();
{
"id": 4,
....
"promotions": [
{
"id": 3,
"group_id": 11,
"user_id": 4,
...
"group": {
"id": 11,
...
"promotions": [
{
"id": 8,
"title": "Lavagem Mensal 1 unid.",
"const": "LAVAGEM_MENSAL1_UNID",
"pivot": {
"group_id": 11,
"promotion_id": 8
}
}
]
}
}
]
}
So what I need is to get a pluck list of Promotions.const.
Tried this way but it returns [null]
$user->groupUsers()->with('group')->with('group.promotions')->get()->pluck('group.promotions.const');
UPDATE: Now this way I close to achieve it.
$user->groupUsers()->with('group')->with('group.promotions')->get()->pluck('group.promotions.*.const');
"promotions": [
[
"BRF_LAVAGEM_MENSAL_1_UNID"
]
]
Just need to flat this array.
Try this:
$user->groupUsers()
->with('group.promotions')
->get()
->pluck('group.promotions.*.const')
->collapse();
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.
I have three tables:
products table (id, name)
ingredients table (id, ingredient_name)
products_ingredients table (product_id, ingredient_id)
I have a relation defined in Product
public function ingredients(){
return $this->hasMany('App\ProductIngredient','product_id','id');
}
When I am listing products using
$products = Product::where('category_id',$cat->id)->with('ingredients')->get();
I am getting this output
"products": [{
"id": 1,
"name": "Hesp",
"category_id": 1,
"ingredients": [{
"id": 41,
"product_id": 1,
"ingredient_id": 4,
},
{
"id": 42,
"product_id": 1,
"ingredient_id": 5,
}]
}]
Which is right.
What I want is to add the ingredient name in ingredients list like this
"products": [{
"id": 1,
"name": "Hesp",
"category_id": 1,
"ingredients": [{
"id": 41,
"product_id": 1,
"ingredient_id": 4,
"ingredient_name": "some name" // I want to add this field
},
{
"id": 42,
"product_id": 1,
"ingredient_id": 5,
"ingredient_name": "some name" // I want to add this field
}]
}]
I want to return this as JSON to use in API not in blade where I can call other relations.
How can I achieve this?
You have implemented the many-to-many relationship all wrong. Per the documentation:
Many-to-many relationships are defined by writing a method that returns the result of the belongsToMany method.
...
To define the inverse of a many-to-many relationship, you place another call to belongsToMany on your related model.
class Product extends Model
{
public function ingredients()
{
return $this->belongsToMany('App\Ingredient', 'products_ingredients');
}
}
class Ingredient extends Model
{
public function products()
{
return $this->belongsToMany('App\Product', 'products_ingredients');
}
}
Note the related model is first argument to belongsToMany(). It's not needed to create a model for a simple pivot table. Because you have misnamed the pivot table, it must be specified as the second argument to the belongsToMany(). The default name is singular model in alphabetic order, i.e. ingredient_product.
Your output should look something like this now, including the values from the ingredients table, not the pivot table.
"products": [{
"id": 1,
"name": "Hesp",
"category_id": 1,
"ingredients": [{
"id": 4,
"ingredient_name": "some name"
},
{
"id": 5,
"ingredient_name": "some name"
}]
}]
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