Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CSV Response Class #361

Open
wants to merge 5 commits into
base: develop
from

Conversation

Projects
None yet
4 participants
@settermjd
Copy link

commented Jun 10, 2019

Why is the new feature needed? What purpose does it serve?

After doing a bit of searching, I didn't find a class that would send a CSV response. There were the Text, JSON, HTML, Redirect, XML, and Empty response classes, but nothing specific to CSV. So I created this PR to add a CSV response class, which can send both plain CSV text as well as a response that will be interpreted by the client as a downloadable file.

How will users use the new feature?

Users can use the CSV response class very similarly to how they use the existing response classes. The only difference is that if they supply a file name as the third parameter to the constructor, then a download response will be sent, not a textual response.

settermjd added some commits Jun 8, 2019

Create a CSV Response class
This class is almost identical to TextResponse, except that it sets the
Content-Type header to text/csv.
Add a trait for managing download responses
After doing a bit of searching, I didn't find anything that would help
with sending a downloaded response, instead of the default, which is to
send a text response. So I created this one, which sends six headers
that ensure that the response sent will be interpreted by the client as
a downloadable file.
Refactor the CsvResponse to support sending a downloadable response
This change update the CsvResponse to be able to either send a plain CSV
text response, or to send the response as a downloadable file instead.
Update documentation to add document new CsvResponse class
In the style of the existing Response class documentation, this change
adds a section covering the new CsvResponse class.
@webimpress
Copy link
Contributor

left a comment

Hi @settermjd !

I am not maintainer so I can't approve this PR, but please see my comments. As I said I don't know if this feature is gonna be accepted.

I would consider passing the array to CsvResponse and build the body inside from the array. Similar we have with JSON response - we are passing array and json is build inside.

<?php
/**
* @see https://github.com/zendframework/zend-diactoros for the canonical source repository
* @copyright Copyright (c) 2015-2018 Zend Technologies USA Inc. (https://www.zend.com)

This comment has been minimized.

Copy link
@webimpress

webimpress Jun 10, 2019

Contributor

Just 2019, please

This comment has been minimized.

Copy link
@settermjd

settermjd Jun 10, 2019

Author

Thanks for the feedback. Shall do.

*/
public function __construct($text, int $status = 200, string $filename = '', array $headers = [])
{
if (is_string($filename) && $filename !== '') {

This comment has been minimized.

Copy link
@webimpress

webimpress Jun 10, 2019

Contributor

It IS string, per param declaration, so we don't need to check again here

This comment has been minimized.

Copy link
@settermjd

settermjd Jun 10, 2019

Author

Good point. I forgot to remove that after setting the parameter type.

*
* Allows creating a CSV response by passing a string to the constructor;
* by default, sets a status code of 200 and sets the Content-Type header to
* text/csv.

This comment has been minimized.

Copy link
@webimpress

webimpress Jun 10, 2019

Contributor

wouldn't be better to accept also/only array with data and process it here?

This comment has been minimized.

Copy link
@settermjd

settermjd Jun 10, 2019

Author

Honestly, hadn't considered that. Thanks.

*/
private function getDownloadHeaders(string $filename): array
{
$headers = [];

This comment has been minimized.

Copy link
@webimpress

webimpress Jun 10, 2019

Contributor

we can do just:

return [
    'cache-control' => 'must-revalidate',
   // ... all other headers
    'pragma' => 'Public',
];

This comment has been minimized.

Copy link
@weierophinney

weierophinney Jul 10, 2019

Member

I agree; just return the fully-populated array.

<?php
/**
* @see https://github.com/zendframework/zend-diactoros for the canonical source repository
* @copyright Copyright (c) 2015-2018 Zend Technologies USA Inc. (https://www.zend.com)

This comment has been minimized.

Copy link
@webimpress

webimpress Jun 10, 2019

Contributor

only 2019, please

$overridesDownloadHeaders = false;
foreach (array_keys($headers) as $header) {
if (in_array($header, $downloadHeaders)) {

This comment has been minimized.

Copy link
@webimpress

webimpress Jun 10, 2019

Contributor

please add 3rd param to true (strict types)

}
public function invalidHeadersWhenDownloading()

This comment has been minimized.

Copy link
@webimpress

webimpress Jun 10, 2019

Contributor

we can have RTH here

This comment has been minimized.

Copy link
@settermjd

settermjd Jun 11, 2019

Author

Sorry, what's RTH?

This comment has been minimized.

Copy link
@webimpress

webimpress Jun 11, 2019

Contributor

Return Type Hint declaration:

public function invalidHeadersWhenDownloading() : array
                                                ^^^^^^^^
['content-disposition', 'upload.csv',],
['content-transfer-encoding', 'Binary',],
['expires', '0',],
['pragma', 'Public',]

This comment has been minimized.

Copy link
@webimpress

webimpress Jun 10, 2019

Contributor

there should be no comma before ] but only trailing comma after ]

{
$status = 404;
$headers = [
'x-custom' => [ 'foo-bar' ],

This comment has been minimized.

Copy link
@webimpress

webimpress Jun 10, 2019

Contributor

no spaces after [ and before ], please

}
/**
* @group 115

This comment has been minimized.

Copy link
@webimpress

webimpress Jun 10, 2019

Contributor

what is the group?

@@ -1,1519 +1,1519 @@
# Changelog

This comment has been minimized.

Copy link
@webimpress

webimpress Jun 10, 2019

Contributor

What has happened on that file?

$response = new Zend\Diactoros\Response\TextResponse(
$text,
200,
['Content-Type' => ['text/csv']]

This comment has been minimized.

Copy link
@froschdesign

froschdesign Jun 11, 2019

Member

With the new response type for CSV, the media type for this example should be changed. Maybe text/example. (see RFC 4735)

@weierophinney
Copy link
Member

left a comment

I like the idea of this a lot. However, I would argue we need two things here:

  • A general DownloadResponse for specifying file downloads. This could build on your provided DownloadResponseTrait. This should allow for either specifying string content as the download OR a filename; the latter is useful, as it can prevent the need for having the full file contents in memory at once.
  • Three different constructors for the CsvResponse:
    • One which accepts a CSV-formatted string to use.
    • One which accepts a CSV file to use.
    • One which accepts an array of arrays to use, as well as (optionally) a delimiter character and enclosure character. This would build the CSV string.

You can do multiple constructors by using static constuctor methods:

  • CsvResponse::fromString()
  • CsvResponse::fromFile()
  • CsvResponse::fromArray() (or fromTraversable())

Finally, this could be interesting either as part of Diactoros, or as a stand-alone package that uses PSR-17 to create the initial stream and response instances. If you want to push it here, we can definitely maintain it, though.

- An array of supplemental headers
```php
$response = new Zend\Diactoros\Response\TextResponse(

This comment has been minimized.

Copy link
@weierophinney

weierophinney Jul 10, 2019

Member

Why TextResponse here?

*/
private function getDownloadHeaders(string $filename): array
{
$headers = [];

This comment has been minimized.

Copy link
@weierophinney

weierophinney Jul 10, 2019

Member

I agree; just return the fully-populated array.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.