Laravel 8 Multiple Table Relationship - php

This is a long post, sorry in advance.
I have a couple of questions regarding the diagram below.
How do I create a relationship for these tables? (tried "belongsToMany" and "hasMany")
Should I use Eloquent or Query Builder to get desired result? (image is attached below for desired result)
Is "client_order_items" a pivot table?
How can you distinguish a pivot table? Is it because the table has multiple
foreign keys?
How can I access "images" table from "Manufacturer" model? Can "hasOneThrough / hasManyThrough" achieve this?
Is the desired result achievable purely using Eloquent or DB Query?
//sample:
Manufacturers::find(2)->client_order_items->cars->images->car_image
I tried considering "client_order_items" as a pivot table then making relationships using "belongsToMany" in "Manufacturer", "Car" and "ClientOrder" models.
// App\Model\Manufacturer.php
public function client_orders() {
return $this->belongsToMany(ClientOrder::class,
"client_order_items", "manufacturer_id", "client_order_id")
->withPivot('id', 'quantity', 'car_id');;
}
public function cars() {
return $this->belongsToMany(Car::class,
"client_order_items", "manufacturer_id", "car_id");
}
// App\Model\Car.php
public function client_orders() {
return $this->belongsToMany(ClientOrder::class,
"client_order_items", "car_id", "client_order_id");
}
public function manufacturers() {
return $this->belongsToMany(Manufacturer::class,
"client_order_items", "car_id", "manufacturer_id");
}
// App\Model\ClientOrder.php
public function cars() {
return $this->belongsToMany(Manufacturer::class,
"client_order_items", "client_order_id", "car_id");
}
public function manufacturers() {
return $this->belongsToMany(Manufacturer::class,
"client_order_items", "client_order_id", "manufacturer_id");
}
I also tried "hasMany" relationship:
// App\Model\Manufacturer.php
public function client_order_items() {
return $this->hasMany(ClientOrderItems::class);
}
// App\Model\Cars.php
public function client_order_items() {
return $this->hasMany(ClientOrderItems::class);
}
// App\Model\ClientOrder.php
public function client_order_items() {
return $this->hasMany(ClientOrderItems::class);
}
// App\Model\ClientOrderItems.php
public function car() {
return $this->belongsTo(Car::class);
}
public function manufacturer() {
return $this->belongsTo(Manufacturer::class);
}
public function client_order() {
return $this->belongsTo(ClientOrder::class);
}
My goal is to get the result if i did something like this:
// Manufacturer::find(2)->client_orders
// Desired Result:
[
{
"client_order_id": 88062,
"first_name": "Clark",
"last_name": "Kent",
"order_items": [
{
"client_order_item_id": 37394,
"quantity": 1,
"car_id": 68,
"image": "path_img1"
}
]
},
{
"client_order_id": 60978,
"first_name": "Bruce",
"last_name": "Wayne",
"order_items": [
{
"client_order_item_id": 79913,
"quantity": 1,
"car_id": 68,
"image": "path_img1"
},
{
"client_order_item_id": 84743,
"quantity": 1,
"car_id": 95,
"image": "path_img2"
}
]
}
]
But the result I'm currently getting (with "belongsToMany") is:
// Manufacturer::find(2)->client_orders
// Current Result:
[
{
"id": 88062,
"first_name": "Clark",
"last_name": "Kent",
"pivot": {
"manufacturer_id": 2,
"client_order_id": 88062,
"id": 37394,
"quantity": 1,
"car_id": 68
}
},
{
"id": 60978,
"first_name": "Bruce",
"last_name": "Wayne",
"pivot": {
"manufacturer_id": 2,
"client_order_id": 60978,
"id": 79913,
"quantity": 1,
"car_id": 68
}
},
{
"id": 60978,
"first_name": "Bruce",
"last_name": "Wayne",
"pivot": {
"manufacturer_id": 2,
"client_order_id": 60978,
"id": 84743,
"quantity": 1,
"car_id": 95
}
}
]
Sorry again for the long post.
Thank you in advance.

After experimenting and reading for couple of hours, I was able to get the result I want.
Making the relationships:
Manufacturer model has "belongsToMany" relationship with "ClientOrder"
// App\Model\Manufacturer
public function client_orders() {
return $this->belongsToMany(ClientOrder::class, 'client_order_items', 'manufacturer_id', 'client_order_id');
}
"Car" and "Image" model has "hasMany" relationship. This will give us access to "Image" model from "ClientOrderItems" model.
// App\Models\Car
public function image() {
return $this->belongsTo(Image::class);
}
// App\Models\Image
public function cars() {
return $this->hasMany(Car::class);
}
Creating Accessors to Models
In "ClientOrder" model, create an accessor for the "order_items" key. This will fetch rows from "OrderLineItems" that are related to the "manufacturer_id" and "client_order_id".
// App\Models\ClientOrder
protected $appends = ["order_items"];
public function getOrderItemsAttribute() {
$items = ClientOrderItems::where("manufacturer_id", '=', $this->pivot->manufacturer_id)
->where("client_order_id","=",$this->pivot->client_order_id)
->select(["id as client_order_item_id","car_id","quantity"])
->get();
return $items;
}
In "ClientOrderItems" pivot model, create an accessor for the "image".
// App\Models\ClientOrderItems
protected $appends = ["image"];
public function getImageAttribute() {
return Car::find($this->car_id)->image->car_image;
}
The result of the 2 codes above looks something like this:
[
{
"client_order_item_id": 79913,
"quantity": 1,
"car_id": 68,
"image": "path_img1"
},
{
"client_order_item_id": 84743,
"quantity": 1,
"car_id": 95,
"image": "path_img2"
}
]
More on Accessors & Mutators:
https://laravel.com/docs/8.x/eloquent-mutators#accessors-and-mutators
Retrieving Records
To get the "client orders" that are related to manufacturer_id, use:
/// App\Http\Controllers\ManufacturerController;
public function index() {
$client_order = Manufacturer::find($manufacturer_id)->client_orders->unique("id")->values();
}
Above code will only fetch unique rows from "client_orders" table. Appending the related "order_items" for each row with the help of accessors.
[
{
"client_order_id": 88062,
"first_name": "Clark",
"last_name": "Kent",
"order_items": [
{
"client_order_item_id": 37394,
"quantity": 1,
"car_id": 68,
"image": "path_img1"
}
]
},
{
"client_order_id": 60978,
"first_name": "Bruce",
"last_name": "Wayne",
"order_items": [
{
"client_order_item_id": 79913,
"quantity": 1,
"car_id": 68,
"image": "path_img1"
},
{
"client_order_item_id": 84743,
"quantity": 1,
"car_id": 95,
"image": "path_img2"
}
]
}
]

Related

laravel eloquent ->with not working with one to one

now i have this method inside my Product model
public static function product_lines($request)
{
$keyword = Helper::clean_keyword($request['keyword']);
$data = Product::where
('number','like','%'.$keyword.'%')->
orWhere('name','like','%'.$keyword.'%')->
with('get_unit_relations')-> // this is one to many
limit(30)->
get();
return $data;
}
and this is the get_unit_relations
public function get_unit_relations()
{
return $this->hasMany('App\Models\Product_unit','product_id','id');
}
like this its working and its return data without any problem ..
{
"id": 1,
"number": "1",
"name": "test",
"foreign_name": null,
"get_unit_relations": [
{
"id": 3,
"unit_id": 2,
"product_id": 1,
"barcode": "455",
"smallest_unit_relation": 12,
"price": 12,
"cost": null,
"created_by": null,
"deleted_by": null,
"created_at": "2020-05-17T21:46:26.000000Z",
"updated_at": "2020-05-17T21:46:26.000000Z",
"deleted_at": null
}
]
}
but if i change the code like this ..
public static function product_lines($request)
{
$keyword = Helper::clean_keyword($request['keyword']);
$data = Product::where
('number','like','%'.$keyword.'%')->
orWhere('name','like','%'.$keyword.'%')->
with('get_smallest_unit_id')-> // this is one to one
limit(30)->
get();
return $data;
}
and this is the get_smallest_unit_id
public function get_smallest_unit_id()
{
return $this->hasOne('App\Models\Unit','id','smallest_unit_id');
}
im getting null in return like this ..
{
"id": 1,
"number": "1",
"name": "test",
"foreign_name": null,
"get_smallest_unit_id": null // it always return null in any one to one relaiton ..
}
how can i use ->with() method with one to one relation
thanks

How to remove pivot keyword from json using laravel?

I am trying to fetch data from games table which has pivot table user_games. Below code if works fine for me
$UserGames = User::with(['games' => function ($query){
$query->withPivot('highscore','level');
}])->find(request()->user()->id);
I am getting following json response
{
"data": [
{
"id": 2,
"name": "culpa",
"type_id": 3,
"created_at": "2018-10-30 11:23:27",
"updated_at": "2018-10-30 11:23:27",
"pivot": {
"user_id": 2,
"game_id": 2,
"highscore": 702,
"level": 3
}
}
]
}
But I wanted to remove pivot keyword from above json and pull pivot detail into root as like below my desire response
{
"data": [
{
"id": 2,
"name": "culpa",
"type_id": 3,
"created_at": "2018-10-30 11:23:27",
"updated_at": "2018-10-30 11:23:27",
"user_id": 2,
"highscore": 702,
"level": 3
}
]
}
Can someone kindly guide me how to fix the issue. I would appreciate. Thank you so much
You can utilise hidden and appends on the pivot model to re-structure the returned data.
class PivotModel extends model
{
protected $hidden = ['pivot'];
protected $appends = ['user_id'];
public function getUserIdAttribute()
{
return $this->pivot->user_id;
}
}
Reference for hidden
Reference for appends
You can convert the json into an array than reconvert it to json.
$UserGames = User::with(['games' => function ($query){
$query->withPivot('highscore','level');
}])->find(request()->user()->id);
$UserGames = json_decode($UserGames, true);
$pivot = $UserGames['data'][0]['pivot'];
unset($UserGames['data'][0]['pivot']);
$UserGames = json_encode(array_merge($UserGames[0], $pivot));
You can override the User model's jsonSerialize method which is called in the toJson method, this is the initial method body:
public function jsonSerialize()
{
return $this->toArray();
}
And you can do something like this:
public function jsonSerialize()
{
$attrs = $this->toArray();
if (isset($attrs['pivot'])) {
$attrs = array_merge($attrs, $attrs['pivot']);
unset($attrs['pivot']);
}
return $attrs;
}

Laravel Eloquent - how to join a table

I working on an API for my app.
I'm trying to pull items from the database and return them in JSON object,
my items table looks like this:
Items
-id
-name
-description
-price
-currency_id
-company_id
this is how I'm getting the items:
$rows = Company::where('guid',$guid)
->first()
->items()
->orderBy('name', $sort_order);
I want to replace the currency_id with a currency object that contains all the columns of currency table
so my result will be like this:
[
{
'id':'1',
'name':'name',
'description': 'example',
'price':'100',
'currency':{
'id':'1',
'name':'usd',
'symbol': '$'
}
}
]
update:
This is my currencies table:
id
name
symbol
code
Edit 2: The user's problem was more complex than this since there was pagination and search integration with the query. Helped with https://pastebin.com/ppRH3eyx
Edit : I've tested the code. So here.
In Company model
public function items()
{
return $this->hasMany(Item::class);
}
In Item model
public function currency()
{
return $this->belongsTo(Currency::class);
}
Controller logic
$items = Company::with(['items' => function($query) use ($sort_order) {
$query->with('currency')->orderBy('name', $sort_order);
}])
->where('guid', $guid)
->first()
->items;
Result with test data
[
{
"id": 2,
"name": "Toy",
"description": "Random text 2",
"price": 150,
"company_id": 1,
"currency_id": 1,
"currency": {
"id": 1,
"name": "usd",
"symbol": "$",
"code": "USD"
}
},
{
"id": 1,
"name": "Phone",
"description": "Random text",
"price": 100,
"company_id": 1,
"currency_id": 1,
"currency": {
"id": 1,
"name": "usd",
"symbol": "$",
"code": "USD"
}
}
]
Try this.
$rows = Company::with('items.currency')
->where('guid', $guid)
->first()
->items()
->orderBy('name', $sort_order);
Try below
Make one relationship in Item Model
public function currencies() {
return $this->hasMany('App\Currency');
}
then do below in your controller
$row=Items::All()->with('currencies');

Laravel 5.3 Eloquently Lazy/Eager load multiple models while selecting certain columns

Description:
Hello, in my scenario I'm trying to perform joins on 3 tables regarding a connections system I've built up. I'm able to use eloquent to connect these 3 tables together correctly and return the results no problem however, I'm trying to be more efficient in how it's done. I don't enjoy the fact that every query eloquent fires off defaults to a select * so I'd like to select specific columns instead. Here's where I'm currently at:
Connection Controller Code
Groups::with([
'list.userAccount.profile'
])->where('user_id_fk', $my->user_id)
->orderBy('group_num', 'ASC')
->take(3)
->get(['group_num','group_title','group_id']);
Group Settings Model
public function list() {
return $this->hasMany('App\UserConnections','group_id_fk');
}
User Connections Model
public function userAccount() {
return $this->hasOne('App\User','user_id_fk');
}
User Account Model
public function profile() {
return $this->hasOne('App\Profile','user_id_fk');
}
Now, If I make a call to it's assigned route to get users an example result is the following:
{
"conn_id_fk": 2,
"user_id_fk": 10,
"type": 0,
"group_id_fk": 4,
"status": 1,
"created_at": "2017-02-09 13:58:34",
"updated_at": "2017-02-09 13:58:34",
"user_account": {
"user_id": 10,
"email": "someemail#email.com",
"verified": 0,
"locked": 0,
"code": 0,
"request": "",
"created_at": "2017-02-09 13:20:51",
"updated_at": "2017-02-09 13:20:51",
"profile": {
"user_id_fk": 3,
"first_name": "Jillian",
"last_name": "Jacobs",
"avatar": "",
"age": 0,
"about_me": "",
"one_liner": "",
"inspire": "",
"created_at": "2017-02-09 13:18:32",
"updated_at": "2017-02-09 13:18:32"
}
}
Again My dilemma is that I don't want it to do select * to get all that data, it's unnecessary and will eventually slow down my server and cost me more money on I/O alone.
I've tried doing something like the following for example:
public function profileEnd() {
return $this->hasOne('App\Profile','user_id_fk');
}
public function profile() {
return $this->profileEnd->select('first_name','last_name','avatar');
}
///////////////////// AND I have tried///////////////////////////
public function profileEnd() {
return $this->hasOne('App\Profile','user_id_fk');
}
public function profile() {
return $this->profileEnd->select(['first_name','last_name','avatar'])->get(); <---Returns 500
}
which results in the following:
{
"conn_id_fk": 2,
"user_id_fk": 10,
"type": 0,
"group_id_fk": 4,
"status": 1,
"created_at": "2017-02-09 13:58:34",
"updated_at": "2017-02-09 13:58:34",
"user_account": {
"user_id": 10,
"email": "someemail#email.com",
"verified": 0,
"locked": 0,
"code": 0,
"request": "",
"created_at": "2017-02-09 13:20:51",
"updated_at": "2017-02-09 13:20:51",
"profile": null <------------------------------- Kills off the profile section
}
So What am I doing wrong here and how can I fix this? Any assistance is greatly appreciated.
Thanks in advance
Cheers!
Laravel needs the keys in order to match up the related collections of models. In your example, you did not select the key, so after the records are retrieved, Laravel has no idea which profiles are related to which user accounts. Add the key to the select and you should be good to go.
public function profileEnd() {
return $this->hasOne('App\Profile', 'user_id_fk');
}
public function profile() {
return $this->profileEnd->select('user_id_fk', 'first_name', 'last_name', 'avatar');
}

How to access a nested field in json with Laravel?

Hello I need to access to a nested field within a json file. I am using Laravel 5 and I have this in controller:
public function getLanguages($id){
$user=User::find($id);
if (!$user)
{
return response()->json(['errors'=>array(['code'=>404,'message'=>'No se encuentra un usuario con ese código.'])],404);
}
return Response::make(json_encode($user->languages), 200)->header('Content-Type', 'application/json');
}
This is the output:
[
{
"id": 1,
"name": "Español",
"pivot": {
"id_user": 1,
"id_language": 1
}
},
{
"id": 2,
"name": "Inglés",
"pivot": {
"id_user": 1,
"id_language": 2
}
},
{
"id": 3,
"name": "Alemán",
"pivot": {
"id_user": 1,
"id_language": 3
}
}
]
I want to access to the name of languages that a user has, how can I do that? This is a many to many relationship between User and Language, is better access to the languages trough the User model? or access from the pivot table?
Thanks.
Try This
$user = User::find($id)->languages->toArray();
$languages = array_column($user, 'name');
return Response::make(json_encode($languages), 200)->header('Content-Type', 'application/json');
Since $user->languages is an array containing the languages, you could use array_map, e.g.
function extract_name($el) {
return $el['name'];
}
$names = array_map('extract_name', $user->languages);

Categories