Append a Laravel collection with another collection - php

I am trying to append an Eloquent collection with another Eloquent collection in Laravel 5.3.
This is what I've done so far:
$entries = Entry::all();
$posts = Post::all();
$entries->merge($posts);
I tried to use merge() as shown in the code above, but it seems that I'm stuck with this kind of problem (since some of them have the same id with the same value):
Collection merge eating up some rows
Any ideas?

For versions < 5.4 you can merge the two eloquent collections by resetting keys with toBase like this:
$mergedCollection = $entries->toBase()->merge($posts);
For versions >= 5.4 you can use concat as suggested by Jason.

I believe you may be looking for concat(). This will append one container to the end of another container, regardless of the keys of either.
$mergedCollection = $entries->concat($posts);

Here's the link: laravel7.X
$collection = collect(['Desk', 'Chair']);
$merged = $collection->merge(['Bookcase', 'Door']);
$merged->all();

if you need to merge big_products and small_products:
$products = $bigProducts->values()->merge($smallProducts->values());
The merge method merges the given array or collection with the original collection. If a string key in the given items matches a string key in the original collection, the given items's value will overwrite the value in the original collection.
But
If the given items's keys are numeric, the values will be appended to the end of the collection:
Thus, all you need is to get rid of keys and you can do it with ->values() function.
Tested on Laravel-6

The merge() method receives an array, so you have to do something like
$entries->merge($posts->toArray());
Laravel Collections: merge() method

Related

Laravel: Get model attributes by key array

I'm attempting to access a models attributes by an array of keys. The desired functionality would work something like $model->getAttribute('name'), but accept an array instead of a string.
Let's say we had a model with attributes name of 'A', age of 2 and blood_type of 'B'.
$attributesToPull = ['name', 'age'];
$model->getAttributes($attributesToPull);
// returns ['A', 2]
I've checked through the Laravel docs and cant find anything that quite fits.
The results dont need to come straight from the model, they can be pulled into their own associative array using $model->getAttributes() and then have a native PHP array function such as array_intersect to filter the results, but even then I can't seem to find a function that will allow me to filter an associative array with an indexed array.
Does anyone know how I could go about this, ideally without using a loop or a callback? The answer can be pretty open, it can return a collection or an array and use either the associative array of the model attributes or call a function on the model itself.
The Model has a only method:
$model->only($attributesToPull);

Laravel Collection returns only 11 values

I'm building timeline for my app and got this issue, that my collection returns me only 11 values.
I have tried to change merged data and it always returns me 11 values that makes me very confused, bc their is no limits in my code.
public function timeline($company_id)
{
// USER COMPANY
$company = auth()->user()->companies()->findOrFail($company_id);
//GETTING DATA TO MERGE COLLECTION
$equities = Share::where('company_id', $company_id)->get();
$equityGrants = EquityGrant::whereIn('share_id', $equities->pluck('id')->toArray())->get();
$warrants = Warrant::where('company_id', $company_id)->orderBy('created_at', 'DESC')->get();
$warrantGtrants = WarrantGrant::whereIn('warrant_id', $warrants->pluck('id')->toArray())->get();
$convertibles = Convertible::where('company_id', $company_id)->get();
// CREATING COLLECTION
$operations = $equities->merge($equityGrants)->merge($warrants)->merge($warrantGtrants)->merge($convertibles);
$operations = $operations->sortByDesc('created_at');
// RETURNS ME ONLY 11 VALUES
return $operations->values()->all();
}
I tried to merge() less instances, like $operations = $equities->merge($equityGrants)->merge($warrants)->merge($warrantGtrants) but always maximum 11 values. I need to return all data for my timeline.
HELP ME Please! :)
Cheers, love :)
SOLUTION:
function concat() instead of merge() fixed the problem.
Using merge() on collections in Laravel will cause overiding all elements with the same ids comming from Eloquent query.
I believe that is why you are getting only 11 elements because this is the count off all elements having distinct ids.
According to the documentation of merge, when a key matches, it will be overwritten by the last item with that key (the collection you are merging in).
A solution to your problem would be the keyBy method (documentation). If you use keyBy('created_at'), it will give you an array where the keys are the created_at timestamps.
Although it sounds like they would be unique, there is a great change that some of the linked resources (like warrant and warrentGrants) are created at the exact same moment. If that is the case, you should try to find another key or something that is unique across all resources.
SOLUTION: function concat() instead of merge() fixed the problem.

convert value of a collection into a key in laravel

this my mysql query:
$tmp=almacen::select('nombre_empresa','oferta')->join('users','users.id','=','almacen.emp_id')->where('almacen.event_id','5')->get();
this returns several objects like these:
...
App\almacen {#1948
nombre_empresa: "Aux1",
oferta: "Serv_1234",
},
App\almacen {#1947
nombre_empresa: "Aux2",
oferta: "Serv 12345678",
},
...
i need to convert "nombre_empresa" in a key, for example
$tmp['Aux2']
this return:
"Serv 12345678"
Is it possible to do this in Laravel? or should I do it in another way?
Sure Laravel can handle that, check out the available collections methods. mapWithKeys is probably what you're looking for:
$mapped = $results->mapWithKeys(function ($item) {
return [$item['nombre_empresa'] => $item['oferta']];
});
Edit: mapWithKeys rather than map
https://laravel.com/docs/5.5/collections#method-mapwithkeys
You can use keyBy:
$tmp->keyBy('nombre_empresa');
You can chain it directly at the end of your query, after the get method.
But on this case you are only getting 2 fields from the database you can use pluck directly on the query builder:
$tmp=almacen::select('nombre_empresa','oferta')
->join('users','users.id','=','almacen.emp_id')
->where('almacen.event_id','5')
->pluck('oferta','nombre_empresa');
The second argument of pluck will be use for the key.
If none is given it will just use numeric keys: 0, 1,...
Of course, all solutions are valid, but the way I would do it which I think is also considered to be best practice in Laravel is by using pluck()
Documentation
Example:
$tmp=almacen::select('nombre_empresa','oferta')
->join('users','users.id','=','almacen.emp_id')
->where('almacen.event_id','5')
->get()
->pluck('oferta', 'nombre_empresa')
->toArray();
Do notice that pluck() is a new method introduced in version 5.2 to replace the before used lists()
Notice that I would transform toArray() but it is not a must, I would consider it since with such a simple collection I might not need the extra functionality making this variable take up less space.

Iterating collections CakePHP 3

I am having an array that I make a collection out of it with CakePHP 3 then I use match to extract a new collection containing only the elements that id=2.
What I am unable to understand is after I use match if I use each to iterate with my original collection i see the element with id=2. \
Shouldn't it be removed from the original collection ?
How can I iterate my new collection cause each, compile, foreach are not working, and when I use debug all I get is
\src\Controller\ComlibsController.php (line 51)
object(Cake\Collection\Collection) {
'count' => (int) 0
}
The collection code id :
//get the current answer and remove it from the query array
$mycollection = new Collection($query[0]['answers']);
$answer = $mycollection->match(['answers.id' => $theid]);
Cookbook is not as simple as they claimed.
Any sort of help would be appreciated.
Performing a match on a collection returns a new collection with the elements that match, but it doesn't alter the original collection in any way.
When you're creating your collection, you're specifying the ['answers'] key, which means that won't be part of the path any more for elements in the collection. You will therefore simply want to do ->match(['id' => $theid]) to find the matches.

how to get collection results as array with ids

it should be simple but I am missing something,
lets say this simple eloquent:
Post::select('id')->take(5)->get();
I want to get simple array with the results id's so it will look like this:
[1,2,3,4,5]
but i am getting something like this:
[{"id":"1"},{"id":"2"},{"id":"3"},{"id":"4"},{"id":"5"}]
flatten() not working and I am getting the same results:
Post::select('id')->take(5)->get()->flatten();
http://laravel.com/docs/master/collections#method-flatten
The flatten
method flattens a multi-dimensional collection into a single
dimension:
what i am missing? I remember there is a short line laravel way of getting this results without iterate through the array and create a new one
just got it, its the lists() that do the magic so the answer is:
Post::select('id')->take(5)->lists('id');
Update:
as of laravel 5.2 lists() become deprecated
The lists method on the Collection, query builder and Eloquent query
builder objects has been renamed to pluck. The method signature
remains the same.
the new method name is pluck which work the same:
Post::select('id')->take(5)->pluck('id');

Categories