I'm new to laravel, and I'm making an app that need a .CSV data to be imported into the database, the data has been succesfully imported however i faced this issue :
ErrorException array_combine(): Both parameters should have an equal number of elements
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Accounts;
class AccountController extends Controller
{
public function show(){
return view ('upload');
}
public function store(Request $request){
$file = $request->file('upload-file');
$csvData = file_get_contents($file);
$rows = array_map("str_getcsv", explode("\n", $csvData));
// dd($rows);
$header = array_shift($rows);
// dd($header);
foreach ($rows as $row) {
$row = array_combine($header, $row);
if (count($header) != count($row)) {
continue;
}
set_time_limit(0);
Accounts::create([
'AccountClass' => $row['Classe'],
'AccountNumber' => $row['Compte'],
'AccountDesc' => $row['Desc'],
'active' => 1,
]);
}
return view ('home');
}
}
would you point me to the right direction thank you in advance
This error appears when you try to combine two arrays with unequal length.
$arr1 = ["a", "s", "d"];
$arr2 = [1, 2, 3];
if(count($arr1) == count($arr2)){
$result = array_combine($arr1, $arr2);
} else{
echo "Error array combine";
}
Related
I'm trying to walk through as CSV file and extract all its contents.
After the contents are imported I need to insert them into my mysql database table but before I can do that I need to check if these records exist. If a record does not, import this register.
I've got headers and all content and I had created a arrays with this information, but I don't know if it's 100% correct. This is my actual code:
function csv_content_parser($content) {
foreach (explode("\n", $content) as $line) {
// Generator saves state and can be resumed when the next value is required.
yield str_getcsv($line);
}
}
$archivo = fopen($route, "r");
// Get content from csv file.
$content = file_get_contents($adjunto);
// Create arrays from csv file's lines.
$data = array();
$headers = array();
$rows = array();
//get all content
foreach ($this->csv_content_parser($content) as $fields) {
array_push($data, $fields);
}
//get headers
foreach($data[0] as $dat){
array_push($headers, $dat);
}
//get all content of csv without headers
for($i=1; $i<count($data); $i++){
array_push($rows, $data[$i]);
}
//close the file
fclose($archivo);
The content of the csv file (it's an example file) is for example (I need to create a generic importer):
Array
(
[0] => Array
(
[0] => ggggg#gmail.com
[1] => david
[2] => 005
[3] => hola
[4] => eee
[5] => eee
)
[1] => Array
(
[0] => ggggg#gmail.com
[1] => david
[2] => 005
[3] => hola
[4] => eee
[5] => eee
)
)
And my headers:
Array
(
[0] => Email
[1] => Name
[2] => Identification
[3] => Note
[4] => Field Label 1
[5] => Field Label 2
)
My question is: Is this a good solution or is there any better solution to do this? I need to insert this data into my database. How would can to do this?
updated:
model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Listado extends Model
{
protected $table = 'listado';
protected $fillable = [
'nomape', 'direccion', 'provincia', 'ciudad', 'cp', 'telefono', 'movil', 'id_teleoperadora'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [];
public function scopeTodos( $query )
{
return $query->orderBy('nomape', 'ASC')->orderBy('telefono', 'ASC');
}
public function scopeSinAsignar( $query, $id_llamadas )
{
$query->whereIn('id', $id_llamadas)->whereNull('id_teleoperadora');
}
public static function Filtrado($request)
{
$query = self::query();
if($request['direccion']) {
$query->where('direccion', 'like', '%' .$request['direccion']. '%');
}
if($request['ciudad']) {
$query->where('ciudad', $request['ciudad']);
}
if($request['cp']) {
$query->where('cp', $request['cp']);
}
/*if($request['teleoperadora']) {
$query->where('id_teleoperadora', $request['teleoperadora']);
}*/
return $query;
}
public function scopeConEstado( $query, $nombreEstado )
{
return $query->whereHas('llamada.estado', function ($query) use ($nombreEstado) {
$query->whereNombre($nombreEstado);
})->orWhereDoesntHave('llamada');
}
public function scopeConEstados( $query, $nombresEstado )
{
return $query->whereHas('llamada.estado', function ($query) use ($nombresEstado) {
$query->whereIn('nombre', $nombresEstado);
})->orWhereDoesntHave('llamada');
}
public function llamada()
{
return $this->hasOne('App\Llamada', 'id_listado', 'id')->latest();
}
public function llamada_test()
{
return $this->hasOne('App\Llamada', 'id', 'id_listado');
}
/**
* RETURN OPERATOR
*/
public function teleoperadora()
{
return $this->hasOne('App\User', 'id', 'id_teleoperadora')->withDefault(['nombre' => 'NINGUNA']);
}
/**
* RETURN CALL DATA
*/
public function callStatusName()
{
return $this->hasOne('App\llamada', 'id_listado', 'id');
}
}
update 2
all my function code
function csv_content_parser($content) {
foreach (explode("\n", $content) as $line) {
// Generator saves state and can be resumed when the next value is required.
yield str_getcsv($line);
}
}
public function uploadListing(Request $request)
{
$adjunto = $request->file('attached');
$route = "";
if(isset($adjunto)){
$name = $adjunto->getClientOriginalName();
$result = $adjunto->storeAs('importaciones', $name, 's4');
$route = public_path('storage/importaciones/'.$name);
}else{
return "Error al adjuntar recibo de envío, consulte al administrador del sistema";
}
//Abrimos nuestro archivo
$archivo = fopen($route, "r");
// Get content from csv file.
$content = file_get_contents($adjunto);
// Create arrays from csv file's lines.
$data = array();
$headers = array();
$rows = array();
//get all content
foreach ($this->csv_content_parser($content) as $fields) {
array_push($data, $fields);
}
//get headers
foreach($data[0] as $dat){
array_push($headers, $dat);
}
//get all content of csv without headers
for($i=1; $i<count($data); $i++){
array_push($rows, $data[$i]);
}
Listado::insertOrIgnore(array_map(function($row) {
return array_combine($headers, $row);
}, $rows));
//Cerramos el archivo
fclose($archivo);
}
updated 3
function csv_content_parser($content) {
foreach (explode("\n", $content) as $line) {
// Generator saves state and can be resumed when the next value is required.
yield str_getcsv($line);
}
}
public function uploadListing(Request $request)
{
$adjunto = $request->file('attached');
$route = "";
if(isset($adjunto)){
$name = $adjunto->getClientOriginalName();
$result = $adjunto->storeAs('importaciones', $name, 's4');
$route = public_path('storage/importaciones/'.$name);
}else{
return "Error al adjuntar recibo de envío, consulte al administrador del sistema";
}
//Abrimos nuestro archivo
$archivo = fopen($route, "r");
// Get content from csv file.
$content = file_get_contents($adjunto);
// Create arrays from csv file's lines.
$data = array();
$headers = array();
$rows = array();
//get all content
foreach ($this->csv_content_parser($content) as $fields) {
array_push($data, $fields);
}
//get headers
foreach($data[0] as $dat){
array_push($headers, $dat);
}
//get all content of csv without headers
for($i=1; $i<count($data); $i++){
array_push($rows, $data[$i]);
}
Listado::insertOrIgnore(array_map(function($row) use($headers) {
return array_combine($headers, $row);
}, $rows));
//Cerramos el archivo
fclose($archivo);
}
As per docs:
DB::table('listado')->insertOrIgnore(array_map(fn($row) => array_combine($headers, $row), $rows));
Since you have Eloquent models, you can use Eloquent's insert() method:
Listado::insertOrIgnore(array_map(fn($row) => array_combine($headers, $row), $rows));
Since you said the following
I need to check if these records exist. If a record does not, import this register.
I chose the method insertOrIgnore() but maybe you want something else. Luckily, Laravel comes with a bunch of similar methods for slightly different use cases.
Prior to PHP 7.4, array_map(fn($row) => array_combine($headers, $row), $rows) has to be:
array_map(function($row) use($headers) {
return array_combine($headers, $row);
}, $rows)
This is the code to the upload function in my controller.The problem at hand is that when i upload the csv file to my database I only get(fetch) the first row of the file and the rest of the rows are omitted.I need to be able to get all the rows from the file.Please assist in anyway you can...Thanks in advance
public function upload(Request $request)
{
//get file
//$allowed = array('csv');
$upload=$request->file('upload');
//$extension = File::extension($upload);
$filePath=$upload->getRealPath();
//open and read
$file=fopen($filePath,'r');
$header= fgetcsv($file);
$escapedHeader=[];
//validate
foreach ($header as $key => $value) {
$lheader= strtolower($value);
$escapedItem=preg_replace('/[^a-z]/', '', $lheader);
array_push($escapedHeader, $escapedItem);
}
//looping throught other columns
while ($columns=fgetcsv($file)) {
if ($columns[0]=="")
{
continue;
}
$data= array_combine($escapedHeader, $columns);
dd($data);
//setting type
foreach ($data as $key => $value) {
$value=($key=="phone" || $key=="nationalid" || $key=="staffsalary")?(integer)$value:(string)$value;
}
//table update
$firstname=$data['firstname'];
$lastname=$data['lastname'];
$email=$data['email'];
$phone=$data['phone'];
$nationalid=$data['nationalid'];
$staffid=$data['staffid'];
$stafftitle=$data['stafftitle'];
$staffsalary=$data['staffsalary'];
$employees= Employees::firstOrNew(['phone'=>$phone,'nationalid'=>$nationalid]);
$employees->firstname=$firstname;
$employees->lastname=$lastname;
$employees->email=$email;
$employees->staff_id=$staffid;
$employees->staff_title=$stafftitle;
$employees->staff_salary=$staffsalary;
$employees->employer_phone = Auth::user()->phone;
$employees->save();
return redirect()->route('home');
}
}
The main reason you're having this issue is because your return redirect()->route('home'); is inside your while loop. Just move it so it's on the outside and it should work fine.
Also, this is just an FYI but you could use Collections to clean your controller up a little but:
public function upload(Request $request)
{
$upload = $request->file('upload');
$csv = collect(array_map('str_getcsv', file($upload->getRealPath())));
$keys = array_map(function ($item) {
return preg_replace('/[^a-z]/', '', strtolower($item));
}, $csv->shift());
$csv
->map(function ($row) use ($keys) {
return array_combine($keys, $row);
})
->reject(function ($row) {
return empty(array_first($row));
})
->each(function ($row) {
$row = array_map(function ($value, $key) {
return in_array($key, ['phone', 'nationalid', 'staffsalary']) ? (integer)$value : (string)$value;
}, $row);
$employees = Employees::firstOrNew(['phone' => $row['phone'], 'nationalid' => $row['nationalid']]);
$employees->firstname = $row['firstname'];
$employees->lastname = $row['lastname'];
$employees->email = $row['email'];
$employees->staff_id = $row['staffid'];
$employees->staff_title = $row['stafftitle'];
$employees->staff_salary = $row['staffsalary'];
$employees->employer_phone = Auth::user()->phone;
$employees->save();
});
return redirect()->route('home');
}
Obviously, you don't have to use the code above, I just thought I'd show you an alternative way to write it.
I wasn't sure what if ($columns[0]=="") was there for but I've added that in with the reject() method.
Hope this helps!
Your problem is a different breakline for different OS. Use this construction for better:
// take care of all possible newline-encodings in input file
$NEWLINE_RE = '/(\r\n)|\r|\n/';
$csv = preg_replace($NEWLINE_RE,'===BREAK===', $old_csv);
foreach (explode('===BREAK===', $csv) as $k => $line){
if(strlen($line) > 0) $lines[] = stripcslashes(trim($line));
}
I am trying to make first array value to uppercase.
Code:
$data = $this->positions_model->array_from_post(array('position', 'label'));
$this->positions_model->save($data, $id);
So before save($data, $id) to database I want to convert position value to uppercase. I have tried by this
$data['position'] = strtoupper($data['position']);
but than it is not storing the value in db with uppercase but as it is what user inputs.
Current output of $data:
Array ( [position] => it [label] => Information Technology )
And I want it in uppercase as IT
Added Model Method
public function get_positions_array($id = NULL, $single = FALSE)
{
$this->db->get($this->_table_name);
$positions = parent::get($id, $single);
$array = array();
foreach($positions as $pos){
$array[] = get_object_vars($pos);
}
return $array;
}
Main MY_Model method
public function array_from_post($fields)
{
$data = array();
foreach ($fields as $field) {
$data[$field] = $this->input->post($field);
}
return $data;
}
This should work:
$data = $this->positions_model->array_from_post(array('position', 'label'));
$data['position'] = strtoupper($data['position']);
$this->positions_model->save($data, $id);
If Its not, then $data array have only read attribute.
The array_from_post() method returns an array with the format below:
$data = array(
'position' => 'it',
'label' => 'Information Technology'
);
So, you could make first value of the array to uppercase, by using array_map or array_walk functions as follows:
$data = array_map(function($a) {
static $i = 0;
if ($i === 0) { $i++; return strtoupper($a); }
else return $a;
}, $array);
Note: This only works on PHP 5.3+, for previous versions, use the function name instead.
Here is the array_walk example, which modifies the $data:
array_walk($data, function(&$value, $key) {
static $i = 0;
if ($i == 0) { $i++; $value = strtoupper($value); }
});
Again, if you're using PHP 5.2.x or lower, you could pass the function name instead.
I am new to laravel and having a tough time figuring out a way to export one table to csv.
I have tried the following code in the controller class, but it gives me an error:
public function get_export()
{
$table = Cpmreport::all();
$file = fopen('file.csv', 'w');
foreach ($table as $row) {
fputcsv($file, $row);
}
fclose($file);
return Redirect::to('consolidated');
}
Model Class for Cpmreport:
class Cpmreport extends Eloquent
{
public static $table='cpm_report';
}
The error :
Message:
fputcsv() expects parameter 2 to be array, object given
Location:
C:\xampp\htdocs\cpm_report\application\controllers\cpmreports.php on line 195
Any help would be appreciated.
Easy way
Route::get('/csv', function() {
$table = Cpmreport::all();
$output='';
foreach ($table as $row) {
$output.= implode(",",$row->toArray());
}
$headers = array(
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="ExportFileName.csv"',
);
return Response::make(rtrim($output, "\n"), 200, $headers);
});
fputcsv($file, $table); should be fputcsv($file, $row), shouldn't it?
And convert the object to an array using Eloquent's to_array()method: http://laravel.com/docs/database/eloquent#to-array
public function get_export()
{
$table = Cpmreport::all();
$file = fopen('file.csv', 'w');
foreach ($table as $row) {
fputcsv($file, $row->to_array());
}
fclose($file);
return Redirect::to('consolidated');
}
Select query of MySQL data.
$data = \DB::connection('mysql')->select($select);
Call following function:
query_to_csv($data, 'data.csv');
function data_to_csv($data, $filename)
{
$fp = fopen($filename, 'w');
foreach ($data as $row) {
fputcsv($fp, $row);
}
fclose($fp);
}
0.1 Million records takes 1 second to create.
this is better and simple.
$file_name = "abc";
$postStudent = Input::all();
$ck = DB::table('loan_tags')->select('LAN')->where('liabilitiesId', $postStudent['id'])->get();
$i = 0;
foreach ($ck as $row) {
$apps[$i]['LAN'] = $row->LAN;
$apps[$i]['Account_number'] = $postStudent['account_number'];
$apps[$i]['Bank_Name'] = $postStudent['bank_name'];
$i++;
}
ob_end_clean();
ob_start();
Excel::create($file_name, function($excel) use($apps){
$excel->sheet('Sheetname', function($sheet) use($apps){
$sheet->row(1, array(
'LAN', 'Account number' , 'Bank Name'
));
$k = 2;
foreach ($apps as $deta) {
$sheet->row($k, array($deta['LAN'], $deta['Account_number'], $deta['Bank_Name']
));
$k++;
}
});
})->download('xlsx');
I have a CSV with the first row containing the field names. Example data is...
"Make","Model","Note"
"Chevy","1500","loaded"
"Chevy","2500",""
"Chevy","","loaded"
I need my data formatted in an array of key-value pairs where the key name is the column heading. I guess it would something like this for row 1:
$array = [
"Make" => "Chevy",
"Model" => "1500",
"Note" => "loaded"
];
...row 2...
$array = [
"Make" => "Chevy",
"Model" => "1500",
"Note" => ""
];
...and row 3...
$array = [
"Make" => "Chevy",
"Model" => "",
"Note" => "loaded"
];
I'm not sure how to do this other than statically - problem is the columns with their associated data could change from one file to the next... columns rearranged, dropped, or added.
You ideas are much appreciated.
$all_rows = array();
$header = fgetcsv($file);
while ($row = fgetcsv($file)) {
$all_rows[] = array_combine($header, $row);
}
print_r($all_rows);
PHP offers already 99,9% of what you need within SplFileObject, you add the missing 0,1% by extending from it. In the following example CSVFile extends from it:
$csv = new CSVFile('../data/test.csv');
foreach ($csv as $line)
{
var_dump($line);
}
With your example data:
array(3) {
["Make"]=> string(5) "Chevy"
["Model"]=> string(4) "1500"
["Note"]=> string(6) "loaded"
}
array(3) {
["Make"]=> string(5) "Chevy"
["Model"]=> string(4) "2500"
["Note"]=> string(0) ""
}
array(3) {
["Make"]=> string(5) "Chevy"
["Model"]=> string(0) ""
["Note"]=> string(6) "loaded"
}
CSVFile is defined as the following:
class CSVFile extends SplFileObject
{
private $keys;
public function __construct($file)
{
parent::__construct($file);
$this->setFlags(SplFileObject::READ_CSV);
}
public function rewind()
{
parent::rewind();
$this->keys = parent::current();
parent::next();
}
public function current()
{
return array_combine($this->keys, parent::current());
}
public function getKeys()
{
return $this->keys;
}
}
If you do it this way, the details are nicely encapsulated away. Additionally it's more easy to deal with errors (e.g. count mismatch) inside the current() function so the code which makes use of the data does not need to deal with it.
Edit:
However the example given is short in terms of re-usablity. Instead of extending from SplFileObject it's much better to aggregate it:
class KeyedArrayIterator extends IteratorIterator
{
private $keys;
public function rewind()
{
parent::rewind();
$this->keys = parent::current();
parent::next();
}
public function current()
{
return array_combine($this->keys, parent::current());
}
public function getKeys()
{
return $this->keys;
}
}
The code is identical but the details that were encapsulated in the constructor are left out. This reduction allows to use the type more broadly, e.g. with (but not only with) the said SplFileObject:
$file = new SplFileObject('../data/test.csv');
$file->setFlags($file::READ_CSV);
$csv = new KeyedArrayIterator($file);
foreach ($csv as $line) {
var_dump($line);
}
If that now sounds too verbose, it again can be wrapped to give it again a nicer facade:
class CSVFile extends KeyedArrayIterator
{
/**
* #param string $file
*/
public function __construct($file)
{
parent::__construct(new SplFileObject($file));
$this->setFlags(SplFileObject::READ_CSV);
}
}
Thanks to the standard decorating-ability of TraversableIterator, the original constructor code from the first example of CSVFile could just be copied 100%.
This last addition also allows to keep the original code that uses the CSVFile Iterator intact:
$csv = new CSVFile('../data/test.csv');
foreach ($csv as $line) {
var_dump($line);
}
So just a quick refactoring to allow more code-reuse. You get a KeyedArrayIterator for free.
$csv_data = array_map('str_getcsv', file('Book.csv'));// reads the csv file in php array
$csv_header = $csv_data[0];//creates a copy of csv header array
unset($csv_data[0]);//removes the header from $csv_data since no longer needed
foreach($csv_data as $row){
$row = array_combine($csv_header, $row);// adds header to each row as key
var_dump($row);//do something here with each row
}
function processCsv($absolutePath)
{
$csv = array_map('str_getcsv', file($absolutePath));
$headers = $csv[0];
unset($csv[0]);
$rowsWithKeys = [];
foreach ($csv as $row) {
$newRow = [];
foreach ($headers as $k => $key) {
$newRow[$key] = $row[$k];
}
$rowsWithKeys[] = $newRow;
}
return $rowsWithKeys;
}
At this point I'm assuming you've already solved the issue but thought I'd throw in a suggested way around this, probably not the best/most elegant solution but it does the trick:
$row = 1;
$array = array();
$marray = array();
$handle = fopen('file.csv', 'r');
if ($handle !== FALSE) {
while (($data = fgetcsv($handle, 0, ',')) !== FALSE) {
if ($row === 1) {
$num = count($data);
for ($i = 0; $i < $num; $i++) {
array_push($array, $data[$i]);
}
}
else {
$c = 0;
foreach ($array as $key) {
$marray[$row - 1][$key] = $data[$c];
$c++;
}
}
$row++;
}
echo '<pre>';
print_r($marray);
echo '</pre>';
}
Try this
$csv = array_map("str_getcsv", file('file.csv', FILE_SKIP_EMPTY_LINES));
$header = array_shift($csv); // get header from array
foreach ($csv as $key => $value) {
$csv[$key] = array_combine($header, $value);
var_dump($csv[$key]['Model']);
}
var_dump($csv);
Try with this code:
$query = "SELECT * FROM datashep_AMS.COMPLETE_APPLICATIONS";
$export= mysql_query($query);
$first = true;
$temp = $export[0];
//echo "<pre>"; print_r($first); exit;
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename=file.csv');
header('Pragma: no-cache');
header("Expires: 0");
$outstream = fopen("php://output", "w");
foreach($export as $result)
{
if($first){
$titles = array();
foreach($temp as $key=>$val){
$titles[] = $key;
}
//print_r ($titles);exit;
fputcsv($outstream, $titles);
}
$first = false;
fputcsv($outstream, $result);
}
fclose($outstream);
Thanks
The array_combine() function only works if header colums match the data colums otherwise an error is thrown.
In the answer of Tim Cooper above, instead of
$all_rows = array();
$header = null;
while ($row = fgetcsv($file)) {
if ($header === null) {
$header = $row;
continue;
}
$all_rows[] = array_combine($header, $row);
}
I would code, in a more elegant and efficient way:
$rows = null;
$header = fgetcsv($file);
while ($row = fgetcsv($file)) {
$rows[] = array_combine($header, $row);
}