Skip to content

Commit

Permalink
Merge pull request #13 from Rodenastyle/csvEmptyLinesFix
Browse files Browse the repository at this point in the history
CSV empty lines detection
  • Loading branch information
sergiorodenas authored Aug 10, 2018
2 parents 1ed9d22 + 00b644c commit 698c63c
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 39 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

"require": {
"php": "^7.0",
"ext-xmlreader": "*",
"maxakawizard/json-collection-parser": "^1.1",
"tightenco/collect": "^5.5.33"
},
Expand Down
51 changes: 33 additions & 18 deletions src/Parsers/CSVParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,7 @@ class CSVParser implements StreamParserInterface
protected $reader, $source, $headers, $currentLine;

public static $delimiters = [",", ";"];

public function __construct()
{
Collection::macro('recursive', function () {
return $this->map(function ($value) {
if (is_array($value) || is_object($value)) {
return (new Collection($value))->recursive();
}
return $value;
});
});
}
public static $skipsEmptyLines = true;

public function from(String $source): StreamParserInterface
{
Expand All @@ -42,7 +31,9 @@ public function each(callable $function)
{
$this->start();
while($this->read()){
$function($this->getCurrentLineAsCollection());
if($this->currentLineIsValid()){
$function($this->getCurrentLineAsCollection());
}
}
$this->close();
}
Expand All @@ -52,7 +43,7 @@ private function start()
$this->reader = fopen($this->source, 'r');

$this->read();
$this->headers = new Collection($this->currentLine);
$this->headers = $this->currentLine;

return $this;
}
Expand All @@ -64,16 +55,40 @@ private function close(){
}

private function read(): bool{
$this->currentLine = (new Collection(fgetcsv($this->reader)))->filter();
return $this->currentLine->isNotEmpty();
$this->currentLine = new Collection(fgetcsv($this->reader));

//EOF detection
return $this->currentLine->first() !== false;
}

private function currentLineIsValid(): bool{
if(static::$skipsEmptyLines){
return ! $this->currentLineIsEmpty();
}

return true;
}

private function currentLineIsEmpty(){
return $this->currentLine->reject(function($value){
return $this->isEmptyValue($value);
})->isEmpty();
}

private function isEmptyValue($value){
return $value === "" || $value === null;
}

private function getCurrentLineAsCollection()
{
$headers = $this->headers;
$values = $this->formatCurrentLineValues($this->currentLine);

return $headers->intersectByKeys($this->currentLine)->combine($values->recursive());
return $headers->intersectByKeys($this->currentLine)
->combine($values->recursive())
->reject(function($value){
return $this->isEmptyValue($value);
});
}

private function formatCurrentLineValues(Collection $collection){
Expand All @@ -90,7 +105,7 @@ private function explodeCollectionValues(Collection $collection){
});
if(is_array($value)){
return (new Collection($value))->reject(function($value){
return empty($value);
return $this->isEmptyValue($value);
});
} else {
return $value;
Expand Down
12 changes: 0 additions & 12 deletions src/Parsers/JSONParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,6 @@ class JSONParser implements StreamParserInterface
{
protected $reader, $source;

public function __construct()
{
Collection::macro('recursive', function () {
return $this->map(function ($value) {
if (is_array($value) || is_object($value)) {
return (new Collection($value))->recursive();
}
return $value;
});
});
}

public function from(String $source): StreamParserInterface
{
$this->source = $source;
Expand Down
22 changes: 19 additions & 3 deletions src/StreamParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,34 @@
use Rodenastyle\StreamParser\Parsers\CSVParser;
use Rodenastyle\StreamParser\Parsers\JSONParser;
use Rodenastyle\StreamParser\Parsers\XMLParser;
use Rodenastyle\StreamParser\Traits\Facade;
use Tightenco\Collect\Support\Collection;

class StreamParser
{
public static function xml(String $source){
use Facade;

private function __construct()
{
Collection::macro('recursive', function () {
return $this->map(function ($value) {
if (is_array($value) || is_object($value)) {
return (new Collection($value))->recursive();
}
return $value;
});
});
}

private function xml(String $source){
return (new XMLParser())->from($source);
}

public static function json(String $source){
private function json(String $source){
return (new JSONParser())->from($source);
}

public static function csv(String $source){
private function csv(String $source){
return (new CSVParser())->from($source);
}
}
18 changes: 18 additions & 0 deletions src/Traits/Facade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
/**
* Created by PhpStorm.
* User: sergio.rodenas
* Date: 27/6/18
* Time: 15:49
*/

namespace Rodenastyle\StreamParser\Traits;


trait Facade
{
public static function __callStatic($name, $arguments)
{
return (new static())->{$name}(...$arguments);
}
}
29 changes: 26 additions & 3 deletions tests/Parsers/CSVParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use PHPUnit\Framework\TestCase;
use Rodenastyle\StreamParser\StreamParser;
use Rodenastyle\StreamParser\Parsers\CSVParser;
use Tightenco\Collect\Support\Collection;

class CSVParserTest extends TestCase
Expand All @@ -24,7 +25,7 @@ public function test_detects_main_elements_automatically()
$count++;
});

$this->assertEquals(5, $count);
$this->assertEquals(6, $count);
}

public function test_transforms_elements_to_collections()
Expand All @@ -41,11 +42,21 @@ public function test_element_values_are_there_after_transform()
"Anthology of World Literature",
"Computer Dictionary",
"Cooking on a Budget",
"Great Works of Art"
"Great Works of Art",
"0"
];

StreamParser::csv($this->stub)->each(function($book) use ($titles){
$prices = [
'12.95',
'24.95',
'24.90',
'0',
'29.95',
];

StreamParser::csv($this->stub)->each(function($book) use ($titles, $prices){
$this->assertContains($book->get('title'), $titles);
$this->assertContains($book->get('price'), $prices);
});
}

Expand All @@ -57,4 +68,16 @@ public function test_also_transforms_element_childs_to_collections_recursively()
}
});
}

public function test_allow_empty_string()
{
CSVParser::$skipsEmptyLines = false;

$count = 0;
StreamParser::csv($this->stub)->each(function() use (&$count){
$count++;
});

$this->assertEquals(8, $count);
}
}
9 changes: 6 additions & 3 deletions tests/Stubs/sample.csv
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
title,price,comments
The Iliad and The Odyssey,12.95,"Best translation I've read.,I like other versions better."
Anthology of World Literature,24.95,"Needs more modern literature.,Excellent overview of world literature."
Computer Dictionary,24.95,"A valuable resource.,"
Cooking on a Budget,23.95,"Delicious!,"
Great Works of Art,29.95,

Computer Dictionary,24.90,"A valuable resource.,"
,,
0,0,"0,0"
Cooking on a Budget,0,"Delicious!,"
Great Works of Art,29.95,

0 comments on commit 698c63c

Please sign in to comment.