Laravel - Put all records together? - php

I want to get every 'stat' value from every record in where government_type = 'higher_government' but at the moment my code only gets the stats instances for the first government role it finds, how can I put together all of the stats in some sort of array?
$higherGovernment = Cache::remember('government.higher_government', 1, function() {
return GovernmentRole::where('government_type', 'higher_government')->first()->stats;
});
Stats relationship:
public function government_role()
{
return $this->belongsTo('App\Database\Website\Roleplay\GovernmentRole', 'government_id');
}
Government relationship:1
public function stats(){
return $this->hasMany('App\Database\Website\User\Roleplay', 'government_id');
}

Use pluck()->toArray()
GovernmentRole::where('government_type', 'higher_government')->pluck('stats')->toArray();
This code will return an array of stats properties.

You should be able to use the pluck() method from Eloquent's Collection. https://laravel.com/docs/5.3/collections#method-pluck
Change your query to something like:
GovernmentRole::where('government_type', 'higher_government')
->get()
->pluck('stats', 'government_id');
This will give you a collection containing government_id => stats.
If you want an array rather than a collection, simply add ->toArray()

Related

How to sum up sub child in laravel and sort

How can I sort or order the result of my query based on the sum of third child of the parent. Please see details below.
What I tried
$childSumTableSorter = function($parents, $column) use ($dir) {
return ($dir == 'asc')
? $parents->get()->sortBy(function($query) {
return $query->item()->inventory->sum('total');
})
: $parents->get()->sortByDesc(function($query) {
return $query->item()->inventory->sum('total');
});
};
Current result
Undefined property: Illuminate\Database\Eloquent\Relations\HasMany::$inventory
Parent Table
public function item(){
return $this->hasMany(Item::class);
}
Child table
public function inventory() {
return $this->belongsTo(Inventory::class);
}
Inventory class
public function receive_item(){
return $this->hasOne(Item::class);
}
The inventories table has total column which I have to sum up and sort the parent based on the sum of the inventory.
Laravel version: 5.2
Error
The undefined property error is due to that item() returns relationship query, not the result of the relationship. You should perform the query and then get your desired property from retrieved results.
Solution
You can use nested orderBy like as below and perform summation in database layer as stated in orderByRaw method.
$childSumTableSorter = function($parents, $column) use ($dir) {
return ($dir == 'asc')
? Parent::with(['item'=>function($query){
$query->with(['inventory'=>function($query){
$query->orderByRaw('SUM(total) ASC');
}]);
}]);
: Parent::with(['item'=>function($query){
$query->with(['inventory'=>function($query){
$query->orderByRaw('SUM(total) DESC');
}]);
}]);
};
Optimization
As said in laravel doc for has many through relationship, your can query second relationship in a more straightforward way. In your case, you can define inventories method in Parent class.
Parent class
public function inventories(){
return $this->hasManyThrough(Inventory::class, Item::class);
}
By using this method, you can get inventories right from the parent class. Something like $parent->inventories->sum('total')

Laravel modal query

I am trying to get array of related model to my data and it returns null.
Code
public function collection()
{
return Product::with(['allBarcodes' => function ($query) {
$query->select('serial_number');
}])->get();
}
result
Also I tried pluck like $query->pluck('serial_number'); and result was
My real data
the data I suppose to receive is like
[{
"id":1,
"product_id":1,
"serial_number":"5245412185", // I only need this to be return as array
"sold":1,
"created_at":"2020-05-24T04:21:56.000000Z",
"updated_at":"2020-05-24T04:21:56.000000Z"
}]
Any idea?
When you are doing this $query->select('serial_number'); you are only selecting serial_number and not the column that connects both the modals i.e. product_id inside barcodes table.
Do this.
$query->select('product_id', 'serial_number');. However this will return 2 columns. If you want just one then you will have to use collection transform.
$products = $products->map(function ($product) {
$product->allBarcodes->transform(function ($q) {
return $q->serial_number;
});
return $product;
});
Keep me posted in the comments below.

Laravel - Get all records, not just first?

today I am facing a problem with Laravel. What I want to do is get every 'stat' value for every record where government_type = higher_government. By the way, I am different types, but this is just one of many.
$higherGovernment = Cache::remember('government.higher_government', 1, function() {
return GovernmentRole::where('government_type', 'higher_government')->first()->stats;
});
How can I get all the stats for all types instead of just calling ->first() ?
I have tried removing the ->first() but it doesn't do anything except give me this error:
Undefined property: Illuminate\Database\Eloquent\Builder::$stats
stats is a relationship by the way:
public function stats(){
return $this->hasMany('App\Database\Website\User\Roleplay', 'government_id');
}
Try This
\App\YourModelName::select('fields')->where('condition')->get();
If you only want one column from this table, you can use the select function to indicate wich ones you want.
Like so:
<?php
$higherGovernment = Cache::remember('government.higher_government', 1, function() {
return GovernmentRole::where('government_type', 'higher_government')->select('stats')->get();
});
The collection $higherGovernment will now contain GovernmentRole objects with one parameter, stats.
If you would like this to be an array of only the stats property, you can use collections to pluck this value out of it. Like so:
<?php
$higherGovernment = Cache::remember('government.higher_government', 1, function() {
return GovernmentRole::where('government_type', 'higher_government')->select('stats')->get()->pluck('stats');
});
Update:
Now that we know that stats is a relation I think the best solution is to just load all GovernmentRole objects and eager load the relation. Then you can display the stats for every GovernmentRole.
You can do it like this:
<?php
$higherGovernment = Cache::remember('government.higher_government', 1, function() {
return GovernmentRole::where('government_type', 'higher_government')->with('stats')->get();
});
// Just an example on how to get the stats
foreach ($g in $higherGovernment)
echo $g->stats->count();
What you could do is do a select right in the relationship, I think, like this:
public function stats(){
return $this->hasMany('App\Database\Website\User\Roleplay', 'government_id')->select('stats')->get();
}
This would in that case remove the relation, but you'd get the stats. Another way to do it is to just, straight away, get the stats from the relationship like this:
GovernmentRole::where('government_type', 'higher_government')->first()->stats->select('stats')->get();
or if that gives you an error try:
GovernmentRole::where('government_type', 'higher_government')->first()->stats()->select('stats')->get();

laravel eloquent sort by relationship

I have 3 models
User
Channel
Reply
model relations
user have belongsToMany('App\Channel');
channel have hasMany('App\Reply', 'channel_id', 'id')->oldest();
let's say i have 2 channels
- channel-1
- channel-2
channel-2 has latest replies than channel-1
now, i want to order the user's channel by its channel's current reply.
just like some chat application.
how can i order the user's channel just like this?
channel-2
channel-1
i already tried some codes. but nothing happen
// User Model
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved')
->with(['replies'])
->orderBy('replies.created_at'); // error
}
// also
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved')
->with(['replies' => function($qry) {
$qry->latest();
}]);
}
// but i did not get the expected result
EDIT
also, i tried this. yes i did get the expected result but it would not load all channel if there's no reply.
public function channels()
{
return $this->belongsToMany('App\Channel')
->withPivot('is_approved')
->join('replies', 'replies.channel_id', '=', 'channels.id')
->groupBy('replies.channel_id')
->orderBy('replies.created_at', 'ASC');
}
EDIT:
According to my knowledge, eager load with method run 2nd query. That's why you can't achieve what you want with eager loading with method.
I think use join method in combination with relationship method is the solution. The following solution is fully tested and work well.
// In User Model
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved');
}
public function sortedChannels($orderBy)
{
return $this->channels()
->join('replies', 'replies.channel_id', '=', 'channel.id')
->orderBy('replies.created_at', $orderBy)
->get();
}
Then you can call $user->sortedChannels('desc') to get the list of channels order by replies created_at attribute.
For condition like channels (which may or may not have replies), just use leftJoin method.
public function sortedChannels($orderBy)
{
return $this->channels()
->leftJoin('replies', 'channel.id', '=', 'replies.channel_id')
->orderBy('replies.created_at', $orderBy)
->get();
}
Edit:
If you want to add groupBy method to the query, you have to pay special attention to your orderBy clause. Because in Sql nature, Group By clause run first before Order By clause. See detail this problem at this stackoverflow question.
So if you add groupBy method, you have to use orderByRaw method and should be implemented like the following.
return $this->channels()
->leftJoin('replies', 'channels.id', '=', 'replies.channel_id')
->groupBy(['channels.id'])
->orderByRaw('max(replies.created_at) desc')
->get();
Inside your channel class you need to create this hasOne relation (you channel hasMany replies, but it hasOne latest reply):
public function latestReply()
{
return $this->hasOne(\App\Reply)->latest();
}
You can now get all channels ordered by latest reply like this:
Channel::with('latestReply')->get()->sortByDesc('latestReply.created_at');
To get all channels from the user ordered by latest reply you would need that method:
public function getChannelsOrderdByLatestReply()
{
return $this->channels()->with('latestReply')->get()->sortByDesc('latestReply.created_at');
}
where channels() is given by:
public function channels()
{
return $this->belongsToMany('App\Channel');
}
Firstly, you don't have to specify the name of the pivot table if you follow Laravel's naming convention so your code looks a bit cleaner:
public function channels()
{
return $this->belongsToMany('App\Channel') ...
Secondly, you'd have to call join explicitly to achieve the result in one query:
public function channels()
{
return $this->belongsToMany(Channel::class) // a bit more clean
->withPivot('is_approved')
->leftJoin('replies', 'replies.channel_id', '=', 'channels.id') // channels.id
->groupBy('replies.channel_id')
->orderBy('replies.created_at', 'desc');
}
If you have a hasOne() relationship, you can sort all the records by doing:
$results = Channel::with('reply')
->join('replies', 'channels.replay_id', '=', 'replies.id')
->orderBy('replies.created_at', 'desc')
->paginate(10);
This sorts all the channels records by the newest replies (assuming you have only one reply per channel.) This is not your case, but someone may be looking for something like this (as I was.)

Laravel Multiple Models Eloquent Relationships Setup?

I have 3 models
User
Pick
Schedule
I'm trying to do something like the following
$picksWhereGameStarted = User::find($user->id)
->picks()
->where('week', $currentWeek)
->first()
->schedule()
->where('gameTime', '<', Carbon::now())
->get();
This code only returns one array inside a collection. I want it to return more than 1 array if there is more than 1 result.
Can I substitute ->first() with something else that will allow me to to return more than 1 results.
If not how can I set up my models relationship to allow this to work.
My models are currently set up as follow.
User model
public function picks()
{
return $this->hasMany('App\Pick');
}
Schedule model
public function picks()
{
return $this->hasMany('App\Pick');
}
Pick model
public function user()
{
return $this->belongsTo('App\User');
}
public function schedule()
{
return $this->belongsTo('App\Schedule');
}
Since you already have a User model (you used it inside you find method as $user->id), you can just load its Pick relationship and load those Picks' Schedule as follows:
EDIT:
Assuming you have a schedules table and your picks table has a schedule_id column. Try this.
$user->load(['picks' => function ($q) use ($currentWeek) {
$q->join('schedules', 'picks.schedule_id', '=', 'schedules.id')
->where('schedules.gameTime', '<', Carbon::now()) // or Carbon::now()->format('Y-m-d'). See what works.
->where('picks.week', $currentWeek);
}])->load('picks.schedule');
EDIT: The code above should return the user's picks which have a schedules.gameTime < Carbon::now()
Try it and do a dump of the $user object to see the loaded relationships. That's the Eloquent way you want.
Tip: you may want to do $user->toArray() before you dump $user to see the data better.
EDIT:
The loaded picks will be in a form of Collections so you'll have to access it using a loop. Try the following:
foreach ($user->picks as $pick) {
echo $pick->schedule->gameTime;
}
If you only want the first pick from the user you can do: $user->picks->first()->schedule->gameTime
I think a foreach loop may be what you're looking for:
$picks = User::find($user->id)->picks()->where('week', $currentWeek);
foreach ($picks as $pick){
$pickWhereGameStarted = $pick->schedule()->where('gameTime', '<', Carbon::now())->get();
}
Try this and see if it's working for you

Categories