Groupby from relation laravel - laravel

I simply want to sum the column quantity_ton from actual_discharging model and groupby cargo_type from schedule_loading
data:
[{
"actual_discharging":{
"id": "2",
"quantity_ton": "44",
"actual_loading": {},
"schedule_loading": {
"id": "40",
"vessel_type": "001 - Spot Contract",
"destination": "002 - Export",
"cargo_type": "004 - Container"
}
}
}]
i've tried to use whereHas but it is not working
$data = $data->select(DB::raw("SUM(quantity_ton) as total"))->whereHas('schedule_loading',function($query){
$query->groupBy('cargo_type');
})->get();
also tried this code but it's also not working
this is the model and relationship
class ActualDischarging extends Model
{
use Compoships;
protected $table = "actual_discharging";
protected $fillable = [
'vessel','voyage_no','discharging_port','actual_arrival','actual_berthing','actual_discharging_from','actual_discharging_to','actual_departure','cement_type','packing','pack_kind','quantity_bag','quantity_ton','n_of_containers','shrinkage','shortage','overlanded','claim_rate','returning_port','actual_return_arrival'
];
public function schedule_loading(){
return $this->belongsTo('App\Model\ScheduleLoading',['vessel','voyage_no','cement_type','packing','pack_kind'],['vessel','voyage_no','cement_type','packing','pack_kind']);
}
}
how do i make the output display objects with the name of cargo_type and its sum?

Related

How to get data from laravel relationship in single object?

IN USER MODEL this is my relation
public function User() {
return $this->belongsTo('App\models\Users','UserId');
}
IN WALLET MODEL this is my relation
public function Wallet() {
return $this->HasOne('App\Models\Wallet','UserId','Id');
}
but when i am running the query
$user = Users::with([
'Wallet' => function($query){
$query->select('test_userwallet.UserId','test_userwallet.CoinBalance');
}
])->get()->toArray();
i am getting the data in a object like this
{
"Id": 1,
"UID": "8oDI617ZlsInXtUkRpMqVKo5J4XPzI12567",
"CountryCode": "91",
"Status": "active",
"TimeStamp": "2021-02-12 06:43:08",
"wallet": {
"UserId": 1,
"CoinBalance": 6
}
which is totally fine but i am guessing is there any way or method by which i can get the data in this format
{
"Id": 1,
"UID": "8oDI617ZlsInXtUkRpMqVKo5J4XPzI12567",
"CountryCode": "91",
"Status": "active",
"TimeStamp": "2021-02-12 06:43:08",
"UserId": 1,
"CoinBalance": 6
}
like in single object as i am working in apis so i want to do like that
note : only using query or eloquent
you can do this using join:
$user = Users::query()->leftJoin('test_userwallet','test_userwallet.UserId','users.id')
->select(['test_userwallet.Id','UID','CountryCode','Status','TimeStamp','UserId','CoinBalance'])
->get()->toArray();
You've specified only using query or eloquent.
I would say the best way would be to return your endpoint response as a Resource from your controllers.
But if you must do it as a property on the model. You can use the $appends array and an accessor. See docs This will add the properties to the model any time it is serialised like when it's returned in a response.
protected $appends = [
'coin_balance',
];
public function getCoinBalance Attribute()
{
return $this->Wallet->coin_balance;
}

Multiple Relationship Laravel - Object in Object

I want use Laravel Eloquent to do relationship, but I have a problem accessing a specific filtered object in the relationship.
My objects:
courses:
id - integer
name - string
contents:
id - integer
name - string
course_contents:
id - integer
course_id - integer
content_id - integer
I want get the contents by the course. Until now I can only filter the course_contents to filter contents
My controller:
Course::hasContents()->find($id);
Course Model
public function contents()
{
return $this->hasMany('App\CourseContent');
}
public function scopeHasContents($query)
{
$query->with(['contents' => function($contentQuery) {
$contentQuery->has('content')->with('content');
}]);
}
CourseContents Model:
public function content()
{
return $this->hasOne('App\Content', 'id');
}
My json return ( Course Find ) :
{
"id":1,
"name":"Course Example 1",
"contents":[
{
"id":1,
"course_id":1,
"content_id":1,
"deleted_at":null,
"created_at":"2019-07-16 17:31:05",
"updated_at":"2019-07-16 17:31:05",
"content":{
"id":1,
"name":"Content Example 1",
"deleted_at":null,
"created_at":"2019-07-16 17:31:05",
"updated_at":"2019-07-16 17:31:05"
}
},
{
"id":2,
"course_id":1,
"content_id":2,
"deleted_at":null,
"created_at":"2019-07-16 17:31:05",
"updated_at":"2019-07-16 17:31:05",
"content":{
"id":2,
"name":"Content Example 2",
"deleted_at":null,
"created_at":"2019-07-16 17:31:05",
"updated_at":"2019-07-16 17:31:05"
}
},{ ... }
],
}
What I need:
{
"id":1,
"name":"Course Example 1",
"contents":[
{
"id":1,
"name":"Content Example 1",
"deleted_at":null,
"created_at":"2019-07-16 17:31:05",
"updated_at":"2019-07-16 17:31:05"
},
{
"id":2,
"name":"Content Example 2",
"deleted_at":null,
"created_at":"2019-07-16 17:31:05",
"updated_at":"2019-07-16 17:31:05"
},{ ... }
],
}
First, you need to adjust the relationships a bit. You've many to many relationships so the models should look like:
Course.php
public function contents()
{
return $this->belongsToMany(Content::class, 'course_contents');
}
Content.php
protected $hidden = ['pivot'];
public function courses()
{
return $this->belongsToMany(Course::class, 'course_contents');
}
You can retrieve contents data as given below:
for instance: you want to get all contents for a course 1
Content::whereHas('courses', function($query) {
$query->where('courses.id', 1);
})->get();
// You need to pass course id dynamically but for demonstration, I hard coded it.
This will give you the following result:
array:1 [
0 => array:2 [
"id" => 1
"name" => "Content 1"
]
]
Use the belongsToMany relationship:
In your Course model:
public function contents()
{
return $this->belongsToMany(Contents::class, 'course_contents');
}
Then, use $course->contents;
This function returns all content models of the course.
Hope it helps.
The many-to-many relationship is defined by returning a belongsToMany relationship in the relationship method contents in the Course model. As stated in the Laravel many-to-many documentation.
To retrieve only the Content items in the many to many relationship and not the pivot columns, you should change the relationship instance from App\CourseContent to App\Content.
in content model
public function course()
{
return $this->hasMany('App\Course');
}
in course model
public function content()
{
return $this->hasMany('App\Content');
}
you can do
Course::with('content')->find($id)
or
Content::whereHas('course',function($q)use($id){
$q->where('id',$id)
})->get();

Laravel : Three table with relationship to get data

I have a 3 table in my app.products,category,attributes
product hasMany category and category hasMany attributes relationship.
I want to get all products with Category details and catgoey attributes detail in json format.How can i do this?
Currently i am trying with this function in my controller:
public function index()
{
$productDetails = Product::all();
if(!empty($productDetails)) {
foreach ($productDetails as $key => $value) {
//print_r($value->id."</br>");
}
}
}
Output which i want :
{
"productInformation": {
"id": 1,
"name": "productone",
"status": "Active",
"CategoryDetails": [
{
"id": 1,
"product_id": 1,
"categoryTitle": "categoryone",
"attribute" : [
{
"id": 1,
"product_id": 1,
"category_id": 1,
"title": "attrib-title-one",
},
{
"id": 2,
"product_id": 1,
"category_id": 1,
"title": "attrib-title-two",
},
]
}
]
}
}
relationship :
On categories table
$table->foreign('product_id')->references('id')->on('products');
On attributes table :
$table->foreign('product_id')->references('id')->on('products');
$table->foreign('category_id')->references('id')->on('categories');
How can i do this ?
Your Product Model
public function categories(){
return $this->hasMany('App\Category','product_id');
}
Category Model
public function attributes(){
return $this->hasMany('App\Attribute','cat_id');
}
In your Controller
public function index()
{
$productDetails = Product::with('catgories.attributes')->get();
if(!empty($productDetails)) {
$jsonData = json_encode($productDetails->toArray());
}
}
You can define your relationship inside models for different categories & attributes, need to define a hasMany relationship in products Model for Categories(named categories), then another hasMany in categories(named attributes) model for attributes. After that you can follow this process to prepare your data array
$productDetails = Product::all();
if(!empty($productDetails)) {
foreach ($productDetails as $key => $value) {
$productDetails[$key]['categorieDetails'] = $productDetails[$key]->categories;
foreach ($productDetails[$key]['categorieDetails'] as $key2 => $value2) {
$productDetails[$key]['categorieDetails'][$key2]['
attribute'] = $productDetails[$key]->categorieDetails[$key2]->attributes;
}
}
}
Then you can use json_encode for produce your json data
In Your Controller
public function index()
{
$productDetails = Product::with('catgories.attributes')->get();
return response()->json($productDetails);
}

Constraining a nested 3rd level relationship

I'm building an api using eager loading so i can simply return the user model with its deep relations and it automatically be converted as json. Here's the set up.
users
id
..
clients
id
..
user_clients
id
user_id
client_id
..
campaigns
id
..
client_campaigns
id
client_id
campaign_id
..
campaign_activities
id
campaign_id
..
client_campaign_activity_templates
id
campaign_activity_id
client_id *(templates are unique per client)*
..
I've setup the models' relationships.
User
public function clients() {
return $this->belongsToMany('App\Client','user_clients');
}
Client
public function campaigns() {
return $this->belongsToMany('App\Campaign','client_campaigns');
}
Campaign
public function activities() {
return $this->hasMany('App\CampaignActivity');
}
CampaignActivity
public function templates() {
return $this->hasMany('App\ClientCampaignActivityTemplate')
}
I have a simple api endpoint to provide a JSON of a User object including its deep relations using eager loading.
public function getLoggedInUser(Request $request) {
return \App\User::with('clients.campaigns.activities.templates')->find($request->user()->id);
}
Testing this using postman, I can get the user including its deep relations.
{
"user": {
"id": 1,
"name": "user1",
"clients": [
{
"id": 1,
"name": "client1",
"campaigns": [
{
"id": 1,
"name": "campaign1",
"activities": [
{
"id": 1,
"name": "activity1",
"templates": [
{
"id": 1,
"name": "template1 for client1",
"client_id": 1,
"body": "this is a template.",
}, {
"id": 2,
"name": "template1 for client2",
"client_id": 2,
"body": "This is a template for client2"
}
]
}, {
"id": 2,
"name": "activity2",
"templates": []
}, {
"id": 3,
"name": "activity3",
"templates": []
}
]
}
]
}
]
}
}
However, on the user->clients->campaigns->activities->templates level, it will list all the templates for that activity. I know based on the code of the relationships of the models above that it's supposed to behave like that.
So the question is How would you filter the templates to filter for both campaign_activity_id and client_id?
I've been experimenting on how to filter the templates so it will only list templates for that activity AND for that client as well. I have a working solution but it's N+1, I'd prefer eloquent approach if possible. I've been scouring with other questions, answers and comments for a closely similar problem, but I had no luck, hence I'm posting this one and seek for your thoughts. Thank you
I think what you need are eager loading constraints.
public function getLoggedInUser(Request $request) {
return \App\User::with('clients.campaigns.activities.templates',
function($query) use($request) {
$client_ids = Client::whereHas('users', function($q) use($request){
$q->where('id', $request->user()->id);
})->pluck('id');
$query->whereIn('templates.client_id', $client_ids);
})->find($request->user()->id);
}
Not tested but it should only require one additional query.
What I am doing is: define a constraint for your eager loading, namely only show those templates that have a client_id that is in the list (pluck) of Client IDs with a relation to the User.
Try using closures to filter through related models:
$users = App\User::with([
'clients' => function ($query) {
$query->where('id', $id);
},
'clients.campaigns' => function ($query) {
$query->where('id', $id);
}
])->get();
Here's my working solution, but I'm still interested if you guys have a better approach of doing this.
On the CampaignActivity model, I added a public property client_id and modified the relationship code to
CampaignActivity
public $client_id = 0
public function templates() {
return $this->hasMany('App\ClientCampaignActivityTemplate')->where('client_id', $this->client_id);
}
and on my controller, limit the eager loading to activities only (actually, there are more sqls executed using eager loading[9] in this case vs just iterating[7], and also eager loading doesn't make sense anymore because we're iterating lol)
public function getLoggedInUser(Request $request) {
foreach ($user->clients as $client)
foreach( $client->campaigns as $campaign)
foreach ($campaign->activities as $activity) {
$activity->client_id = $client->id;
$activity->templates; //to load the values
}
return $user;
}

Eager loading relationship returns empty using SELECT in WITH clause

Using Laravel 5.4, I have a query that correctly returns a relationship. Using the "with" clause in the query, I am attempting to return only selected columns from the relationship in my controller.
When I add the select to the with clause, relationship returns an empty array. Oddly enough, if I add a different parameter, such as a groupBy or join the query DOES return results. So something about my setup dislikes the select on the query.
Thus far I have tried:
using selectRaw
using select(DB::raw)
tried defining this as a separate relationship on my model.
Nothing has worked this far. Sql log looks good when I dump it.
Here is my model:
// MODEL
namespace App;
use Illuminate\Database\Eloquent\Model;
use DB;
class ClassBlocks extends Model
{
public $timestamps = false;
public function schedule(){
return $this->hasMany('App\ClassSchedules', 'class_block_id', 'id');
}
}
And here is my controller:
//CONTROLLER
use App;
use DateTime;
use Illuminate\Http\Request;
class ProgramsController extends Controller
{
public function filterClass(Request $request, App\ClassBlocks $block)
{
$block = $block->newQuery();
// Attempt to eager load relationship
// Returns results when "select" disabled
$block->with([
'schedule' => function($query){
$query->select('time_start');
$query->groupBy('day');
},
]);
return $block->get();
}
}
Here is a sample result with select enabled (schedule returns empty):
[
{
"id": 13,
"program_id": "1",
"class_group_id": "1",
"schedule": [
]
}
]
And here is a result with select disabled (returns relationship when select disabled):
[
{
"id": 13,
"program_id": "1",
"class_group_id": "1",
"schedule": [
{
"id": 338,
"class_group_id": "1",
"program_id": "1",
"class_block_id": "13",
"date": "06/13/2017",
"day": "Tuesday",
"instructor_id": "1",
"time_start": "6:30am",
"time_end": "6:30am"
},
{
"id": 339,
"class_group_id": "1",
"program_id": "1",
"class_block_id": "13",
"date": "06/14/2017",
"day": "Wednesday",
"instructor_id": "2",
"time_start": "6:30am",
"time_end": "6:30am"
}
]
},
]
Any insight would be greatly appreciated.
The problem here is:
$query->select('time_start');
Laravel need to have column that is connection between 2 records. In this case you should probably use:
$query->select('time_start', 'class_block_id');
to make it work.
Obviously you will have class_block_id in response this way. If you really don't want it, probably you should create some transformer that will return exactly you want in response.

Resources