My Village model;
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Village extends Model {
public function positions() {
return $this->belongsTo(Map::class, 'id', 'field_id');
}
}
My Map class migration;
Schema::create('map_data', function (Blueprint $table) {
$table->increments('id');
$table->integer('field_type');
$table->integer('field_id');
$table->string('x');
$table->string('y');
$table->timestamps();
});
My "villages" methon on "VillageController" class;
public function villages() {
$villages = Village::with([
'positions' => function ($query) {
$query->select('x', 'y');
}
])->get();
return $villages;
}
Result;
{
"villages": [
{
"id": 1,
"name": "village 1",
"created_at": "2016-10-26 18:36:34",
"updated_at": "2016-10-26 18:36:34",
"positions": null
},
{
"id": 2,
"name": "village 2",
"created_at": "2016-10-26 18:36:34",
"updated_at": "2016-10-26 18:36:34",
"positions": null
}
]
}
"Select" method only needs to be mentioned, but not the column returns NULL.
If I delete the $query->select('x', 'y'); code returns the following results.
{
"villages": [
{
"id": 1,
"name": "village 1",
"created_at": "2016-10-26 18:36:34",
"updated_at": "2016-10-26 18:36:34",
"positions": {
"id": 1,
"field_type": "1",
"field_id": "1",
"x": "21",
"y": "21",
"created_at": "2016-10-26 18:36:34",
"updated_at": "2016-10-26 18:36:34"
}
},
{
"id": 2,
"name": "village 2",
"created_at": "2016-10-26 18:36:34",
"updated_at": "2016-10-26 18:36:34",
"positions": {
"id": 2,
"field_type": "1",
"field_id": "2",
"x": "0",
"y": "0",
"created_at": "2016-10-26 18:36:34",
"updated_at": "2016-10-26 18:36:34"
}
}
]
}
But I use the $query->select('x'); code and the result should be the following
resource: https://stackoverflow.com/a/32185643/3287225
When you try to eagerly load positions relation for villages with
Village::with(['positions'])->get();
3 things happen:
Eloquent loads all villages
Eloquent loads all positions
Eloquent assigns positions to corresponding Village objects using the field_id column
In order for it to work, fetched positions need to have field_id column fetched, otherwise Eloquent is unable to match them with their corresponding Villages.
When you do
$query->select('x', 'y');
you're fetching only x and y columns from your positions table. field_id column is not fetched, that's why Eloquent is unable to fetch them with Village objects and that's why you're getting null in instead of a collection of positions.
Replace
$query->select('x', 'y');
with
$query->select('field_id', 'x', 'y');
to make your code work as expected.
Related
I am making an axios request to the address I specified below with Laravel.
I want the incoming data to come as follows.
I want to list the data with language en from the categories table and list the data matching (language) with the category_id in the pages table, but I was not successful. Where am I doing wrong?
http://localhost:3000/categories?lang=en
Mysql Table & Model
Mysql Table
SELECT * FROM `categories`
id
title
slug
language
Model
class Category extends Model
{
use HasFactory;
public function pages(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Page::class, 'category_id', 'id');
}
}
Mysql Table & Model
Mysql Table
SELECT * FROM `pages`
id
title
slug
content
category_id
language
Model
class Page extends Model
{
use HasFactory;
}
Routing
Route::prefix('categories')->group(function () {
Route::get('', [CategoryController::class, 'index']);
});
CategoryController
public function index()
{
return $category = Category::where('language', 'en')->with('pages')->get();
}
output
[
{
"id": 2,
"title": "corporate",
"slug": "corporate",
"seo": null,
"order": "0",
"status": "1",
"language": "en",
"created_at": "2022-04-04T13:31:27.000000Z",
"updated_at": "2022-04-04T13:31:27.000000Z",
"pages": null
},
{
"id": 4,
"title": "new category",
"slug": "new -category",
"seo": null,
"order": "0",
"status": "1",
"language": "en",
"created_at": "2022-04-08T12:23:57.000000Z",
"updated_at": "2022-04-08T12:23:57.000000Z",
"pages": null
}
]
what i want
[
{
"id": 2,
"title": "corporate",
"slug": "corporate",
"seo": null,
"order": "0",
"status": "1",
"language": "en",
"created_at": "2022-04-04T13:31:27.000000Z",
"updated_at": "2022-04-04T13:31:27.000000Z",
"pages": null,
"children": [
{
"id": "1",
"title": "hello word",
"slug": "hello-word",
"content": "",
"category_id": "2",
"language": "en"
}
]
},
{
"id": 4,
"title": "new category",
"slug": "new -category",
"seo": null,
"order": "0",
"status": "1",
"language": "en",
"created_at": "2022-04-08T12:23:57.000000Z",
"updated_at": "2022-04-08T12:23:57.000000Z",
"pages": null,
"children:": []
}
]
Assuming a page has one category, and a category has many pages, your relationship seems to be incorrect.
Change it to:
class Category extends Model
{
use HasFactory;
public function pages(): \Illuminate\Database\Eloquent\Relations\hasMany
{
return $this->hasMany(Page::class, 'category_id', 'id');
}
}
and in page model, add:
class Page extends Model
{
use HasFactory;
public function category(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(Category::class, 'id', 'category_id');
}
}
then you should be able to use ->with('pages') on your Category model
I have attempted to achieve to pull the user's info and the product details (Which specified the nickname and name) when the product name equal to "Oops" but I have no idea why the getProducts does not return any things.
User Model
public function getProducts(){
return $this->hasMany('App\Models\Product','users_id');
}
Product Model
public function users(){
return $this->belongsTo(User::class);
}
The code of pulling data:
$products = User::with(['getProducts' => function($query){
$query->select("users_id","name","nickname");
}])->get();
The current output:
"data": [
{
"id": 1,
"name": "John Smith",
"email": "john.smith#hotmail.com",
"email_verified_at": null,
"created_at": "2021-04-08T13:29:13.000000Z",
"updated_at": "2021-04-08T13:29:13.000000Z",
"role": 0,
"get_products": [
]
},
{
"id": 2,
"name": "Kelvin Ooi",
"email": "kelvin.ooi#hotmail.com",
"email_verified_at": null,
"created_at": "2021-04-08T13:29:13.000000Z",
"updated_at": "2021-04-13T12:07:11.000000Z",
"role": 1,
"get_products": [
{
"nickname":"MCD",
"name":"Oops"
},
{
"nickname":"Mary Brown",
"name":"Oops"
},
{
"nickname":"Kentucy",
"name":"KFC"
},
{
"nickname":"Texas Chicken",
"name":"TXS"
}
]
}
]
The expected output
"data": [
{
"id": 1,
"name": "John Smith",
"email": "john.smith#hotmail.com",
"email_verified_at": null,
"created_at": "2021-04-08T13:29:13.000000Z",
"updated_at": "2021-04-08T13:29:13.000000Z",
"role": 0,
"get_products": [
]
},
{
"id": 2,
"name": "Kelvin Ooi",
"email": "kelvin.ooi#hotmail.com",
"email_verified_at": null,
"created_at": "2021-04-08T13:29:13.000000Z",
"updated_at": "2021-04-13T12:07:11.000000Z",
"role": 1,
"get_products": [
{
"nickname":"MCD",
"name":"Oops"
},
{
"nickname":"Mary Brown",
"name":"Oops"
}
]
}
]
getProducts is not a good name for the relationship, lets simply call it products.
public function products()
{
return $this->hasMany('App\Models\Product','users_id');
}
Your code in the comment doesn't work because you specify the where clause in the main query, not the sub query.
return User::with(['products' => function ($query) {
$query->select("users_id","name","nickname");
}])
// This got to be in the sub query.
->where("products.name","Oops")
->get();
So let's update your code to this:
$productName = 'Oops';
return User::with(['products' => function ($query) use ($productName) {
$query->select("users_id","name","nickname", "price")
->where("name","LIKE", "%{$productName}%");
}])
->get();
I have seen your comment to this answer. Let's define a total custom attribute for the User model:
class User extends Model
{
protected $appends = ['total'];
public function getTotalAttribute()
{
// This is higher order message, if you haven't used it: https://laravel.com/docs/8.x/collections#higher-order-messages
return $this->products->sum->price;
}
}
Then the total attribute will be part of any user.
I am new in laravel in php. So it might be very silly mistake. I have song table and song categories table. I am trying to fetch all category with their respective songs. I have implemented larvel eloquent one to many relationship between song category and song.
Here is my code of fetching data:
public function getSongCategoriesWithSongs(){
$json_array = array();
$song_categories = SongCategory::all();
foreach ($song_categories as $item) {
# code...
$json = [];
$json['category'] = $item;
$json['songs'] = $item->songs;
array_push($json_array,$json);
}
return $json_array;
}
Here is response:
[{
"category": {
"id": 1,
"title": "Rock",
"created_at": "2020-12-20T02:58:32.000000Z",
"updated_at": "2020-12-20T02:58:32.000000Z",
"songs": [{
"id": 1,
"title": "Mere Mehboob",
"thumbnail": "https:\/\/static.toiimg.com\/photo\/msid-71407401\/71407401.jpg?108311",
"song_category_id": 1,
"stream_link": "https:\/\/2u039f-a.akamaihd.net\/downloads\/ringtones\/files\/mp3\/mere-mehboob-qayamat-hogi-52150.mp3",
"created_at": "2020-12-20T13:26:30.000000Z",
"updated_at": "2020-12-20T13:26:30.000000Z"
}, {
"id": 2,
"title": " Taaron Ke Shehar",
"thumbnail": "https:\/\/static.toiimg.com\/photo\/msid-71407401\/71407401.jpg?108311",
"song_category_id": 1,
"stream_link": "https:\/\/newmp3ringtones.net\/assets\/sass\/Ringtones\/TaaronKeSheharRingtoneByNehaKakkarJubinNautiyal2145436126.mp3",
"created_at": null,
"updated_at": null
}, {
"id": 3,
"title": "Bewafa Tera Masoom Chehra",
"thumbnail": "https:\/\/static.toiimg.com\/photo\/msid-71407401\/71407401.jpg?108311",
"song_category_id": 1,
"stream_link": "https:\/\/newmp3ringtones.net\/assets\/sass\/Ringtones\/BewafaTeraMasoomChehraRingtoneByJubinNautiyal352778308.mp3",
"created_at": null,
"updated_at": null
}]
}
}, {
"songs": [{
"id": 1,
"title": "Mere Mehboob",
"thumbnail": "https:\/\/static.toiimg.com\/photo\/msid-71407401\/71407401.jpg?108311",
"song_category_id": 1,
"stream_link": "https:\/\/2u039f-a.akamaihd.net\/downloads\/ringtones\/files\/mp3\/mere-mehboob-qayamat-hogi-52150.mp3",
"created_at": "2020-12-20T13:26:30.000000Z",
"updated_at": "2020-12-20T13:26:30.000000Z"
}, {
"id": 2,
"title": " Taaron Ke Shehar",
"thumbnail": "https:\/\/static.toiimg.com\/photo\/msid-71407401\/71407401.jpg?108311",
"song_category_id": 1,
"stream_link": "https:\/\/newmp3ringtones.net\/assets\/sass\/Ringtones\/TaaronKeSheharRingtoneByNehaKakkarJubinNautiyal2145436126.mp3",
"created_at": null,
"updated_at": null
}, {
"id": 3,
"title": "Bewafa Tera Masoom Chehra",
"thumbnail": "https:\/\/static.toiimg.com\/photo\/msid-71407401\/71407401.jpg?108311",
"song_category_id": 1,
"stream_link": "https:\/\/newmp3ringtones.net\/assets\/sass\/Ringtones\/BewafaTeraMasoomChehraRingtoneByJubinNautiyal352778308.mp3",
"created_at": null,
"updated_at": null
}]
}, {
"category": {
"id": 2,
"title": "Soft",
"created_at": null,
"updated_at": null,
"songs": []
}
}, {
"songs": []
}]
As you can see songs get repeated.
UPDATE
Solved using eager loading
public function getSongCategoriesWithSongs(){
return SongCategory::with('songs')->get();
}
But don't know why the foreach method not working.
Try this code
public function getSongCategoriesWithSongs(){
$json_array = array();
$song_categories = SongCategory::all();
foreach ($song_categories as $item) {
$json_array[] = ['category' => $item, 'songs' => $item->songs] ;
}
return $json_array;
}
The problem is that you assign the same relation twice.
Each SongCategory already has a collection of songs inside.
So in your foreach block, you assign a category with $json['category'] => $item which will load all related songs and pass them to the final JSON object. And you duplicate this by passing the next item $json['songs'] = $item->songs to the same array. Default Laravel behavior will be to fetch all related objects and transform them into JSON.
I would suggest you to use Laravel resources to return JSON objects with exact shapes: API Resources.
You can fix your code block without eager loading by removing $json['songs'] = $item->songs assignment.
Eager loading works because you passed all your objects only once.
Get query instead of replace foreign key by name in laravel.Get record replacement of menu_id to menu_name.Menu table contains id and menu_name.
"cart_items": [
{
"id": 1,
"cart_id": 1,
"menu_id": 5,
"quantity": "3",
"amount": "150",
"created_at": "2019-09-04 09:45:28",
"updated_at": "2019-09-04 09:45:28"
},
{
"id": 2,
"cart_id": 1,
"menu_id": 4,
"quantity": "3",
"amount": "150",
"created_at": "2019-09-04 09:54:32",
"updated_at": "2019-09-04 09:54:32"
}
]
use Laravel mutators to add menu_name for object, and add menu_id to $hidden =['manu_id']; in the model class
I use with method while fetching query.Its works
$data['cart_items'] = CartItem::with('menu')->where('cart_id',$cart->id)->get();
In my CartItem model i use eloquent orm function:
public function menu()
{
return $this->hasOne('App\Menu','id','menu_id')->select(['id','menu_name']);
}
It returns output:
"cart_items": [
{
"id": 1,
"cart_id": 1,
"menu_id": 5,
"quantity": "3",
"amount": "150",
"created_at": "2019-09-04 09:45:28",
"updated_at": "2019-09-04 09:45:28",
"menu": {
"id": 5,
"menu_name": "PODI IDLI"
}
},
{
"id": 2,
"cart_id": 1,
"menu_id": 4,
"quantity": "3",
"amount": "150",
"created_at": "2019-09-04 09:54:32",
"updated_at": "2019-09-04 09:54:32",
"menu": {
"id": 4,
"menu_name": "CORN PANEER MIX VEG SALAD WITH SAUSAGE"
}
}]
I am working on a system where I need many users can have many accounts and they can have multiple roles per account.
How can I set that up in eloquent.
I am currently having in User model:
public function accounts() {
return $this->belongsToMany('App\Models\Account');
}
public function roles() {
return $this->belongsToMany('App\Models\Account')->withPivot('role');
}
I have a pivot table user_account where the role is defined.
My problem is that when I go
$user = User::with('accounts')
->with('roles')
->where('id', $user['id'])
->first();
My output is:
{
"id": 1,
"name": "John Doe",
"created_at": "2018-03-11 20:46:46",
"updated_at": "2018-03-11 20:46:46",
"accounts": [
{
"id": 1,
"name": "Acme Inc",
"type": "BUSINESS",
"created_at": "2018-03-11 20:46:46",
"updated_at": "2018-03-11 20:46:46",
"pivot": {
"user_id": 1,
"account_id": 1
}
},
{
"id": 1,
"name": "Acme Inc",
"type": "BUSINESS",
"created_at": "2018-03-11 20:46:46",
"updated_at": "2018-03-11 20:46:46",
"pivot": {
"user_id": 1,
"account_id": 1
}
}
],
"roles": [
{
"id": 1,
"name": "Acme Inc",
"type": "BUSINESS",
"created_at": "2018-03-11 20:46:46",
"updated_at": "2018-03-11 20:46:46",
"pivot": {
"user_id": 1,
"account_id": 1,
"role": "SYSTEMADMINISTRATOR"
}
},
{
"id": 1,
"name": "Acme Inc",
"type": "BUSINESS",
"created_at": "2018-03-11 20:46:46",
"updated_at": "2018-03-11 20:46:46",
"pivot": {
"user_id": 1,
"account_id": 1,
"role": "USER"
}
}
]
}
What I want is for the roles to contain just the data from the pivot table with user_id, account_id and the role name. Any pointers? I would also like the accounts output to contain just one account.
Through extensive searching on the net I actually found a solution.
You need to make an AccountUser model and add the role to the account_user table.
In the User class you set up these relations:
public function roles() {
return $this->hasMany('\App\Models\AccountUser');
}
public function accounts() {
return $this->hasManyThrough('\App\Models\Account', '\App\Models\AccountUser', 'id', 'id');
}
And in the AccountUser class
public function user() {
return $this->belongsTo('App\Models\User');
}
public function account() {
return $this->belongsTo('App\Models\Account');
}
And in the Account class
public function accountUser() {
return $this->hasMany('\App\Models\AccountUser');
}
Now I can do
$user = User::with('roles')
->with('accounts')
->where('email', $data['email'])
->first();
And I get my expected output :)
Hope this helps someone...