|

Large CSV Export

Sometimes you need to export a large amount of data from your database.  Obviously, if you are going to accumulate all data in an array and then write it to a csv file, you will eventually run out of memory.  A better solution is to use streams.  Here is how you can export data from database into a csv file using Eloquent.

First we open a file to write to and query orders from database chunking them in 500s. Using callback function, we write those records to csv file. Headers array contains key – value pairs of the column names and the way we want the to appear in csv. It acts as a filter for the columns we want to export.

$tmpFile = fopen('tmp_export_'. time() .'.csv', w+);
$orders = $query->with('orderItems', 'fulfillments')->chunk(500, function ($orders) use (&$tmpFile) {
	$ordersAndStuff=$this->getItems($orders);
	\Log::info("Writing to csv file ".$orders->count()." order batch");
	foreach($ordersAndStuff as $order){
	$dataArray=[];
	foreach($this->getHeadersArray() as $key=>$value){
	$dataArray[]=$order->$key;
	}
	fputcsv($tmpFile, $dataArray);
	}
});
rewind($tmpFile);

After we have written to the resource file, we rewind the resource, because now we want to push it to Amazon (or other) cloud storage. This is why initially we opened the file for both reading and writing.

//try catch destroy pending record return null.
try{
	$updatedRecord=$this->saveToStorage($tmpFile, $pendingRecord);
        $message = "Records were exported successfully";
}catch(\Exception$e){
	$pendingRecord->destroy();
	\Log::info($e->getMessage());
        $message = "Error exporting records";
}

protected function saveToStorage(&$resource, $pendingRecord)
{
	$csvHandler=new CsvHandler;
	$record=$csvHandler->save($resource,$this->accountId,'orders',$this->request, $pendingRecord);
	return $record;
}

Earlier in the code (this is not necessary) an export record was created with the status pending. If an operation of saving file to a remote storage fails, we simply erase pending record (or we can give it status ‘failed’). CsvHandler class takes care of saving our export file to AWS. It uses stream to do so. That is why we are passing the resource we created by reference.

$exportMessage=new \stdClass;
$exportMessage->entity='orders';
$exportMessage->accountId=$this->accountId;
$exportMessage->message = $message;
event(new ExportComplete($exportMessage));
fclose($tmpFile);
unlink($tmpFilePath);
return $exportMessage;

The above part of code is doing clean up by creating an export message object. Broadcasting export complete event and returning export message class.

Share this article

Similar Posts

  • Retrying and Logging Requests with Guzzle

    When consuming 3d party API, you may want to do two things: In this article we will look at how to implement the above features using Guzzle, a popular PHP library for making API calls. Let us scaffold our app composer init composer require guzzlehttp/guzzle:~6.0 composer require monolog/monolog composer require –dev phpunit/phpunit:^8 We will be…

    Share this article
  • Upload to FTP with PHP

    $fp = fopen(‘https://www.example.com/pdfdoc’, ‘r’); $user = “sammy”; $pass = “password”; $ftp_server = “192.168.10.10”; //should be wrapped in try catch to properly handle errors $ftp_conn = ftp_ssl_connect($ftp_server); $login = ftp_login($ftp_conn, $user, $pass); ftp_chdir($ftp_conn, ‘path/to/folder’); //can also use ftp_pwd ftp_pasv($ftp_conn, true); //passive mode ftp_fput($ftp_conn, “mydocument.pdf”, $fp, FTP_BINARY); fclose($fp); ftp_close($ftp_conn); Above code can be used to upload a…

    Share this article
  • Complex Eloquent Query

    A query below selects products that need to be updated in a remote application. It takes quantities of products in host application that are connected to source products application. On top of that it looks at all the orders that are in “Pending” status and reserves quantities for those products. SELECT products.quantity_available, connector_products.stock_id, products.id, connector_products.sku,…

    Share this article
  • Using Local and Public Disks

    Having vagrant run on Windows host machine may cause some problems with symbolic links. After trying to make symbolic links to work and failing, I decided to use public disk in development and local disk in production. php artisan storage:link makes symbolic link from “public/storage” to “storage/app/public”.  When developing on windows-vagrant this command will not work properly,…

    Share this article
  • API Client Design

    When you extensively work with certain APIs, like Shopify’s for example, you will end up with bunch of functions that map to API’s endpoints. One of the approaches I have seen so far is to create an API class ShopifyApi and make those functions class methods. So it looks something like the figure below. I…

    Share this article