Laravel: Export customer record as CSV











up vote
-1
down vote

favorite












Using Laravel, I need to export some value from the DB in a CSV and then upload the CSV in a sftp or return it in a Response.



I'm trying to be SOLID but with this scenario, I'm not sure how to proceed. As I understand I should have one class that handle the CSV, one that handle the sFTP, one for the Response, and one to handle the logic of the model (the mapping in my case). But I don't understand how I can separate them.



Later one, I will have more Model to export.



<?php

namespace AppServices;

use AppLine;
use SymfonyComponentHttpFoundationStreamedResponse;
use Storage;

class LinesCsv
{
const DOCUMENT_TYPE = 20;
const DELIMITER = ';';

public function exportCSVFileToSftp($filename = 'export.csv')
{
$handle = fopen('php://temp', 'w');
$handle = $this->buildCsv($handle);
return Storage::disk('sftp')->put($filename, $handle);
}

public function exportCSVFileToResponse($filename = 'export.csv')
{
return new StreamedResponse(function () use ($filename) {
$handle = fopen('php://output', 'w');
$handle = $this->buildCsv($handle);
fclose($handle);
}, 200, [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
]);
}


public function buildCsv($handle, $header = false)
{
if ($header) {
fputcsv(
$handle,
array_keys($this->lineMapping(Line::first())),
self::DELIMITER
);
}

Line::with(['invoice', 'invoice.customer', 'item'])
->whereHas('invoice', function ($query) {
$query->where('is_exportable', 1);
})
->chunk(200, function ($lines) use ($handle) {
foreach ($lines as $line) {
fputcsv(
$handle,
$this->lineMapping($line),
self::DELIMITER
);
}
});

return $handle;
}

protected function lineMapping(Line $line)
{
return [
'Invoice number' => $line->invoice->id,
'Document type' => self::DOCUMENT_TYPE,
'Date' => $line->invoice->date,
];
}
}









share|improve this question









New contributor




cbaconnier is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.













This question has an open bounty worth +50
reputation from cbaconnier ending in 5 days.


This question has not received enough attention.
















  • The current question title, which states your concerns about the code, is too general to be useful here. Please edit to the site standard, which is for the title to simply state the task accomplished by the code. Please see How to get the best value out of Code Review: Asking Questions for guidance on writing good question titles.
    – Toby Speight
    Nov 15 at 10:33










  • @TobySpeight I did the best I could. Perhaps, could you suggest me a better title?
    – cbaconnier
    Nov 15 at 10:40










  • It's hard to advise when the description is also quite vague, but something like "Export customer record as CSV" might work?
    – Toby Speight
    Nov 15 at 10:43












  • @TobySpeight Thanks
    – cbaconnier
    Nov 15 at 10:46















up vote
-1
down vote

favorite












Using Laravel, I need to export some value from the DB in a CSV and then upload the CSV in a sftp or return it in a Response.



I'm trying to be SOLID but with this scenario, I'm not sure how to proceed. As I understand I should have one class that handle the CSV, one that handle the sFTP, one for the Response, and one to handle the logic of the model (the mapping in my case). But I don't understand how I can separate them.



Later one, I will have more Model to export.



<?php

namespace AppServices;

use AppLine;
use SymfonyComponentHttpFoundationStreamedResponse;
use Storage;

class LinesCsv
{
const DOCUMENT_TYPE = 20;
const DELIMITER = ';';

public function exportCSVFileToSftp($filename = 'export.csv')
{
$handle = fopen('php://temp', 'w');
$handle = $this->buildCsv($handle);
return Storage::disk('sftp')->put($filename, $handle);
}

public function exportCSVFileToResponse($filename = 'export.csv')
{
return new StreamedResponse(function () use ($filename) {
$handle = fopen('php://output', 'w');
$handle = $this->buildCsv($handle);
fclose($handle);
}, 200, [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
]);
}


public function buildCsv($handle, $header = false)
{
if ($header) {
fputcsv(
$handle,
array_keys($this->lineMapping(Line::first())),
self::DELIMITER
);
}

Line::with(['invoice', 'invoice.customer', 'item'])
->whereHas('invoice', function ($query) {
$query->where('is_exportable', 1);
})
->chunk(200, function ($lines) use ($handle) {
foreach ($lines as $line) {
fputcsv(
$handle,
$this->lineMapping($line),
self::DELIMITER
);
}
});

return $handle;
}

protected function lineMapping(Line $line)
{
return [
'Invoice number' => $line->invoice->id,
'Document type' => self::DOCUMENT_TYPE,
'Date' => $line->invoice->date,
];
}
}









share|improve this question









New contributor




cbaconnier is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.













This question has an open bounty worth +50
reputation from cbaconnier ending in 5 days.


This question has not received enough attention.
















  • The current question title, which states your concerns about the code, is too general to be useful here. Please edit to the site standard, which is for the title to simply state the task accomplished by the code. Please see How to get the best value out of Code Review: Asking Questions for guidance on writing good question titles.
    – Toby Speight
    Nov 15 at 10:33










  • @TobySpeight I did the best I could. Perhaps, could you suggest me a better title?
    – cbaconnier
    Nov 15 at 10:40










  • It's hard to advise when the description is also quite vague, but something like "Export customer record as CSV" might work?
    – Toby Speight
    Nov 15 at 10:43












  • @TobySpeight Thanks
    – cbaconnier
    Nov 15 at 10:46













up vote
-1
down vote

favorite









up vote
-1
down vote

favorite











Using Laravel, I need to export some value from the DB in a CSV and then upload the CSV in a sftp or return it in a Response.



I'm trying to be SOLID but with this scenario, I'm not sure how to proceed. As I understand I should have one class that handle the CSV, one that handle the sFTP, one for the Response, and one to handle the logic of the model (the mapping in my case). But I don't understand how I can separate them.



Later one, I will have more Model to export.



<?php

namespace AppServices;

use AppLine;
use SymfonyComponentHttpFoundationStreamedResponse;
use Storage;

class LinesCsv
{
const DOCUMENT_TYPE = 20;
const DELIMITER = ';';

public function exportCSVFileToSftp($filename = 'export.csv')
{
$handle = fopen('php://temp', 'w');
$handle = $this->buildCsv($handle);
return Storage::disk('sftp')->put($filename, $handle);
}

public function exportCSVFileToResponse($filename = 'export.csv')
{
return new StreamedResponse(function () use ($filename) {
$handle = fopen('php://output', 'w');
$handle = $this->buildCsv($handle);
fclose($handle);
}, 200, [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
]);
}


public function buildCsv($handle, $header = false)
{
if ($header) {
fputcsv(
$handle,
array_keys($this->lineMapping(Line::first())),
self::DELIMITER
);
}

Line::with(['invoice', 'invoice.customer', 'item'])
->whereHas('invoice', function ($query) {
$query->where('is_exportable', 1);
})
->chunk(200, function ($lines) use ($handle) {
foreach ($lines as $line) {
fputcsv(
$handle,
$this->lineMapping($line),
self::DELIMITER
);
}
});

return $handle;
}

protected function lineMapping(Line $line)
{
return [
'Invoice number' => $line->invoice->id,
'Document type' => self::DOCUMENT_TYPE,
'Date' => $line->invoice->date,
];
}
}









share|improve this question









New contributor




cbaconnier is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











Using Laravel, I need to export some value from the DB in a CSV and then upload the CSV in a sftp or return it in a Response.



I'm trying to be SOLID but with this scenario, I'm not sure how to proceed. As I understand I should have one class that handle the CSV, one that handle the sFTP, one for the Response, and one to handle the logic of the model (the mapping in my case). But I don't understand how I can separate them.



Later one, I will have more Model to export.



<?php

namespace AppServices;

use AppLine;
use SymfonyComponentHttpFoundationStreamedResponse;
use Storage;

class LinesCsv
{
const DOCUMENT_TYPE = 20;
const DELIMITER = ';';

public function exportCSVFileToSftp($filename = 'export.csv')
{
$handle = fopen('php://temp', 'w');
$handle = $this->buildCsv($handle);
return Storage::disk('sftp')->put($filename, $handle);
}

public function exportCSVFileToResponse($filename = 'export.csv')
{
return new StreamedResponse(function () use ($filename) {
$handle = fopen('php://output', 'w');
$handle = $this->buildCsv($handle);
fclose($handle);
}, 200, [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
]);
}


public function buildCsv($handle, $header = false)
{
if ($header) {
fputcsv(
$handle,
array_keys($this->lineMapping(Line::first())),
self::DELIMITER
);
}

Line::with(['invoice', 'invoice.customer', 'item'])
->whereHas('invoice', function ($query) {
$query->where('is_exportable', 1);
})
->chunk(200, function ($lines) use ($handle) {
foreach ($lines as $line) {
fputcsv(
$handle,
$this->lineMapping($line),
self::DELIMITER
);
}
});

return $handle;
}

protected function lineMapping(Line $line)
{
return [
'Invoice number' => $line->invoice->id,
'Document type' => self::DOCUMENT_TYPE,
'Date' => $line->invoice->date,
];
}
}






php object-oriented csv laravel network-file-transfer






share|improve this question









New contributor




cbaconnier is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




cbaconnier is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited Nov 15 at 10:46





















New contributor




cbaconnier is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked Nov 15 at 7:54









cbaconnier

494




494




New contributor




cbaconnier is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





cbaconnier is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






cbaconnier is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






This question has an open bounty worth +50
reputation from cbaconnier ending in 5 days.


This question has not received enough attention.








This question has an open bounty worth +50
reputation from cbaconnier ending in 5 days.


This question has not received enough attention.














  • The current question title, which states your concerns about the code, is too general to be useful here. Please edit to the site standard, which is for the title to simply state the task accomplished by the code. Please see How to get the best value out of Code Review: Asking Questions for guidance on writing good question titles.
    – Toby Speight
    Nov 15 at 10:33










  • @TobySpeight I did the best I could. Perhaps, could you suggest me a better title?
    – cbaconnier
    Nov 15 at 10:40










  • It's hard to advise when the description is also quite vague, but something like "Export customer record as CSV" might work?
    – Toby Speight
    Nov 15 at 10:43












  • @TobySpeight Thanks
    – cbaconnier
    Nov 15 at 10:46


















  • The current question title, which states your concerns about the code, is too general to be useful here. Please edit to the site standard, which is for the title to simply state the task accomplished by the code. Please see How to get the best value out of Code Review: Asking Questions for guidance on writing good question titles.
    – Toby Speight
    Nov 15 at 10:33










  • @TobySpeight I did the best I could. Perhaps, could you suggest me a better title?
    – cbaconnier
    Nov 15 at 10:40










  • It's hard to advise when the description is also quite vague, but something like "Export customer record as CSV" might work?
    – Toby Speight
    Nov 15 at 10:43












  • @TobySpeight Thanks
    – cbaconnier
    Nov 15 at 10:46
















The current question title, which states your concerns about the code, is too general to be useful here. Please edit to the site standard, which is for the title to simply state the task accomplished by the code. Please see How to get the best value out of Code Review: Asking Questions for guidance on writing good question titles.
– Toby Speight
Nov 15 at 10:33




The current question title, which states your concerns about the code, is too general to be useful here. Please edit to the site standard, which is for the title to simply state the task accomplished by the code. Please see How to get the best value out of Code Review: Asking Questions for guidance on writing good question titles.
– Toby Speight
Nov 15 at 10:33












@TobySpeight I did the best I could. Perhaps, could you suggest me a better title?
– cbaconnier
Nov 15 at 10:40




@TobySpeight I did the best I could. Perhaps, could you suggest me a better title?
– cbaconnier
Nov 15 at 10:40












It's hard to advise when the description is also quite vague, but something like "Export customer record as CSV" might work?
– Toby Speight
Nov 15 at 10:43






It's hard to advise when the description is also quite vague, but something like "Export customer record as CSV" might work?
– Toby Speight
Nov 15 at 10:43














@TobySpeight Thanks
– cbaconnier
Nov 15 at 10:46




@TobySpeight Thanks
– cbaconnier
Nov 15 at 10:46










1 Answer
1






active

oldest

votes

















up vote
0
down vote













Try to think about this in a more generic way, you basically have:




  1. Select data from DB

  2. Put that data in a CSV (will you in the future want to use other formats like XLSX, etc? If so, we should abstract this step too, but I wont do this right now)

  3. Return that CSV file (doesn't matter to who you're returning)


So we can create a few classes from those steps




  1. Any class that can query the DB, your app probably has a bunch of them

  2. AbstractWriter - this class is an abstraction/interface to write anywhere, you'll need to implement it for specific formats, like Writer/Csv, Writer/Xlsx, Writer/JSON, etc.

  3. AbstractFileReturner - this class is an abstraction/interface to return it to anyone, you'll need to implement it for specific cases, AbstractFileReturner/like Sftp, AbstractFileReturner/HttpResponse, etc.


Any time you need more File formats or Returner, you just implement the AbstractWriter or FileReturner respectively one more time.



Doing all these, you'll be using Single Responsability Principle (each class do only one thing) and Open/Closed (the abstractions/interfaces are closed for modification, but open for extension).






share|improve this answer





















  • I already understand this. But in practice, when I'm looking the code code and tri to dissassemble them, I Just don't understand how.
    – cbaconnier
    15 hours ago













Your Answer





StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");

StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});






cbaconnier is a new contributor. Be nice, and check out our Code of Conduct.










 

draft saved


draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f207704%2flaravel-export-customer-record-as-csv%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
0
down vote













Try to think about this in a more generic way, you basically have:




  1. Select data from DB

  2. Put that data in a CSV (will you in the future want to use other formats like XLSX, etc? If so, we should abstract this step too, but I wont do this right now)

  3. Return that CSV file (doesn't matter to who you're returning)


So we can create a few classes from those steps




  1. Any class that can query the DB, your app probably has a bunch of them

  2. AbstractWriter - this class is an abstraction/interface to write anywhere, you'll need to implement it for specific formats, like Writer/Csv, Writer/Xlsx, Writer/JSON, etc.

  3. AbstractFileReturner - this class is an abstraction/interface to return it to anyone, you'll need to implement it for specific cases, AbstractFileReturner/like Sftp, AbstractFileReturner/HttpResponse, etc.


Any time you need more File formats or Returner, you just implement the AbstractWriter or FileReturner respectively one more time.



Doing all these, you'll be using Single Responsability Principle (each class do only one thing) and Open/Closed (the abstractions/interfaces are closed for modification, but open for extension).






share|improve this answer





















  • I already understand this. But in practice, when I'm looking the code code and tri to dissassemble them, I Just don't understand how.
    – cbaconnier
    15 hours ago

















up vote
0
down vote













Try to think about this in a more generic way, you basically have:




  1. Select data from DB

  2. Put that data in a CSV (will you in the future want to use other formats like XLSX, etc? If so, we should abstract this step too, but I wont do this right now)

  3. Return that CSV file (doesn't matter to who you're returning)


So we can create a few classes from those steps




  1. Any class that can query the DB, your app probably has a bunch of them

  2. AbstractWriter - this class is an abstraction/interface to write anywhere, you'll need to implement it for specific formats, like Writer/Csv, Writer/Xlsx, Writer/JSON, etc.

  3. AbstractFileReturner - this class is an abstraction/interface to return it to anyone, you'll need to implement it for specific cases, AbstractFileReturner/like Sftp, AbstractFileReturner/HttpResponse, etc.


Any time you need more File formats or Returner, you just implement the AbstractWriter or FileReturner respectively one more time.



Doing all these, you'll be using Single Responsability Principle (each class do only one thing) and Open/Closed (the abstractions/interfaces are closed for modification, but open for extension).






share|improve this answer





















  • I already understand this. But in practice, when I'm looking the code code and tri to dissassemble them, I Just don't understand how.
    – cbaconnier
    15 hours ago















up vote
0
down vote










up vote
0
down vote









Try to think about this in a more generic way, you basically have:




  1. Select data from DB

  2. Put that data in a CSV (will you in the future want to use other formats like XLSX, etc? If so, we should abstract this step too, but I wont do this right now)

  3. Return that CSV file (doesn't matter to who you're returning)


So we can create a few classes from those steps




  1. Any class that can query the DB, your app probably has a bunch of them

  2. AbstractWriter - this class is an abstraction/interface to write anywhere, you'll need to implement it for specific formats, like Writer/Csv, Writer/Xlsx, Writer/JSON, etc.

  3. AbstractFileReturner - this class is an abstraction/interface to return it to anyone, you'll need to implement it for specific cases, AbstractFileReturner/like Sftp, AbstractFileReturner/HttpResponse, etc.


Any time you need more File formats or Returner, you just implement the AbstractWriter or FileReturner respectively one more time.



Doing all these, you'll be using Single Responsability Principle (each class do only one thing) and Open/Closed (the abstractions/interfaces are closed for modification, but open for extension).






share|improve this answer












Try to think about this in a more generic way, you basically have:




  1. Select data from DB

  2. Put that data in a CSV (will you in the future want to use other formats like XLSX, etc? If so, we should abstract this step too, but I wont do this right now)

  3. Return that CSV file (doesn't matter to who you're returning)


So we can create a few classes from those steps




  1. Any class that can query the DB, your app probably has a bunch of them

  2. AbstractWriter - this class is an abstraction/interface to write anywhere, you'll need to implement it for specific formats, like Writer/Csv, Writer/Xlsx, Writer/JSON, etc.

  3. AbstractFileReturner - this class is an abstraction/interface to return it to anyone, you'll need to implement it for specific cases, AbstractFileReturner/like Sftp, AbstractFileReturner/HttpResponse, etc.


Any time you need more File formats or Returner, you just implement the AbstractWriter or FileReturner respectively one more time.



Doing all these, you'll be using Single Responsability Principle (each class do only one thing) and Open/Closed (the abstractions/interfaces are closed for modification, but open for extension).







share|improve this answer












share|improve this answer



share|improve this answer










answered yesterday









Edson Horacio Junior

1236




1236












  • I already understand this. But in practice, when I'm looking the code code and tri to dissassemble them, I Just don't understand how.
    – cbaconnier
    15 hours ago




















  • I already understand this. But in practice, when I'm looking the code code and tri to dissassemble them, I Just don't understand how.
    – cbaconnier
    15 hours ago


















I already understand this. But in practice, when I'm looking the code code and tri to dissassemble them, I Just don't understand how.
– cbaconnier
15 hours ago






I already understand this. But in practice, when I'm looking the code code and tri to dissassemble them, I Just don't understand how.
– cbaconnier
15 hours ago












cbaconnier is a new contributor. Be nice, and check out our Code of Conduct.










 

draft saved


draft discarded


















cbaconnier is a new contributor. Be nice, and check out our Code of Conduct.













cbaconnier is a new contributor. Be nice, and check out our Code of Conduct.












cbaconnier is a new contributor. Be nice, and check out our Code of Conduct.















 


draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f207704%2flaravel-export-customer-record-as-csv%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Quarter-circle Tiles

build a pushdown automaton that recognizes the reverse language of a given pushdown automaton?

Mont Emei