I'm using akeneo-labs spreadsheet-parser library to extract data from xlsx file.
use Akeneo\Component\SpreadsheetParser\SpreadsheetParser;
$workbook = SpreadsheetParser::open('myfile.xlsx');
$myWorksheetIndex = $workbook->getWorksheetIndex('myworksheet');
foreach ($workbook->createRowIterator($myWorksheetIndex) as $rowIndex => $values) {
var_dump($rowIndex, $values);
}
Actually, you can get value by column index in a loop, is it possible to get value by column name instead?
maybe using another package as suggested fixes your problem.
also you can use array_column https://www.php.net/manual/en/function.array-column.php
Maybe you can do that in a spreadsheet CSV file by using PHP default function fgetcsv(), you can go throw an overview from here: https://www.php.net/manual/en/function.fgetcsv
fgetcsv — Gets line from file pointer and parse for CSV fields
First of all, save as your Xls file in CSV type then you can take your value from that CSV file by column name.
You can try this.
use Akeneo\Component\SpreadsheetParser\SpreadsheetParser;
$workbook = SpreadsheetParser::open('myfile.xlsx');
$myWorksheetIndex = $workbook->getWorksheetIndex('myworksheet');
// all columns
$columns = [];
foreach ($workbook->createRowIterator($myWorksheetIndex) as $rowIndex => $values) {
if ($rowIndex == 1) {
$columns = $values;
} else {
$datum = array_combine($columns, $values);
// get value by column name
var_dump($datum['name']);
}
}
Related
I am working on a CSV file upload function. The whole script is working fine and the way I am doing it is by eliminating the first line of the CSV file which is the heading and then using the data only to insert into the database. However, this adds a restriction for the CSV to be sorted always. I need to accept unsorted CSV too. For example, if a column name is in second column and of the CSV file then the array index becomes $arr[1]. Currently, I am using $arr[1] for inserting the values in the database and performing operations. This is bad. If the user uploads an unsorted CSV where name is in 4th column and say phone is in 2nd column where earlier I expected the name to be, then this will disrupt the whole operation. Therefore, how can I use the first heading line and use them as a key like $arr['name'] for performing the required operations?
My current code:
$csvFile = fopen($_FILES['file']['tmp_name'], 'r');
fgetcsv($csvFile);
$arr = [];
while($data = fgetcsv($csvFile, 100, ",")){
$arr['id'] = $data[0]; // WANT TO USE $data['id'] FROM CSV FILE's FIRST LINE
$arr['date'] = date('Y-m-d', strtotime($data[1])); // WANT TO USE $data['date'] FROM CSV FILE's FIRST LINE
$arr['stock'] = $data[2]; // WANT TO USE $data['stock'] FROM CSV FILE's FIRST LINE
$arr['price'] = $data[3]; // WANT TO USE $data['price'] FROM CSV FILE's FIRST LINE
$ar[] = $arr;
}
fclose($csvFile);
How can I get the keys from the file and use it here in the code above?
UPDATE
I see that if I store the keys in array and print it like this
$csvFile = fopen($_FILES['file']['tmp_name'], 'r');
$getFileKeys = fgetcsv($csvFile); // STORED KEYS IN ARRAY
fgetcsv($csvFile);
print_r($getFileKeys);
then I get an array like this:
Array
(
[0] => id
[1] => date
[2] => stock_name
[3] => price
)
I need to write logic in such a way that if I have 4 variables one for each of the element above, then no matter if the index changes for any element, the variable will receive the same value dynamically.
Since you already got the keys inside $getFileKeys variable you can simply use a for loop to loop through the array of keys and dynamically assign the indexes based upon the field.
$getFileKeys = fgetcsv($csvFile);
$keys = [];
for($i = 0; $i < count($getFileKeys); $i++){
if($getFileKeys[$i] == 'id'){
$keys['id'] = $i;
}else if($getFileKeys[$i] == 'date'){
$keys['date'] = $i;
}else if($getFileKeys[$i] == 'stock_name'){
$keys['stock'] = $i;
}else if($getFileKeys[$i] == 'price'){
$keys['price'] = $i;
}
}
while($getData = fgetcsv($csvFile, 100, ",")){
$arr['id'] = $getData[$keys['id']];
$arr['date'] = date('Y-m-d', strtotime($getData[$keys['date']]));
$arr['stock'] = trim($getData[$keys['stock']]);
$arr['price'] = $getData[$keys['price']];
$ar[] = $arr;
}
The $keys array now dynamically stores the indexes for each key. Therefore, this now sorts the CSV file no matter at what order which column is placed in.
You could make variables out of the first line, so that you can call them by her name.
$exampleLine=[
'id' => 1
'date' => '2022-07-01'
'stock_name' => 'stock_name'
'3' => 'price'
];
foreach($exampleLine as $name){
$$name=$name;
}
This will give you the variables with appropriate content, so that $id contains 1, $date '2022-07-01' and so on.
to prevent different spellings, you could still use lcfirst and trim.
PHP-Manual
I wondered if it was possible to read a csv file written like this in symfony using League csv or something else.
water_level,2,456,345
wind_speed,25,456,56
food_level,10.4,123,23
animal_count,56,34,124
number_of_machines,150,345,54
machineId,1234567,1234568,1234567
Header in the first column and data in the next columns.
Right now, I can read the files when the header is in the first row, I just want to know if it's possible to read it the other way!
I tried transposing but I get an error. I don't really know what's causing it if the array really is transposed.
$pathString = implode($newFilesPath);
$reader = (Reader::createFromPath($pathString))->setHeaderOffset(0);
$results = $this->transpose(iterator_to_array($reader->getRecords()));
foreach ($results as $row) {
$properties = (new AppProperty)
->setWaterLevel($row['water_level'])
->setWindSpeed($row['wind_speed'])
->setFoodLevel($row['food_level'])
->setAnimalCount($row['animal_count'])
->setNumberOfMachines($row['number_of_machines'])
->setMachineId($row['machineId'])
->setDate(new \DateTime());
$this->em->persist($properties);
}
$this->em->flush();
}
private function transpose ($array){
return array_map(null, $array);
}
The error:
Argument 1 passed to App\Entity\Property::setWaterLevel() must be of the type int or null, string given.
It looks like the transpose just didn't work.
I've a list of clients in csv file as:
Name,Credited
ABC,Y
BCD,Y
XYZ
ABC
My task is to check, if the client already exists in the list:
IF yes, check if he is already credited.
If yes, delete that name from the list.
I've started writing the code, but not sure how can I achieve my task.
//Store the file in array
$fcsv = file($files);
foreach($headers as $header) {
// Push headers to new array.
array_push($headings, strtolower(trim($header)));
}
Can someone please help!!
Thanks in advance
Loop through all the lines in the file, and use str_getcsv() to parse it as a CSV line. Then check if the first column is the client name and the second is Y.
$clients = file($files, FILE_IGNORE_NEW_LINES);
$deleted = false;
foreach ($clients as $index => $client_line) {
$split = str_getcsv($client_line);
if ($split[0] == $client) {
if (isset($split[1]) && $split[1] == 'Y') {
unset($clients[$index]);
$deleted = true;
}
break; // Stop searching after we found the client
}
}
// Rewrite the file if we deleted the client.
if ($deleted) {
file_put_contents($files, implode("\n", $clients));
}
Here's the basic implementation of searching if the name of the client exist in the given csv file.
$client = "ABC";
$fcsv = file('uploads/task.csv');
foreach ($fcsv as $key => $value) {
$temp = explode(',', $value);
if ($temp[0] == $client) {
unset($fcsv[$key]);
}
}
Each rows in csv, if you look at it using var_dump() would look like this,
"Name,Credited"
If your csv file contains of more than one rows, this line
$fcsv = file('uploads/task.csv');
will return array of rows, using a loop split the content of each rows using explode(',', $value);for you to have a data in every column of the rows.
Like in the example above you can now chose, which column you need look, to be able for you to compare where the client exist and delete.
This works. It's sort-of a generic csv file importer and key assigner. Looking for feedback how this approach could be made more elegant. I started learning php last week. This forum is fantastic.
<?php
$csvfilename = "sdb.csv";
$filekeys = array('SSID','EquipName','EquipTypeSignalName','elecpropID');
$records = InputCsvFile($csvfilename,$filekeys);
function InputCsvFile($filename,$filekeys){
$array1 = array_map('str_getcsv', file($filename));
foreach($array1 as $element){
$int1 = 0;
unset($array3);
foreach($filekeys as $key){
$array3[$key] = $element[$int1];
$int1++;}
$array2[] = $array3;}
return $array2;}
?>
Using array_map() is clever, but since you have to further process each row, is somewhat unnecessary. I would rewrite InputCsvFile like this:
function InputCsvFile($filename, array $columns) {
$expectedCols = count($columns);
$arr = [];
// NOTE: Confirm the file actually exists before getting its contents
$rows = file($filename);
foreach($rows as $row) {
$row = str_getcsv($row);
if (count($row) == $expectedCols)) {
$arr[] = array_combine($filekeys, $element);
} else {
// Handle the column count mismatch. The test is required because
// otherwise, array_combine will complain loudly.
}
}
return $arr;
}
Alternatively, since you're dealing with files, you could loop on fgetcsv(), rather than using file() + str_getcsv(). Using fgetcsv() will use less memory (since the entire file doesn't have to be read in entirely and be kept in memory through the duration of the iteration), which may or may not be a concern depending on your file sizes.
array_combine() (which incidentally, is one of my favorite functions in PHP) creates a new array given arrays of keys (your list of columns in your $filekeys array) and values (the processed rows from the csv), and is practically tailor-made for turning csv files into more usable arrays.
I am trying to dynamically get a specific data array by using a function (parsing an excel file, so for instance, I can grab the fourth column of the file as follows):
foreach ($obj->Worksheet->Table->Row as $row)
{
$rows[] = (string)$row->Cell[4]->Data;
}
My problem is im trying to dynamically specify the "cell" to get and I dont know how to properly format it as I cant use $ within the [] apparently. So Im trying to do this and its not working:
$col = 4;
foreach ($obj->Worksheet->Table->Row as $row)
{
$rows[] = (string)$row->Cell[$col]->Data;
}
Your expression works, but if you have more complicated expressions use braces.
$rows[] = (string)$row->{Cell[$col]}->Data;