egyg33k Bundle , generating csv from an ajax call - php

I'm trying to generate a csv file with egyg33k bundle which I'm using in my symfony project.
I have a twig which contains two date picker inputs and a button. When I click on the button I get the values of the two date pickers and pass them to the php action to use them on a query builder (all of this works), but the file doesn't start downloading. Instead I get the content at the browser console (see photo)
moreover the action was working before using ajax.
ajax call:
$(document).ready(function() {
$("#get_extract").on("click", function() {
$.ajax({
method: "POST",
url: "/back-office/extraction",
data: {
begin_date: $("#begin_date").val(),
end_date: $("#end_date").val()
}
})
});
});
PHP:
public function getExtractRecordsAction(Request $request)
{
if ($request->isXmlHttpRequest() || $request->query->get('showJson') == 1) {
$beginDate = $request->get('begin_date');
$endDate = $request->get('end_date');
$recordsRepository = $this->getDoctrine()->getRepository(Record::class);
$query = $recordsRepository->createQueryBuilder('r')
->where('r.createdAt BETWEEN :beginDate AND :endDate')
->setParameter('beginDate', $beginDate)
->setParameter('endDate', $endDate)
->getQuery();
$records = $query->getResult();
$writer = $this->container->get('egyg33k.csv.writer');
$csv = $writer::createFromFileObject(new \SplTempFileObject());
$csv->insertOne([
'ID',
'FIRST_NAME',
'LAST_NAME',
'CIVILITY',
'PHONE_NUMBER',
'EMAIL',
'ZIP_CODE',
'OPTIN',
]);
foreach ($records as $record) {
$csv->insertOne([
$record->getId(),
$record->getPersonalDetails()->getFirstName(),
$record->getPersonalDetails()->getLastName(),
$record->getPersonalDetails()->getCivility(),
$record->getPersonalDetails()->getPhoneNumber(),
$record->getPersonalDetails()->getEmail(),
$record->getPersonalDetails()->getZipCode(),
$record->getPersonalDetails()->getOptin(),
]);
}
$csv->output('EXAMPLE.csv');
exit();
}
}

Ciao, please extend your ajax call, it just download the data you need to force the download of the file. Actually the ajax call transfer the data from remote to local browser so you just need a way to let the browser putting everything inside a file and starting the local download (from browser to Download folder)
$("#get_extract").on("click", function() {
$.ajax({
method: "POST",
url: "/back-office/extraction",
data: {
begin_date: $("#begin_date").val(),
end_date: $("#end_date").val()
},
success: function(csv_content) {
let filename = 'data.csv';
let csvFile = new Blob([csv_content], {type: "text/csv"});
let downloadLink = document.createElement("a");
downloadLink.download = filename;
downloadLink.href = window.URL.createObjectURL(csvFile);
document.body.appendChild(downloadLink);
downloadLink.click();
}
})

Related

Laravel How to create and download PDF from view on same route

I have a laravel-application where I want to generate a PDF from the values, the user has entered in some input fields. So when the user has entered the data, he clicks a button which generates the PDF and downloads it immediately afterwards automatically. All this should happen on the same route/view. The PDF should not be stored somewhere.
So right now, when I click the button, the entered Data gets through, e.g. stored in the DB, and it seems that a PDF is created, but I can't see or find it, and my browser does not inform me that there is a PDF available for download.
Before I started, I installed the laravel-dompdf-plugin, and followed the instructions.
So my route look like this
Route::view('formpage', 'app.statement')->name('statement'); // The blade view with the Form
Route::post('statement', 'MyController#generatePDF')->name('generatePDF'); // this is where I post the form
This is my controller
use PDF;
class MyController extends Controller {
public function generatePDF(Request $request){
$statement = Statement::create([
'name' => $validated['name'],
'email' => $validated['email'],
'phone' => $validated['phone'],
'declaration_date' => $validated['declaration_date'],
]);
$pdf = PDF::loadView('pdf.statement', $statement);
return $pdf->download('File__'.$statement->name.'.pdf');
}
}
I posting the form with javascript by using axios by simply doing this:
$('#submitBtn').click(function(e) {
const formData = new FormData();
formData.append(
"name",
$("#statement")
.find('input[name="name"]')
.val()
);
...etc with all other fields
axios.post($("#statement form").attr("action"), formData)
.then(response => {
$('#submitBtn')
.attr("disabled", "disabled")
.addClass("disabled")
.html('<i class="fas fa-fw fa-check"></i> Success'); */
$("#statement form")[0].reset();
})
.catch(error => {
console.log("ERR: ", error); // DEBUG
$("#statement .text-danger").show();
$('#sworn-statement button[type="submit"]')
.removeAttr("disabled")
.removeClass("disabled")
.html("Send");
});
}
What am I doing wrong?
UPDATE
I tried to do this:
const FileDownload = require("js-file-download");
axios.post($("#statement form").attr("action"), formData)
.then(response => {
FileDownload(response.data,"File.pdf");
}).catch(error => {
console.log('error:', error);
});
which gives me a blank page.
So as I said in the comments your problem is that the file is in the response you get from the axios POST request. If you don't handle the filedownload after you get the response nothing will happen.
You can use the js-file-download module. After you've installed this module you can modify your code to something like this:
const FileDownload = require('js-file-download');
axios.get(YOUR_URL)
.then((response) => {
FileDownload(response.data, YOUR_FILE_NAME);
});
There's also an another solution with JQuery which I got from that answer:
$.ajax({
type: "POST",
url: url,
data: params,
success: function(response, status, xhr) {
// check for a filename
var filename = "";
var disposition = xhr.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
}
var type = xhr.getResponseHeader('Content-Type');
var blob = new Blob([response], { type: type });
if (typeof window.navigator.msSaveBlob !== 'undefined') {
// IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
window.navigator.msSaveBlob(blob, filename);
} else {
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
if (filename) {
// use HTML5 a[download] attribute to specify filename
var a = document.createElement("a");
// safari doesn't support this yet
if (typeof a.download === 'undefined') {
window.location = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
}
} else {
window.location = downloadUrl;
}
setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
}
}
});
Just replace the url and params attributes with your stuff. This creates also a POST request and handles the incomming PDF file as filedownload after the response arrives.

Angular 6 CSV download

I'm new to angular, currently i'm working in a project which needs an csv export. Here i'm using Angular 6 as frontend and laravel as backend
This is how i wrote laravel function using mattwebsite/excel
// Lead export to csv
public function downloadExcel(Request $request)
{
$credentials = $request->only('token');
$token = $credentials['token'];
$userid = $this->getUseridFromToken($token);
$type = "xls";
$data = DB::table('user_mailbox AS A')
->select('A.id', 'A.name', 'A.email', 'A.phone', DB::raw('DATE_FORMAT(A.send_on, "%d / %b / %Y") as send_on'), 'B.listing_heading','B.listing_id','B.listing_heading', 'C.name')
->leftjoin('broker_listing AS B', 'B.listing_id', '=', 'A.listing_id')
->leftjoin('users AS C', 'C.id', '=', 'A.sent_by')
->where('A.sent_to', $userid)
->where('A.user_type', '1')
->orderBy('A.id', 'desc')->get()->toArray();
Excel::create('Lead_Export', function($excel) use ($data) {
$excel->sheet('Lead_Export', function($sheet) use ($data)
{
$sheet->fromArray($data);
});
})->download($type);
}
This is how i wrote function in angular component
// Download leads as excel
download_excel(){
const fd = new FormData();
fd.append('token',this.token);
this.brokerleads.downloadLeads(fd).subscribe(
data => this.handleResponsedwnload(data),
error => this.handleErrordwnload(error)
);
}
handleResponsedwnload(data){ console.log('test');
const blob = new Blob([data], { type: 'text/xls' });
const url= window.URL.createObjectURL(blob);
window.open(url);
}
handleErrordwnload(data){
}
service is like this
// Download as excel
downloadLeads(data):Observable<any>{
return this.http.post(`${this.baseUrl}downloadExcel`, data);
}
view
<a class="export-leads" href="javascript:void(0);" (click)="download_excel()" >EXPORT LEADS</a>
while doing this i'm getting response like this but file is not downloading
You need to navigate the browser to the route where the Excel file is made on the backend (in a new tab) either with a link <a href="path" target="_blank"> or with window.open
The ->download() function sets headers so that the file will be automatically downloaded.
When you fetch this data with an AJAX call (which is what HttpClient does) you simply get the binary data returned (which is what you see in your Response tab in Chrome developer tools).
(There are front-end hacks to download a file retrieved by ajax such as creating a link element and clicking it with JavaScript (see below), but they can not be recommended):
let fileName = 'filename.xlsx';
let a = document.createElement('a');
a.href = window.URL.createObjectUrl(responseData);
a.download = fileName;
a.click();
This can also be done using file-saver:
import * as FileSaver from 'file-saver';
this.http.post(`${this.baseUrl}downloadExcel`, data, { responseType: 'blob' })
.subscribe((resp: any) => {
saveAs(resp, `filename.csv`)
});
This function working for me to export csv,
downloadFile(data: any) {
const replacer = (key, value) => value === null ? '' : value; // specify how you want to handle null values here
const header = Object.keys(data[0]);
let csv = data.map(row => header.map(fieldName => JSON.stringify(row[fieldName], replacer)).join(','));
csv.unshift(header.join(','));
let csvArray = csv.join('\r\n');
var a = document.createElement('a');
var blob = new Blob([csvArray], {type: 'text/csv' }),
url = window.URL.createObjectURL(blob);
a.href = url;
a.download = "myFile.csv";
a.click();
window.URL.revokeObjectURL(url);
a.remove();
}

How to update the data when click the button using ajax in Laravel?

I am using Laravel 5.4 and I have Check Out button in my table. I want when I click that button, it will update check out time in the database and update my table. I am trying to use ajax, but it is not working, but sometimes it is working too but not reload the table so I need to refresh the page manualy. Here is my button code:
<a type="button" name="checkout_btn" id="checkout_btn" data-id=" {{ $Data->VST_ID }}" class="btn btn-primary">Check Out</a>
This is my ajax code:
$('#checkout_btn').click(function addseries(e){
var VST_ID = $(e.currentTarget).attr('data-id');
alert("dfads");
$.ajax({
type: "GET",
url: 'visitor/checkout',
data: "VST_ID="+VST_ID,
success: function(data) {
console.log(data);
}
});
});
Here is my controller code:
public function Checkout(Request $request)
{
$visitor = Visitor::where("VST_ID",$request['VST_ID'])->first();
$visitor->VST_CHECKOUT = date('Y-m-d H:i:s');
$visitor->UPDATED_AT = date('Y-m-d H:i:s');
$visitor->UPDATED_BY = 'User';
$visitor->save();
return redirect()->route('Visitor.VList')->with('message','The Visitor has been Checked Out !');
}
You can do this by two methods.
1st: by making different view for table body.
In controller set view as below Example
if($request->ajax())
{
$records=\View::make('admin.settings.state.state-holiday-tbody',['contents'=>$contents,'active'=>$active,'tab'=>$tab])->render();
return response()->json(array('html'=>$records));
}
and then perform for loop to append data in table body.
Note: call function to get all table records on document ready.
2nd: Make table body using javascript for loop by getting all records in response.
I think if you want to update the data, the method is PUT and why are you using GET method?
$('#checkout_btn').click(function addseries(e){
var VST_ID = $(e.currentTarget).attr('data-id');
$.ajax({
type: "PUT",
url: 'visitor/checkout' + VST_ID,
success: function(data) {
console.log(data);
}
});
});
change your controller
public function Checkout(Request $request, $id)
{
$visitor = Visitor::find($id);
$visitor->VST_CHECKOUT = date('Y-m-d H:i:s');
$visitor->UPDATED_AT = date('Y-m-d H:i:s');
$visitor->UPDATED_BY = 'User';
$visitor->save();
return redirect()->route('Visitor.VList')->with('message','The Visitor has been Checked Out !');
}
and change your route.php
Route::put('visitor/checkout/{id}', 'visitorController#Checkout');

Export CSV using Laravel via Ajax

I have a export csv function, it worked fine with laravel. But now I want to call export function via ajax and use method post, but I there is no respone. I can send a variable from laravel controller to response, but can not send a file download.
Here is my code :
route.php
Route::get('/title/show', 'TitleController#show');
Route::post('/title/show', 'TitleController#exportFromDB');
show.blade.php
<script>
$(document).ready(function () {
$('#exportFromDB').click(function () {
$.ajax({
url: "",
type: "post",
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
data: {},
success: function (response) {
var a = document.createElement("a");
a.href = response.file;
a.download = response.name;
}
})
})
})
TitleController.php:
$dataExport['Oversea'] = $dataOversea;
$this->titleRepository->export('csv', $properties, $dataExport);
TitleRepository.php
public function export($type, $properties, $data)
{
if (in_array($type, self::EXPORT_TYPE)) {
try {
return Excel::create($properties['_title'], function ($excel) use ($data, $properties) {
$excel->setTitle($properties['_title']);
$excel->setCreator($properties['_creator'])
->setCompany($properties['_company']);
$excel->setDescription($properties['_description']);
$excel->sheet('Sheet', function ($sheet) use ($data) {
foreach ($data as $item) {
$sheet->fromArray($item);
}
});
})->export($type);
} catch (Exception $error) {
throw $error;
}
}
}
How can I fix them ? Thank !
Try this -
Don't write the code for export in your controller method, instead just save the excel file in your public folder.
Your controller method should return the filename.
On your ajax success do this -
location.href = path/to/file/property_title.xls
So replace your this line
->export($type);
with
->store($type, 'public/reports/', true);
I see your ajax url null value
Change it to
url : "{{ action('TitleController#exportFromDB') }}"
After that, response is data you return in controller
success: function (response) {}
I installed the maatwebsite/excel package and was able to do it without writing any javascript. All you need to do is setup a link (or typical form post if you prefer) to an action like so:
public downloadItemsExcel() {
$items = Item::all();
Excel::create('items', function($excel) use($items) {
$excel->sheet('ExportFile', function($sheet) use($items) {
$sheet->fromArray($items);
});
})->export('xls');
}
This works for csv/excel files alike. There is no reload of a page in the browser.
First you have to change POST method to GET.
For ajax you can do it like this:
$(document).ready(function () {
$('#exportFromDB').click(function () {
$.get('/title/show, function(data){
window.location = '/title/show=' + data;
});
})
})

Search all entities Symfony2

I'm working on a webapplication in Symfony2. At the moment I have several pages that include a search form where you can search for specific entities that belong to that page.
For example; I have a client page with an overview of client information. Here you can search for clients with a name like your search value. Thats no rocket science I guess.
At the front page I want to somehow search all my entities at once. I was thinking about combining the searches I already have, or maybe there is a function in Symfony that allows this?
Here's some of my code for the search(es) I have so far:
Live search action for clients:
public function liveSearchAction(Request $request)
{
$string = $this->getRequest()->request->get('sQuery');
$clients = $this->getDoctrine()
->getRepository('clientsBundle:client')
->findByLetters($string);
$response = new JsonResponse(array('clients' => $clients));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
The repository function findByLetters:
public function findByLetters($string){
$query = $this->getEntityManager()
->createQuery(
'SELECT c FROM clientsBundle:client c
WHERE c.name LIKE :string'
)->setParameter('string', '%'.$string.'%');
$result = $query->getArrayResult();
return $result;
}
The AJAX call for returning searchresults
(function($, Handlebars, window, document, undefined) {
var that = this;
var oXHR;
var source = $("#searchResult").html();
var template = Handlebars.compile(source);
var action = $('#quickSearch').data('action');
var route = $('#quickSearch').data('route');
Handlebars.registerHelper('url', function(options) {
console.log(this, options);
return new Handlebars.SafeString(
Routing.generate(route, {'id': this.id})
);
});
$('#quickSearch').on('input',function() {
var $this = $(this);
var searchText = $this.val();
console.log('searching for: ' + searchText);
if (typeof oXHR !== 'undefined') {
oXHR.abort();
}
oXHR = $.ajax({
type: "POST",
url: action,
dataType: "json",
data: {
sQuery : searchText
},
success: function(response)
{
var html = template(response);
// console.log(html);
$('#list .list-group').html(html);
},
error: function(failresponse)
{
console.log( failresponse );
}
});
});
}).call(window.Test = window.Test || {}, jQuery, Handlebars, window, document);
As you might have noticed, the return of the AJAX call gets handled by handlebars.

Categories