thephpleague/csv

Allow typecasting of values

tacman opened this issue · 5 comments

tacman commented

Feature Request

Q A
New Feature yes
BC Break no

Proposal

When parsing CSV, the values are always strings:

$csvString = "name,age
bob,44";
$csvReader = Reader::createFromString($csvString)->setHeaderOffset(0);
var_dump($csvReader->first());

array(2) { ["name"]=> string(3) "bob" ["age"]=> string(2) "44" }

One idea is to typecast in the column itself, the other is to have an external schema.

$csvString = "name,age:int
bob,44";
$csvReader = Reader::createFromString($csvString)->setHeaderOffset(0);
$parser = new Parser($csvReader);
var_dump($parser->first());

array(2) { ["name"]=> string(3) "bob" ["age"]=> int 44 }

The idea for the CSV Parser comes from https://github.com/shakahl/csv-schema, but I modified it to allow embedding the types in the headers. It is part of a Symfony bundle I use when manipulating CSV files, which I'm now refactoring to use phpleague's excellent tools.

But it got me to thinking, perhaps there's a way to use even more of your tools, or incorporate this typecasting directly.

My test file for the parser looks like this right now. Maybe version 10 can incorporate some of these ideas.

tests:
  - source: |
      name,age
      bob,22
    expects: |
      {"name":"bob","age":"22"}

  - source: |
      name,age
      bob,22
    map:
      /age/: int
    expects: |
      {"name":"bob","age":22}

  - source: |
      name,age:int,married:bool
      bob,22,1
    expects: |
      {"name":"bob","age":22, married: true}

  - source: |
      name,hobbies:array|
      bob,"golf|poker"
    expects: |
      {"name":"bob","hobbies":["golf","poker"]}

@tacman thanks for the issue. There's a good reason why this type of casting is not handled by the library. What you are proposing is a step in object hydration or mapping. Ultimately you may want to convert your row or records into either a strongly type array or PHP objects and there are plenty of battle testing library in PHP world that do it better than league/csv will ever do. check this line from Valinor which list different pavckages that could be used to achieve your goal without having to change or add unnecessary feature to the package and its maintenance.

tacman commented

Thanks, I'll check that out!

@tacman since version 9.11.0 a Reader::addFormatter method was added to allow you type the returned array.

$reader = Reader::createFromString($csvString)->setHeaderOffset(0);
$reader->addFormatter(function (array $record): array {
     $record['age'] = (int) $record['age'];

     return $record;
});

var_dump($csvReader->first());

//returns array(2) { ["name"]=> string(3) "bob" ["age"]=> int 44 }
tacman commented

Ah, cool, I'll check it out! I've used the addFormatter on the writer, glad to see it on the reader as well!

@tacman FYI there's currently a PR in review to allow full typecasting on CSV records. feel free to comment or add remarks