Laravel rename collection keys - php

I have the following line to get an array of collections.
$tags = Tag::all()->map->only(['id', 'name']);
Which produces the following data.
[{"id":1,"name":"tag 2"},{"id":2,"name":"tag 3"},{"id":3,"name":"tag-44"},{"id":4,"name":"biyoloji"}]
My objective is to rename the key names inside the collections as follows.
[{"value":1,"text":"tag 2"},{"value":2,"text":"tag 3"},{"value":3,"text":"tag-44"},{"value":4,"text":"biyoloji"}]
Basically, I want to rename "key" to "value" and "name" to "text." I tried the pluck() function, get() function, mapping but couldn't get it to work. Most probably, iterating over it with foreach and toArray() would do the trick, but I'm looking for the proper way to do it. My environment is Laravel 8 with PHP 7.4

The best way I can propose:
$tags = Tag::query()->get(['id', 'name'])
->map(function($tag){
return [
'value' => $tag->id,
'text' => $tag->name,
];
})
->toArray();
Pay attention to get(['id', 'name]) invoking. Passing required fields to get method helps improving query performance. Specially if there are lots of unused columns in the table.

You can do this via your query more efficiently
$tags = Tag::get(['id as value', 'name as text']);

Related

Laravel - pluck with specified keys

I have a line of code similar to the following:
Sport::pluck('id', 'name)
I am dealing with frontend JavaScript that expects a list in this format:
var list = [
{ text: 'Football', value: 1 },
{ text: 'Basketball', value: 2 },
{ text: 'Volleyball', value: 3 }
...
]
I am trying to figure out how I can somehow transform the id and name values that I pluck from my model to a format similar to the Javascript list.
If that's unclear, I am looking to end up with an associative array that contains two keys: text and value, where text represents the name field on my model, and where value represents the id of the model - I hope this makes sense.
How would I approach this?
I initially tried something like this (without checking the documentation)
Sport::pluck(["id" => "value", "name" => "text]);
But that isn't how you do it, which is quite clear now. I've also tried some map-related snippet, which I cannot seem to Ctrl-z to.
Any suggestions?
Another method is to use map->only():
Sport::all()->map->only('id', 'name');
The purpose of pluck is not what you intend to do,
Please have a look at below examples,
Sport::selectRaw("id as value, name as text")->pluck("text","value");
// ['1' => 'Football', '2'=>'BasketBall','3'=>'Volleyball',...]
Syntax
$plucked = $collection->pluck('name', 'product_id');
// ['prod-100' => 'Desk', 'prod-200' => 'Chair']
Please see the documentation.
Your output is possible using simple code.
Sport::selectRaw('id as value, name as text')->get();
You could use map.(https://laravel.com/docs/5.8/collections#method-map)
$mapped = Sport::all()->map(function($item, $index) {
return [
"id" => $item["id"],
"name" => $item["text"]
];
});
This is the easiest way. Actually Laravel offers a better way for it. You can use api resources to transform your data from eloquent for the frontend:
https://laravel.com/docs/5.8/eloquent-resources
Try with toArray function:
Sport::pluck('id', 'name)->toArray();
Then you can return your result with json_encode php function;

Laravel - Cannot Access Collection Indexes

For sure it's an understanding issue on my part. I'm just trying to create a collection in which the elements can be output.
Example:
So I want to be able to execute:
$collection=collect([
'index1' => 'data1',
'index2' => 'data2',
'index3' => 'data3',
]);
$row=$collection->first();
dd($row->index1);
Or similar.. But I get the error
trying to get property of a non object.
It's an understanding issue about Laravel collections, I've read the Laravel documentation which goes from basic usage, to API reference. I cannot find the information on how to produce this basic static collection.
Can someone help?
$row=$collection->first(); points to the value data1, not an object/array.
To get the expected behavior try the code below, it wraps each in individual arrays.
$collection=collect([
['index1' => 'data1'],
['index2' => 'data2'],
['index3' => 'data3'],
]);
$row=$collection->first();
dd($row->index1);
As Laravel collections implement ArrayAccess you can simply access collection items as you do with an array
$value = $collection['index1'];
or you can use the get method on collections
$value = $collection->get('index1');
Thanks for your answers, I see the $collection['index1']; format works.
It's not exactly what I wanted, let me explain why. I know there's probably a better way of doing it although, for the sake of this particular coding requirement I'd like to know the answer to this.
I'm building a CRUD blade form.
On my blade I'll have 'field1' with initial output of $dbcollection->field1
Now of course if the database returns a null (create required) this output will fail. So what I'm trying to do here is pass blade a NULL-filled collection it understands so at least it doesn't complain about non instance of an object and avoiding #if statements on the blade form to account for differences in coding format.
I believe this is what you are looking for:
Controller:
$collection=collect([
'index1' => 'data1',
'index2' => 'data2',
'index3' => 'data3',
]);
$row = $collection->first();
view:
<input type="text" value="{{ ($row->index1 ? $row->index1 : '') }}" >
.
.
.
.

Laravel isset with array value

I am having an issue - well because the value I am trying to pull from the DB does not exist yet.
Is there away that I check if its isset?
Is there any better way that I can get my value from the db to save on double code?
Controller:
$siteSocialFacebook = socialSettings::where('socialName','=','facebook')->get();
$siteFacebook = $siteSocialFacebook[0]['socialLink'];
Blade:
value="{{ old('facebook', #$siteFacebook)}}"
If you will only ever expect one result, use first() instead of get() and skip the array. You can pass it into the Blade template like this:
return view('blade', [
'siteFacebook' => $siteSocialFacebook['socialLink'] ?: null,
]);
This will prevent any issues with undefined parameters.
Edit: I just realized you're treating models as arrays. You can do this too:
return view('blade', [
'siteFacebook' => $siteSocialFacebook->socialLink,
]);
That handles it for you.

How to save multiple records with one query in Laravel?

I have over 100,000 records in my hand, as Array of STD Objects (can be converted to Array of Arrays or Array of Eloquent Models).
How to store all of them in the database with 1 query.
If you are wondering I got these data from another database connection to save them into my database.
Of course I can build each Eloquent model and call the save() function but this will take a lot of time and is not recommended anyway.
I tried the insert() on the model passing to it an Array of Eloquent Models, but didn't worked for some reasons!!
So what options do I have, other than generating the raw SQL query?
I am using Laravel 4.2 here.
You can use the insert() method on the Model, but it must be an array of arrays. You also need to make sure that all your array entries have the same keys. If an entry is missing a key, the inserted values for that entry may be off.
You mentioned you're starting with an array of objects. You can convert this array of objects to an array of arrays, and then use the insert statement.
Assuming you have a Book model:
$data = [
(object) [
'title' => 'Title 1',
'author' => 'Author 1',
'publisher' => 'Publisher 1',
],
(object) [
'title' => 'Title 2',
'author' => 'Author 2',
'publisher' => null, // make sure key exists; use null value;
],
];
// Convert your objects to arrays. You can use array_walk instead,
// if you prefer to just modify the original $data
$arrayData = array_map(function($value) {
return (array) $value;
}, $data);
// do the insert
$success = Book::insert($arrayData);
// view last query run (to confirm one bulk insert statement)
$log = DB::getQueryLog();
print_r(end($log));
I don't know the details about your existing data, so you may need some more complicated logic to ensure the arrays are correct, but the above is a gist of what should work.
Edit
When doing a bulk insert like this, you also need to pay attention to the max_allowed_packet MySQL variable. The default value for this variable is 1 MB, so if you generate a SQL statement larger than 1 MB, you will get an error. max_allowed_packet documentation
So, if this is your issue, you can either up the max_allowed_packet MySQL variable so that it is large enough to contain your entire SQL statement, or you can break your insert into chunks small enough to fit under the current max_allowed_packet size (default 1 MB).
An example of chunking your inserts:
// assume $arrayData is your array of arrays
// you can come up with some fancy algorithm to determine your optimal
// chunk size if you want.
$size = 100;
$chunks = array_chunk($arrayData, $size);
foreach($chunks as $chunk) {
Book::insert($chunk);
}

How to access the nth object in a Laravel collection object?

I have a laravel collection object.
I want to use the nth model within it.
How do I access it?
Edit:
I cannot find a suitable method in the laravel documentation. I could iterate the collection in a foreach loop and break when the nth item is found:
foreach($collection as $key => $object)
{
if($key == $nth) {break;}
}
// $object is now the nth one
But this seems messy.
A cleaner way would be to perform the above loop once and create a simple array containing all the objects in the collection. But this seems like unnecessary duplication.
In the laravel collection class documentation, there is a fetch method but I think this fetches an object from the collection matching a primary key, rather than the nth one in the collection.
Seeing as Illuminate\Support\Collection implements ArrayAccess, you should be able to simply use square-bracket notation, ie
$collection[$nth]
This calls offsetGet internally which you can also use
$collection->offsetGet($nth)
and finally, you can use the get method which allows for an optional default value
$collection->get($nth)
// or
$collection->get($nth, 'some default value')
#Phil's answer doesn't quite obtain the nth element, since the keys may be unordered. If you've got an eloquent collection from a db query it'll work fine, but if your keys aren't sequential then you'll need to do something different.
$collection = collect([0 => 'bish', 2 => 'bash']); $collection[1] // Undefined index
Instead we can do $collection->values()[1] // string(4) bash
which uses array_values()
Or even make a macro to do this:
Collection::macro('nthElement', function($offset, $default = null) {
return $this->values()->get($offset, $default);
}):
Example macro usage:
$collection = collect([0 => 'bish', 2 => 'bash']);
$collection->nthElement(1) // string(4) 'bash'
$collection->nthElement(3) // undefined index
$collection->nthElement(3, 'bosh') // string (4) bosh
I am late to this question, but I thought this might be a useful solution for someone.
Collections have the slice method with the following parameters:
$items->slice(whereToStartSlice, sizeOfSlice);
Therefore, if you set the whereToStartSlice parameter at the nth item and the sizeOfSlice to 1 you retrieve the nth item.
Example:
$nthItem = $items->slice($nth,1);
If you are having problems with the collection keeping the indices after sorting... you can make a new collection out of the values of that collection and try accessing the newly indexed collection like you would expect:
e.g. Get the second highest priced item in a collection
$items = collect(
[
"1" => ["name" => "baseball", "price" => 5],
"2" => ["name"=> "bat", "price" => 15],
"3" => ["name" => "glove", "price" => 10]
]
);
collect($items->sortByDesc("price")->values())[1]["name"];
// Result: glove
Similar to morphs answer but not the same. Simply using values() after a sort will not give you the expected results because the indices remain coupled to each item.
Credit to #howtomakeaturn for this solution on the Laravel Github:
https://github.com/laravel/framework/issues/1335

Categories