Chunk and excel Laravel - excel

guys.
I need help with this code. I need to download an excel with arround 550000 records. So i was wondering how can i use eloquent with chunk, skip and limit and get to make the excel or do something better than this. I started to making some code to save excel in a folder and then download in a zip, but i cant make works chunk and get.
I had problems with time execution and memory limit, but thats no more a problem.
This is my code.
$x=1;
for ($i=0; $i<=550000; $i=$i+15000) {
$personas="";
$personas = DB::table("cat_personas as c")->select("c.*","s.seccion","ca.casilla")
->leftJoin("cat_casillas as ca","c.cat_casilla_id","=","ca.id")
->join("cat_seccion as s","c.cat_seccion_id","=","s.id")
->where($filtros)
->whereRaw("c.id NOT IN ( SELECT cruzado FROM registro_persona )")
->whereRaw($whereCadena)
->orderby('c.consecutivo')
->orderby('c.apellido_paterno')
->orderby('c.apellido_materno')
->orderby('c.nombre')
->orderby('s.seccion')
->orderby('ca.casilla')
->skip($i)->limit(15000);
//$personas=$personas->get();
dd($personas->count());
if($personas->count()>0){
$spreadsheet = new Spreadsheet();
$r=1;
$sheet = $spreadsheet->getActiveSheet()
->setCellValue('A'.$r, '#')
->setCellValue('B'.$r, 'NOMBRE')
->setCellValue('C'.$r, 'APELLIDO PATERNO')
->setCellValue('D'.$r, 'APELLIDO MATERNO')
->setCellValue('E'.$r, 'SECCION')
->setCellValue('F'.$r, 'CASILLA')
->setCellValue('G'.$r, 'CONSECUTIVO');
$r++;
$personas->chunk(5000, function($personas) use (&$spreadsheet,&$r,&$sheet) {
$c=1;
//dd($personas->count());
foreach ($personas as $key) {
$sheet = $spreadsheet->getActiveSheet()
->setCellValue('A'.$r, $c)
->setCellValue('B'.$r, $key->nombre)
->setCellValue('C'.$r, $key->apellido_paterno)
->set`enter code here`CellValue('D'.$r, $key->apellido_materno)
->setCellValue('E'.$r, $key->seccion)
->setCellValue('F'.$r, $key->casilla)
->setCellValue('G'.$r, $key->consecutivo);
$r++;
$c++;
}
});
$writer = new Xlsx($spreadsheet);
//header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
//header('Content-Disposition: attachment; filename="personas.xlsx"');
$writer->save($path."/personas$x.xlsx");
$x++;
}
}

You can use chunk() this way.
$personas = DB::table("cat_personas as c")->select("c.*","s.seccion","ca.casilla")
->leftJoin("cat_casillas as ca","c.cat_casilla_id","=","ca.id")
->join("cat_seccion as s","c.cat_seccion_id","=","s.id")
->where($filtros)
->whereRaw("c.id NOT IN ( SELECT cruzado FROM registro_persona )")
->whereRaw($whereCadena)
->orderby('c.consecutivo')
->orderby('c.apellido_paterno')
->orderby('c.apellido_materno')
->orderby('c.nombre')
->orderby('s.seccion')
->orderby('ca.casilla');
$spreadsheet = new Spreadsheet();
$r = 1;
$sheet = $spreadsheet->getActiveSheet()
->setCellValue('A'.$r, '#')
->setCellValue('B'.$r, 'NOMBRE')
->setCellValue('C'.$r, 'APELLIDO PATERNO')
->setCellValue('D'.$r, 'APELLIDO MATERNO')
->setCellValue('E'.$r, 'SECCION')
->setCellValue('F'.$r, 'CASILLA')
->setCellValue('G'.$r, 'CONSECUTIVO');
$r++;
$c = 1;
$personas->chunk(500, function($personae) use ($spreadsheet, $r, $c) {
foreach ($personae as $persona) {
$spreadsheet->getActiveSheet()
->setCellValue('A'.$r, $c)
->setCellValue('B'.$r, $persona->nombre)
->setCellValue('C'.$r, $persona->apellido_paterno)
->setCellValue('D'.$r, $persona->apellido_materno)
->setCellValue('E'.$r, $persona->seccion)
->setCellValue('F'.$r, $persona->casilla)
->setCellValue('G'.$r, $persona->consecutivo);
$r++;
$c++;
}
});
$writer = new Xlsx($spreadsheet);
$writer->save($path . "/personas.xlsx");
There might be an issue with the total size of the spreadsheet, but if so, this should be a good start towards finding that solution.
PS - This is untested.

Related

Importing images along with other fields (username,subjectname) from excel in laravel

I am using phpspreadsheet. I want to import an excel sheet that have images too, it looks something like this,
I am able to retrieve fields separately and images separately, I want to get them together. Problem I am facing is that Images are being accessed with
$spreadsheet->getActiveSheet()->getDrawingCollection()
and for others field i have to access them like this
$spreadsheet->getRowIterator()
as both of them requires separate loops, should i be merging them into one or what is the right way so that i am able to retrieve both(images and fields) together.
Images retrieve code:
$spreadsheet = IOFactory::load($request->import_file);
$i = 0;
foreach ($spreadsheet->getActiveSheet()->getDrawingCollection() as $key => $drawing) {
if ($drawing instanceof MemoryDrawing) {
ob_start();
call_user_func(
$drawing->getRenderingFunction(),
$drawing->getImageResource()
);
$imageContents = ob_get_contents();
ob_end_clean();
switch ($drawing->getMimeType()) {
case MemoryDrawing::MIMETYPE_PNG :
$extension = 'png';
break;
case MemoryDrawing::MIMETYPE_GIF:
$extension = 'gif';
break;
case MemoryDrawing::MIMETYPE_JPEG :
$extension = 'jpg';
break;
}
} else {
$zipReader = fopen($drawing->getPath(), 'r');
$imageContents = '';
while (!feof($zipReader)) {
$imageContents .= fread($zipReader, 1024);
}
fclose($zipReader);
$extension = $drawing->getExtension();
}
$myFileName = time() .++$i. '.' . $extension;
$imagesCollection['answerImages_'.$key] =$myFileName;
file_put_contents('images/products/' . $myFileName, $imageContents);
$a = Answers::create([
'answerImages'=>$myFileName,
'questionId'=>($key <=4)?1:2,
]);
}
I want to store them into my table in database such that in questionImage column of database it has image name like this
and it is storing it currently but as I mentioned earlier i have to store them separtely
This is how i am storing other fields
$spreadsheet = IOFactory::load($the_file->getRealPath());
$sheet = $spreadsheet->getActiveSheet();
$row_limit = $sheet->getHighestDataRow();
$column_limit = $sheet->getHighestDataColumn();
$row_range = range( 1, $row_limit );
$column_range = range( 'F', $column_limit );
$startcount = 2;
$data = array();
foreach ( $row_range as $row ) {
$data[] = [
'courseName' =>$sheet->getCell( 'A' . $row )->getValue(),
'subjectName' => $sheet->getCell( 'B' . $row )->getValue(),
'question' => $sheet->getCell( 'C' . $row )->getValue(),
'questionImage' => $sheet->getCell( 'D' . $row )->getValue(),
];
$startcount++;
}
DB::table('questions')->insert($data);
How to get them together so that i can store them in one table
you should try maatwebsite/excel package. it will save your time.

How to Flatten / Recompile Excel Spreadsheet Using sheetjs or exceljs on Write

We use excel as a configuration file for clients. However, our processes only run on linux servers. We need to take a master file, update all the client workbooks with the new information, and commit to GitLab. The users then check it out, add their own changes, commit back to GitLab and a process promotes the workbook to Server A.
This process works great using nodeJS (exceljs)
Another process on a different server is using perl to pick up the workbook and then saves each sheet as a csv file.
The problem is, what gets written out is the data from the ORIGINAL worksheet and not the updated changes. This is true of both perl and nodejs. Code for perl and nodejs xlsx to csv is at the end of the post.
Modules Tried:
perl : Spreadsheet::ParseExcel; Spreadsheet::XLSX;
nodejs: node-xlsx, exceljs
I assume it has to do with Microsoft using XML inside the excel wrapper, it keeps the old version as history and since it was the original sheet name, it gets pulled instead of the updated latest version.
When I manually open in Excel, everything is correct with the new info as expected.
When I use "Save as..." instead of "Save" then the perl process is able to correctly write out the updated worksheet as csv. So our workaround is having the users always "Save as.." before committing their extra changes to GitLab. We'd like to rely on training, but the sheer number of users and clients makes trusting that the user will "Save AS..." is not practical.
Is there a way to replicate a "Save As..." during my promotion to Server A or at least be able to tell if the file had been saved correctly? I'd like to stick with excelJS, but I'll use whatever is necessary to replicate the "Save as..." which seems to recompile the workbook.
In addition to nodejs, I can use perl, python, ruby - whatever it takes - to make sure the csv creation process picks up the new changes.
Thanks for your time and help.
#!/usr/bin/env perl
use strict;
use warnings;
use Carp;
use Getopt::Long;
use Pod::Usage;
use File::Basename qw/fileparse/;
use File::Spec;
use Spreadsheet::ParseExcel;
use Spreadsheet::XLSX;
use Getopt::Std;
my %args = ();
my $help = undef;
GetOptions(
\%args,
'excel=s',
'sheet=s',
'man|help'=>\$help,
) or die pod2usage(1);
pod2usage(1) if $help;
pod2usage(-verbose=>2, exitstatus=>0, output=>\*STDOUT) unless $args{excel} || $args{sheet};
pod2usage(3) if $help;
pod2usage(-verbose=>2, exitstatus=>3, output=>\*STDOUT) unless $args{excel};
if (_getSuffix($args{excel}) eq ".xls") {
my $file = File::Spec->rel2abs($args{excel});
if (-e $file) {
print _XLS(file=>$file, sheet=>$args{sheet});
} else {
exit 1;
die "Error: Can not find excel file. Please check for exact excel file name and location. \nError: This Program is CASE SENSITIVE. \n";
}
}
elsif (_getSuffix($args{excel}) eq ".xlsx") {
my $file = File::Spec->rel2abs($args{excel});
if (-e $file) {
print _XLSX(file=>$file, sheet=>$args{sheet});
}
else {
exit 1;
die "\nError: Can not find excel file. Please check for exact excel file name and location. \nError: This Program is CASE SENSITIVE.\n";
}
}
else {
exit 5;
}
sub _XLS {
my %opts = (
file => undef,
sheet => undef,
#_,
);
my $aggregated = ();
my $parser = Spreadsheet::ParseExcel->new();
my $workbook = $parser->parse($opts{file});
if (!defined $workbook) {
exit 3;
croak "Error: Workbook not found";
}
foreach my $worksheet ($workbook->worksheet($opts{sheet})) {
if (!defined $worksheet) {
exit 2;
croak "\nError: Worksheet name doesn't exist in the Excel File. Please check the WorkSheet Name. \nError: This program is CASE SENSITIVE.\n\n";
}
my ($row_min, $row_max) = $worksheet->row_range();
my ($col_min, $col_max) = $worksheet->col_range();
foreach my $row ($row_min .. $row_max){
foreach my $col ($col_min .. $col_max){
my $cell = $worksheet->get_cell($row, $col);
if ($cell) {
$aggregated .= $cell->value().',';
}
else {
$aggregated .= ',';
}
}
$aggregated .= "\n";
}
}
return $aggregated;
}
sub _XLSX {
eval {
my %opts = (
file => undef,
sheet => undef,
#_,
);
my $aggregated_x = ();
my $excel = Spreadsheet::XLSX->new($opts{file});
foreach my $sheet ($excel->worksheet($opts{sheet})) {
if (!defined $sheet) {
exit 2;
croak "Error: WorkSheet not found";
}
if ( $sheet->{Name} eq $opts{sheet}) {
$sheet->{MaxRow} ||= $sheet->{MinRow};
foreach my $row ($sheet->{MinRow} .. $sheet->{MaxRow}) {
$sheet->{MaxCol} ||= $sheet->{MinCol};
foreach my $col ($sheet->{MinCol} .. $sheet->{MaxCol}) {
my $cell = $sheet->{Cells}->[$row]->[$col];
if ($cell) {
$aggregated_x .= $cell->{Val}.',';
}
else {
$aggregated_x .= ',';
}
}
$aggregated_x .= "\n";
}
}
}
return $aggregated_x;
}
};
if ($#) {
exit 3;
}
sub _getSuffix {
my $f = shift;
my ($basename, $dirname, $ext) = fileparse($f, qr/\.[^\.]*$/);
return $ext;
}
sub _convertlwr{
my $f = shift;
my ($basename, $dirname, $ext) = fileparse($f, qr/\.[^\.]*$/);
return $ext;
}
var xlsx = require('node-xlsx')
var fs = require('fs')
var obj = xlsx.parse(__dirname + '/test2.xlsx') // parses a file
var rows = []
var writeStr = ""
//looping through all sheets
for(var i = 0; i < obj.length; i++)
{
var sheet = obj[i]
//loop through all rows in the sheet
for(var j = 0; j < sheet['data'].length; j++)
{
//add the row to the rows array
rows.push(sheet['data'][j])
}
}
//creates the csv string to write it to a file
for(var i = 0; i < rows.length; i++)
{
writeStr += rows[i].join(",") + "\n"
}
//writes to a file, but you will presumably send the csv as a
//response instead
fs.writeFile(__dirname + "/test2.csv", writeStr, function(err) {
if(err) {
return console.log(err)
}
console.log("test.csv was saved in the current directory!")
The answer is its impossible. In order to update data inside a workbook that has excel functions, you must open it in Excel for the formulas to trigger. It's that simple.
You could pull the workbook apart, create your own javascript functions, run the data through it and then write it out, but there are so many possible issues that it is not recommended.
Perhaps one day Microsoft will release a linux Excel engine API for linux. But its still unlikely that such a thing would work via command line without invoking the GUI.

Excel::Writer::XLSX Unreadable content error

I just started using Perl and I am using
Excel::Writer::XLSX
to query a DB2 database and export the data to an .xlsx file. The data is about 250k rows.
The script is running fine, but when I try to open the Excel file it throws an error and asks to repair the file. Upon repairing some of the data gets replaced by inf.
Below is a snippet from my code.
while ( my $sqlStatement = ) {
$mSQL = $dbh->prepare( $sqlStatement )
or die "Can't prepare $sqlStatement";
$mSQL->execute()
or die "Can't execute $sqlStatement";
}
my $workbook = Excel::Writer::XLSX->new( $ARGV[2] );
$workbook->set_tempdir( '/tempDir/' );
$workbook->set_optimization();
my $worksheet = $workbook->add_worksheet();
$worksheet->keep_leading_zeros();
my $row = 0;
my $column = 0;
my #emptyRow = ();
$worksheet->write_row( $row++, $column, [ #{ $mSQL->{NAME_uc} } ] );
$worksheet->write_row( $row++, $column, [ #emptyRow ] );
while ( my #Row = $mSQL->fetchrow_array ) {
$worksheet->write_row( $row++, $column, [ #Row ] ); #, $cellFormat);
$count++;
}
$workbook->close();
Can someone please advise me on this issue?
Finally i figured it out (Thanks to John McNamara). This was resolved by adding a write handler that uses regular expressions to check if a particular token is being converted to "inf", and if it does, it invokes the write_string subroutine instead of write_row.
Below is the code.
#!/usr/bin/perl
use strict;
use warnings;
use Excel::Writer::XLSX;
my $workbook = Excel::Writer::XLSX->new( 'write_handler5.xlsx' );
my $worksheet = $workbook->add_worksheet();
# Add a handler to match any numbers in order to check for and handle
# infinity.
$worksheet->add_write_handler( qr[\d], \&write_with_infinity );
# The following function is used by write() to pre-process any the data when a
# match is found. If it finds something that looks like a number but evaluates
# to infinity it write it as a string.
sub write_with_infinity {
my $worksheet = shift;
my #args = #_;
my $token = $args[2];
# Check if token looks like a number, in the same way as write().
if ( $token =~ /^([+-]?)(?=[0-9]|\.[0-9])[0-9]*(\.[0-9]*)?([Ee]([+-]?[0-9]+))?$/ ) {
# Check for infinity.
$token = $token + 0;
if ($token =~ /inf/) {
# Write the value as a string instead of a number.
return $worksheet->write_string( #args );
}
}
# Reject the match and return control to write()
return undef;
}

Joomla Exporting Or Downloading Reports To CSV

I have the following code in my inventory section when i run this code it save an excel file but the result showing the whole html file not my data.
<?php
$csv = NULL;
$arr = array("product_name","product_sku","product_in_stock","virtuemart_product_id","product_price_display","product_instock_value");
$csv = "Product Name, Product SKU, In Stock, Booked ordered products, Cost Price, Stock Value \n";
$c=0;
while(list($key,$value)=each($arr)){
$c++;
$cc=1;
foreach ($this->inventorylist as $key => $product){
$cc++;
$csv .= join(',',array($product->product_name.",".$product->product_sku.",".$product->product_in_stock.",".$product->virtuemart_product_id.",".$product->product_price_display.",".$product->product_instock_value))." \n";
}
}
JResponse::clearHeaders();
JResponse::setHeader('Content-Type', 'application/vnd.ms-excel', true);
JResponse::setHeader('Content-Disposition', 'csv; filename=inventory_report.csv; size='.strlen($csv), true);
JResponse::sendHeaders();
Any help will be appreciated.
Thanks
Khalique
Here's the brute force solution just to get you going:
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=file.csv");
header("Pragma: no-cache");
header("Expires: 0");
// Echo the csv content.
echo "foo,bar\n1,2";
// Close the application gracefully.
JFactory::getApplication()->close();
A slightly cleaner way without using views, but including &format=raw in the URL is:
$app = JFactory::getApplication();
$doc = JFactory::getDocument();
$doc->setMimeEncoding('application/vnd.ms-excel');
$app->setHeader(
'Content-disposition',
'attachment; filename="inventory_report.csv"',
true
);
echo "foo,bar\n1,2";
But the best way is to use a download view based on the raw document type. You can find an example of this in com_banners:
Controller: https://github.com/joomla/joomla-cms/blob/staging/administrator/components/com_banners/controllers/tracks.raw.php
View: https://github.com/joomla/joomla-cms/blob/staging/administrator/components/com_banners/views/tracks/view.raw.php
You might also gain some extra insight from this discussion https://groups.google.com/forum/#!topic/joomla-dev-general/dRa_39AGDBY

Webforms in excel instead of e-mail

A client of mine asked me if i can find a solution for this problem.
His website (still a WIP) http://welkommagazine.nl/luuk/ has a form. The form obviously uses a sendmail script to send the form to e-mail. From thereon he manually copy/pastes all the submissions to excel.
What he wants is that the forms online automaticcaly are added to an excel document to save him a lot of work.
Now i am not a programmer, but a designer.. I think this can be done, but i have absolutely no clue how. I googled alot for it and the only thing i found was a dreamweaverplugin.
Is there a way to do this, if so, how?
Not a programmer's response, but...
I think an easy solution is to use Google docs. You can set-up a Google Spreadsheet and associate a form to it. Whenever a user fills the form , his data is added to the spreadsheet.
Your client may download that anytime.
There are some other providers on the market, some free, some not. E.g: wufoo.com
Found the answer myself. I wrote a PHP code snippet which actually stores the fields comma seperated in a CSV file and sends an email to a desired adress with the filled in fields.
if(isset($_POST['Submit'])){
$pakket = $_POST['pakket'];
$extragidsen = $_POST['extragidsen'];
$naambedrijf = $_POST['naambedrijf'];
$err = '';
if(trim($pakket)==''){
$err .= '-Please enter a name';
}
if(empty($extragidsen)){
$err .= '-Please enter an email address';
}
if(strlen($naambedrijf)==0){
$err .= '-Please enter a comment';
}
if($err!=''){
echo $err;
}
else{
$filename = 'file.csv';
$somecontent = $pakket . ',' . $extragidsen . ',' . $naambedrijf . "\n";
// Let's make sure the file exists and is writable first.
if (is_writable($filename)) {
// In our example we're opening $filename in append mode.
// The file pointer is at the bottom of the file hence
// that's where $somecontent will go when we fwrite() it.
if (!$handle = fopen($filename, 'a')) {
echo "Cannot open file ($filename)";
exit;
}
// Write $somecontent to our opened file.
if (fwrite($handle, $somecontent) === FALSE) {
echo "Cannot write to file ($filename)";
exit;
}
//--------------------------Set these paramaters--------------------------
// Subject of email sent to you.
$subject = 'Inschrijving welkom';
// Your email address. This is where the form information will be sent.
$emailadd = 'luuk#luukratief.com';
// Where to redirect after form is processed.
$url = 'http://www.google.com';
// Makes all fields required. If set to '1' no field can not be empty. If set to '0' any or all fields can be empty.
$req = '0';
// --------------------------Do not edit below this line--------------------------
$text = "Results from form:\n\n";
$space = ' ';
$line = '
';
foreach ($_POST as $key => $value)
{
if ($req == '1')
{
if ($value == '')
{echo "$key is empty";die;}
}
$j = strlen($key);
if ($j >= 20)
{echo "Name of form element $key cannot be longer than 20 characters";die;}
$j = 20 - $j;
for ($i = 1; $i ';
fclose($handle);
} else {
echo "The file $filename is not writable";
}
}
}
Maybe the code aint that clean as it can be, but eh it works.
Feel free to clean up the code if you want to :)
I guessed I would answer this myself for the community...
BTW u need to set "write" rights to "file.csv"
cheers

Resources