Laravel 5.3 convert query builder to LengthAwarePaginator - laravel

I need to use switch cases to add constraints to the query I need to run. I need it to work with pagination, I tried this:
$albums = Album::with(array(
'images' => function ($query) {
$query->orderBy('order', 'asc');
}
))
->where('votes.votable_type','App\Models\Album')
->groupBy('albums.id');
$albums->published()->orderBy('created_at', 'desc')->paginate(30);
dd($albums);
and I get
Builder {#361 ▼
#query: Builder {#350 ▶}
#model: Album {#351 ▶}
#eagerLoad: array:2 [▶]
#macros: array:5 [▶]
#onDelete: Closure {#364 ▶}
#passthru: array:11 [▶]
#scopes: array:1 [▶]
#removedScopes: []
}
If I run
$albums = Album::with(array(
'images' => function ($query) {
$query->orderBy('order', 'asc');
}
))
->where('votes.votable_type','App\Models\Album')
->groupBy('albums.id')
->published()->orderBy('created_at', 'desc')->paginate(30);
dd($albums);
I get
LengthAwarePaginator {#467 ▼
#total: 97
#lastPage: 4
#items: Collection {#872 ▶}
#perPage: 30
#currentPage: 1
#path: "http://images.dev"
#query: []
#fragment: null
#pageName: "page"
}
why is there difference between these two approaches? I need to use first approach to be able to add constraints using switch case, I can't do that using second approach. But with first approach I do not get LengthAwarePaginator, how to fix it so that I get that?

You should redeclare the variable $albums if you wish it to be saved to this variable. Change this:
$albums->published()->orderBy('created_at', 'desc')->paginate(30);
To:
$albums = $albums->published()->orderBy('created_at', 'desc')->paginate(30);

Related

UpdateorInsert method in dynamic column cominig from array

I have some misunderstanding with the UpdateOrInsert helper with the DB
DB::table('table')
->updateOrInsert( );
}
I have a dynamic collection structured like this:
^ Illuminate\Support\Collection {#1534 ▼
#items: array:9 [▼
"it-IT" => array:1 [▶]
"cs-CZ" => array:1 [▶]
"de-DE" => array:1 [▶]
"en-GB" => array:1 [▶]
"en-US" => array:1 [▶]
"es-ES" => array:1 [▶]
"fr-FR" => array:1 [▶]
"ko-KR" => array:1 [▶]
"sk-SK" => array:1 [▶]
]
I would like to use the it-IT array as reference and Update the other languages or Insert a new line if the it-IT string don't exist.
This Collection comes from an array, I transform it in collectio just for use the chunk(); function.
The problem is, after a lot of tries, using in the correct way the UpdateOrInsert.
Cause, I undestand that I need to separe the reference with the rest, so I split the collection in two collection:
$coll = collect($array);
$chunks = $coll->chunk(500);
$refcol = collect($refArr);
$refchunks = $refcol->chunk(500);
foreach ($chunks as $chunk)
{
DB::table('dictionaries')
->updateOrInsert($refchunks->toArray(),$chunk->toArray() );
}
But this is not enought cause when it start to upgrade the data inside the database i get a strange query error:
select exists(select * from `dictionaries` where ((`1` = stringa di riferimento ))) as `exists`
so I think I have to split the collection in all languages collection, but the number and the languages are not always the same.
How can I do a foreach inside the UpdateOrInsert?
I have tried olso to using the Model way
Dictionary Model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Dictionary extends Model
{
protected $primaryKey = null;
public $incrementing = false;
public $timestamps = false;
}
But when I perform the save() but if the it-IT string already exist it go in exception.
I tell to my self "Never give up" so I tried a thing like this:
try{
Dictionary::save();
} chatch (\Exception $e){
Dictionary::update();
}
But it save me the string like { 1: 'string in correct column'} and update method doesn't work.
Any way to help me?

merging collections grouped multiple times

I have a collection grouped (multiple times) like this:
Illuminate\Support\Collection {#1883 ▼
#items: array:1 [▼
57082 => Illuminate\Support\Collection {#1885 ▼
#items: array:1 [▼
"07-2021" => Illuminate\Database\Eloquent\Collection {#1863 ▼
#items: array:1 [▼
343 => Illuminate\Database\Eloquent\Collection {#1864 ▼
#items: array:1 [▶]
}
]
}
]
}
]
}
I have another collection that could have same values or not, and I need to merge these 2 collections with merged elements.
I tried with this, without success (I lose first "group"):
$fstCollection = $fstCollection->mapWithKeys(function ($items, $key) use ($sndCollection) {
return $sndCollection->get($key) ? $items->merge($sndCollection->get($key)) : $items;
});
I'm expecting your are loosing the key, by using mapWithKeys() wrong, in general that method is used, by returning an associative array, representing the key to map to and the value.
So changing the closure logic to the following, will keep the first group.
$collection = $sndCollection->get($key) ? $items->merge($sndCollection->get($key)) : $items;
return [$key => $collection];
Alternatively, just using map() will solve your problem, map() preserves the keys.
$fstCollection = $fstCollection->map(function ($items, $key) use ($sndCollection) {
return $sndCollection->get($key) ? $items->merge($sndCollection->get($key)) : $items;
});

Is there a way to only return the ID from this Laravel Query

I have run a query to get my specific needs but it's returning a whole bunch of array,
Is there a way to get only the specific column i needed?
This is what i am trying
$datas = DB::table('tableA')
->select('tableA.id')
->rightJoin('tableB','tableA.id','=','tableB.tableAt_id')
->where('tableB.active',1)
->whereIn('tableB.status',$myArray)
->get();
and the output is
Illuminate\Support\Collection {#1349 ▼
#items: array:221 [▼
0 => {#1327 ▼
+"id": 2020010201
}
1 => {#1346 ▼
+"id": 2020010202
}
2 => {#1343 ▼
+"id": 2020011501
}
I only want the ID. i tried toArray() but its the same
Use pluck.
Your code should be.
$datas = DB::table('tableA')
->rightJoin('tableB','tableA.id','=','tableB.tableAt_id')
->where('tableB.active',1)
->whereIn('tableB.status',$myArray)
->pluck('tableA.id');
Read More about Pluck Method

Laravel reducing relationship queries

I have two models. Expense and Method. I want to grab the sum of all expenses for each method type. I have four types:
- bank
- credit-card
- wallet
- savings
Each Expense belongsTo a Method and each Method hasMany Expenses. So it's a One-to-Many relation. So a method_id is stored for each Expense.
The following query works to get the sum of all expenses that have a method with the type bank for example:
$type = 'bank';
$expenses = auth()->user()->expenses()->with('method')->whereHas('method', function ($query) use ($type) {
$query->where('type', $type);
})->sum('amount');
But the thing is, if I want to get the total amount of all expenses for each method type, I will have to run too many queries. I would prefer just grabbing all Expenses, and then filtering through them to get the sum of all expenses for each method type.
The following for example doesn't work:
$expenses = auth()->user()->expenses()->with('method')->get();
$bank_balance = $expenses->filter(function ($expense)
{
$expense->whereHas('method', function ($query) {
$query->where('type', 'bank');
})->get(); // with or without ->get()
});
Any ideas how to get what I want by not using too many queries?
Edit:
I've unaccepted the chosen answer because it didn't give me what I needed in the end. I need to be able to do something like this in my view:
{{ $bank_balance }}
Which means, with xyz's answer, I wouldn't be able to do that, since I cannot distinguish between them. I only get results based on method_id's, but I need to be able to distinguish them by the method name.
DigitalDrifter's answer is the one that almost does the trick, but it gives me this:
Collection {#800 ▼
#items: array:3 [▼
"bank" => Collection {#797 ▼
#items: array:30 [▼
0 => array:2 [▼
"type" => "bank"
"amount" => 1536
]
1 => array:2 [▶]
2 => array:2 [▶]
3 => array:2 [▶]
4 => array:2 [▶]
5 => array:2 [▶]
]
}
"credit-card" => Collection {#798 ▶}
"wallet" => Collection {#799 ▶}
]
}
And I basically need something simple like this:
"bank" => "total sum here for all expenses that have a method of 'bank'"
"credit-card" => "total sum here for all expenses that have a method of 'credit-card'"
And so on..
I think this bit ->groupBy('type')->each->sum('amount') does the trick, but not completely. It does group by type but it does not give a sum for each type, as you can see in the collection example above.
You can group by the method id and then sum the result in a select statement.
The following is untested.
$expenses = auth()->user()
->expenses()
->select(DB::raw('SUM(amount) as total'), 'other_select_fields', ...)
->with('method')
->groupBy('method_id')
->get();
You could do this using the available collection methods:
$expenses = auth()->user()->expenses()->with('method')->get();
$groupedSums = $expenses->map(function ($expense) {
return [
'type' => $expense['method']['type'],
'amount' => $expense['method']['amount']
];
})->groupBy('type')->each->sum('amount');

Get relation of relation

I'm trying to get the comments of the links with laravel relationship, the comments of links return normally, but I don't know how to get it in view or dd(), how can I do it?
Controller:
$page = Page::where('friendly_url', $id)
->select('id', 'photo', 'friendly_url', 'name', 'description', 'followers', 'links', 'tag_id', 'links_clicks')
->with('ptag', 'links', 'links.comments', 'links.tag')
->with(['links.comments.user' => function($query) {
$query->select('id', 'name', 'lastname');
}])->with(['links.comments.userProfile' => function($query) {
$query->select('id', 'photo');
}])->first();
dd($page->getRelation('links')
Collection {#301 ▼
#items: array:2 [▼
0 => Link {#307 ▼
#relations: array:2 [▼
"comments" => Collection {#316 ▼
#items: array:1 [▶]
"tag" => Tag {#322 ▶}
]
dd($page->getRelation('links')->getRelation('comments'))
BadMethodCallException
Method getRelation does not exist.
dd($page->getRelation('links')->comments)
Exception
Property [comments] does not exist on this collection instance.
EDIT:
I want to show the attributes of comment and comment.user, how I can do it?
foreach($page->getRelation('links')->pluck('comments') as $comment) {
dd($comment);
}
Collection {#327 ▼
#items: array:1 [▼
0 => Comment {#325 ▼
#attributes: array:5 [▼
"id" => 3
"content" => "teste"
"link_id" => 1
"user_id" => 1
"created_at" => "2018-01-11 00:47:32"
]
#relations: array:2 [▼
"user" => User {#330 ▼
#attributes: array:3 [▼
"id" => 1
"name" => "Halysson"
"lastname" => "Teves dos Santos"
]
}
"userProfile" => UserProfile {#326 ▶}
]
Try this-
foreach($page->links as $link){
$comments = $link->comments;
}
You should define relations first on relevent models and then using 'with' function can load models with relations OR a relation can be called directly by single model see documentation for further details
eg: you have defined a relation for user to phone, as user has a phone, one to one relation. in user model define a relation like
public function phone()
{
return $this->hasOne('App\Phone');
}
now to call this relation on user model instance.
$user = User::find(1);
dd($user->phone);
Because you're using the select() method, you also need to include all the foreign keys in it to allow Eloquent to be able to load these relationships.
But since you just start using Laravel, I'd recommend you to just remove select() method from this complex query.
If you want the links of a page :
$page->links
if you want all the comments of a page :
$page->links->pluck('comments')
that's it :)

Resources