├── .gitignore ├── .github ├── FUNDING.yml └── workflows │ └── run-tests.yml ├── tests ├── bootstrap.php ├── Laracsv │ ├── Models │ │ ├── Product.php │ │ └── Category.php │ └── ExportTest.php └── TestCase.php ├── .travis.yml ├── phpunit.xml ├── composer.json ├── LICENSE ├── README.md └── src └── Laracsv └── Export.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: usmanhalalit 2 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 'float', 17 | 'original_price' => 'float', 18 | ]; 19 | 20 | public function categories() 21 | { 22 | return $this->belongsToMany(Category::class); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Laracsv/Models/Category.php: -------------------------------------------------------------------------------- 1 | belongsToMany(Product::class); 16 | } 17 | 18 | public function mainCategory() 19 | { 20 | return $this->belongsTo(Category::class, 'parent_id'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | tests/Laracsv/ 15 | 16 | 17 | 18 | 19 | 20 | ./src 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Test Suite 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | max-parallel: 15 16 | matrix: 17 | php-versions: ['7.1', '7.2', '7.3', '7.4'] 18 | name: PHP ${{ matrix.php-versions }} Test 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@master 22 | - name: Install PHP 23 | uses: shivammathur/setup-php@master 24 | with: 25 | php-version: ${{ matrix.php-versions }} 26 | extensions: dom, mbstring 27 | - name: Check PHP Version 28 | run: php -v 29 | - name: Install composer 30 | run: cd $GITHUB_WORKSPACE && composer install 31 | - name: Run tests 32 | run: cd $GITHUB_WORKSPACE && vendor/bin/phpunit 33 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "usmanhalalit/laracsv", 3 | "description": "A Laravel package to easily generate CSV files from Eloquent model.", 4 | "homepage": "https://github.com/usmanhalalit/laracsv", 5 | "keywords": ["laravel", "csv", "eloquent", "export"], 6 | "license": "MIT", 7 | "authors": 8 | [ 9 | { 10 | "name": "Muhammad Usman", 11 | "email": "hi@usman.it", 12 | "role": "Developer" 13 | } 14 | ], 15 | 16 | "require": { 17 | "php": ">=7.1", 18 | "illuminate/database": ">=5.2", 19 | "league/csv": "^9.0" 20 | }, 21 | 22 | "require-dev": { 23 | "fzaninotto/faker": "^1.8", 24 | "phpunit/phpunit": "^7.0" 25 | }, 26 | 27 | "autoload": { 28 | "psr-4": { 29 | "Laracsv\\": "src/Laracsv/" 30 | } 31 | }, 32 | 33 | "autoload-dev": { 34 | "psr-4": { 35 | "Laracsv\\": "tests/Laracsv/" 36 | }, 37 | 38 | "classmap": [ 39 | "tests/TestCase.php" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Muhammad Usman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | addConnection(array( 18 | 'driver' => 'sqlite', 19 | 'database' => ':memory:', 20 | )); 21 | $capsule->setAsGlobal(); 22 | $capsule->bootEloquent(); 23 | 24 | $this->createTables(Capsule::schema()); 25 | $this->seedData(); 26 | } 27 | 28 | public function tearDown() 29 | { 30 | } 31 | 32 | private function createTables($schema) 33 | { 34 | $schema->create('products', function (Blueprint $table) { 35 | $table->increments('id'); 36 | $table->string('title', 100); 37 | $table->decimal('price', 10, 2); 38 | $table->decimal('original_price', 10, 2)->nullable(); 39 | $table->timestamps(); 40 | }); 41 | 42 | $schema->create('categories', function (Blueprint $table) { 43 | $table->increments('id'); 44 | $table->unsignedInteger('parent_id'); 45 | $table->string('title', 40); 46 | $table->timestamps(); 47 | }); 48 | 49 | $schema->create('category_product', function (Blueprint $table) { 50 | $table->increments('id'); 51 | $table->integer('category_id'); 52 | $table->integer('product_id'); 53 | }); 54 | } 55 | 56 | private function seedData() 57 | { 58 | $faker = \Faker\Factory::create(); 59 | foreach (range(1, 10) as $id => $item) { 60 | Category::create([ 61 | 'id' => $id, 62 | 'parent_id' => 1, 63 | 'title' => $faker->name, 64 | ]); 65 | } 66 | 67 | foreach (range(1, 10) as $item) { 68 | $product = Product::create([ 69 | 'title' => $faker->name, 70 | 'price' => collect(range(4, 100))->random(), 71 | 'original_price' => collect(range(5, 120))->random(), 72 | ]); 73 | 74 | $product->categories()->attach(Category::find(collect(range(1, 10))->random())); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LaraCSV 2 | 3 | A Laravel package to easily generate CSV files from Eloquent model. 4 | 5 | [![Build Status](https://travis-ci.org/usmanhalalit/laracsv.svg?branch=master)](https://travis-ci.org/usmanhalalit/laracsv) 6 | [![Total Downloads](https://poser.pugx.org/usmanhalalit/laracsv/downloads)](https://packagist.org/packages/usmanhalalit/laracsv) 7 | [![Daily Downloads](https://poser.pugx.org/usmanhalalit/laracsv/d/daily)](https://packagist.org/packages/usmanhalalit/laracsv) 8 | 9 | ## Basic usage 10 | 11 | ```php 12 | $users = User::get(); // All users 13 | $csvExporter = new \Laracsv\Export(); 14 | $csvExporter->build($users, ['email', 'name'])->download(); 15 | ``` 16 | 17 | And a proper CSV file will be downloaded with `email` and `name` fields. As simple as it sounds! 18 | 19 | ## Installation 20 | 21 | Just run this on your terminal: 22 | 23 | ``` 24 | composer require usmanhalalit/laracsv:^2.1 25 | ``` 26 | and you should be good to go. 27 | 28 | ## Full Documentation 29 | 30 | - [Build CSV](#build-csv) 31 | - [Output Options](#output-options) 32 | - [Download](#download) 33 | - [Custom Headers](#custom-headers) 34 | - [No Header](#no-header) 35 | - [Modify or Add Values](#modify-or-add-values) 36 | - [Add fields and values](#add-fields-and-values) 37 | - [Model Relationships](#model-relationships) 38 | - [Build by chunks](#build-by-chunks) 39 | 40 | 41 | ### Build CSV 42 | 43 | `$exporter->build($modelCollection, $fields)` takes three parameters. 44 | First one is the model (collection of models), seconds one takes the field names 45 | you want to export, third one is config, which is optional. 46 | 47 | ```php 48 | $csvExporter->build(User::get(), ['email', 'name', 'created_at']); 49 | ``` 50 | 51 | ### Output Options 52 | #### Download 53 | 54 | To get file downloaded to the browser: 55 | ```php 56 | $csvExporter->download(); 57 | ``` 58 | 59 | You can provide a filename if you wish: 60 | ```php 61 | $csvExporter->download('active_users.csv'); 62 | ``` 63 | If no filename is given a filename with date-time will be generated. 64 | 65 | #### Advanced Outputs 66 | 67 | LaraCSV uses [League CSV](http://csv.thephpleague.com/). You can do what League CSV 68 | is able to do. You can get the underlying League CSV writer and reader instance by calling: 69 | 70 | ```php 71 | $csvWriter = $csvExporter->getWriter(); 72 | $csvReader = $csvExporter->getReader(); 73 | ``` 74 | 75 | And then you can do several things like: 76 | ```php 77 | $csvString = $csvWriter->getContent(); // To get the CSV as string 78 | $csvReader->jsonSerialize(); // To turn the CSV in to an array 79 | ``` 80 | 81 | For more information please check [League CSV documentation](http://csv.thephpleague.com/). 82 | 83 | 84 | ### Custom Headers 85 | 86 | Above code example will generate a CSV with headers email, name, created_at and corresponding rows after. 87 | 88 | If you want to change the header with a custom label just pass it as array value: 89 | ```php 90 | $csvExporter->build(User::get(), ['email', 'name' => 'Full Name', 'created_at' => 'Joined']); 91 | ``` 92 | 93 | Now `name` column will show the header `Full Name` but it will still take 94 | values from `name` field of the model. 95 | 96 | #### No Header 97 | 98 | You can also suppress the CSV header: 99 | ```php 100 | $csvExporter->build(User::get(), ['email', 'name', 'created_at'], [ 101 | 'header' => false, 102 | ]); 103 | ``` 104 | 105 | ### Modify or Add Values 106 | 107 | There is a hook which is triggered before processing a database row. 108 | For example, if you want to change the date format you can do so. 109 | ```php 110 | $csvExporter = new \Laracsv\Export(); 111 | $users = User::get(); 112 | 113 | // Register the hook before building 114 | $csvExporter->beforeEach(function ($user) { 115 | $user->created_at = date('f', strtotime($user->created_at)); 116 | }); 117 | 118 | $csvExporter->build($users, ['email', 'name' => 'Full Name', 'created_at' => 'Joined']); 119 | ``` 120 | 121 | **Note:** If a `beforeEach` callback returns `false` then the entire row will be 122 | excluded from the CSV. It can come handy to filter some rows. 123 | 124 | #### Add fields and values 125 | 126 | You may also add fields that don't exists in a database table add values on the fly: 127 | 128 | ```php 129 | // The notes field doesn't exist so values for this field will be blank by default 130 | $csvExporter->beforeEach(function ($user) { 131 | // Now notes field will have this value 132 | $user->notes = 'Add your notes'; 133 | }); 134 | 135 | $csvExporter->build($users, ['email', 'notes']); 136 | ``` 137 | 138 | ### Model Relationships 139 | 140 | You can also add fields in the CSV from related database tables, given the model 141 | has relationships defined. 142 | 143 | This will get the product title and the related category's title (one to one): 144 | ```php 145 | $csvExporter->build($products, ['title', 'category.title']); 146 | ``` 147 | 148 | You may also tinker relation things as you wish with hooks: 149 | 150 | ```php 151 | $products = Product::with('categories')->where('order_count', '>', 10)->orderBy('order_count', 'desc')->get(); 152 | $fields = ['id', 'title','original_price' => 'Market Price', 'category_ids',]; 153 | $csvExporter = new \Laracsv\Export(); 154 | $csvExporter->beforeEach(function ($product) { 155 | $product->category_ids = implode(', ', $product->categories->pluck('id')->toArray()); 156 | }); 157 | ``` 158 | 159 | ## Build by chunks 160 | 161 | For larger datasets, which can become more memory consuming, a builder instance can be used to process the results in chunks. Similar to the row-related hook, a chunk-related hook can be used in this case for e.g. eager loading or similar chunk based operations. The behaviour between both hooks is similar; it gets called before each chunk and has the entire collection as an argument. **In case `false` is returned the entire chunk gets skipped and the code continues with the next one.** 162 | 163 | ```$export = new Export(); 164 | 165 | // Perform chunk related operations 166 | $export->beforeEachChunk(function ($collection) { 167 | $collection->load('categories'); 168 | }); 169 | 170 | $export->buildFromBuilder(Product::select(), ['category_label']); 171 | ``` 172 | 173 | The default chunk size is set to 1000 results but can be altered by passing a different value in the `$config` passed to `buildFromBuilder`. Example alters the chunk size to 500. 174 | 175 | ```php 176 | // ... 177 | 178 | $export->buildFromBuilder(Product::select(), ['category_label'], ['chunk' => 500]); 179 | ``` 180 | 181 | © [Muhammad Usman](http://usman.it/). Licensed under MIT license. 182 | -------------------------------------------------------------------------------- /src/Laracsv/Export.php: -------------------------------------------------------------------------------- 1 | writer = $writer ?: Writer::createFromFileObject(new SplTempFileObject); 57 | } 58 | 59 | /** 60 | * Build the writer. 61 | * 62 | * @param \Illuminate\Support\Collection $collection 63 | * @param array $fields 64 | * @param array $config 65 | * @return $this 66 | * @throws \League\Csv\CannotInsertRecord 67 | */ 68 | public function build($collection, array $fields, array $config = []): self 69 | { 70 | $this->config = $config; 71 | 72 | $this->addHeader($this->writer, $this->getHeaderFields($fields)); 73 | $this->addCsvRows($this->writer, $this->getDataFields($fields), $collection); 74 | 75 | return $this; 76 | } 77 | 78 | /** 79 | * Build the CSV from a builder instance. 80 | * 81 | * @param \Illuminate\Database\Eloquent\Builder $builder 82 | * @param array $fields 83 | * @param array $config 84 | * @return $this 85 | * @throws \League\Csv\CannotInsertRecord 86 | */ 87 | public function buildFromBuilder(Builder $builder, array $fields, array $config = []): self 88 | { 89 | $this->config = $config; 90 | 91 | $chunkSize = Arr::get($config, 'chunk', self::DEFAULT_CHUNK_SIZE); 92 | $dataFields = $this->getDataFields($fields); 93 | 94 | $this->addHeader($this->writer, $this->getHeaderFields($fields)); 95 | 96 | $builder->chunk($chunkSize, function ($collection) use ($dataFields) { 97 | $callback = $this->beforeEachChunkCallback; 98 | 99 | if ($callback && $callback($collection) === false) { 100 | return; 101 | } 102 | 103 | $this->addCsvRows($this->writer, $dataFields, $collection); 104 | }); 105 | 106 | return $this; 107 | } 108 | 109 | /** 110 | * Download the CSV file. 111 | * 112 | * @param string|null $filename 113 | * @return void 114 | */ 115 | public function download($filename = null): void 116 | { 117 | $filename = $filename ?: date('Y-m-d_His') . '.csv'; 118 | 119 | $this->writer->output($filename); 120 | } 121 | 122 | /** 123 | * Set the callback. 124 | * 125 | * @param callable $callback 126 | * @return $this 127 | */ 128 | public function beforeEach(callable $callback): self 129 | { 130 | $this->beforeEachCallback = $callback; 131 | 132 | return $this; 133 | } 134 | 135 | /** 136 | * Callback which is run before processsing each chunk. 137 | * 138 | * @param callable $callback 139 | * @return $this 140 | */ 141 | public function beforeEachChunk(callable $callback): self 142 | { 143 | $this->beforeEachChunkCallback = $callback; 144 | 145 | return $this; 146 | } 147 | 148 | /** 149 | * Get a CSV reader. 150 | * 151 | * @return Reader 152 | */ 153 | public function getReader(): Reader 154 | { 155 | return Reader::createFromString($this->writer->getContent()); 156 | } 157 | 158 | /** 159 | * Get the CSV writer. 160 | * 161 | * @return Writer 162 | */ 163 | public function getWriter(): Writer 164 | { 165 | return $this->writer; 166 | } 167 | 168 | /** 169 | * Get all the data fields for the current set of fields. 170 | * 171 | * @param array $fields 172 | * @return array 173 | */ 174 | private function getDataFields(array $fields): array 175 | { 176 | foreach ($fields as $key => $field) { 177 | if (is_string($key)) { 178 | $fields[$key] = $key; 179 | } 180 | } 181 | 182 | return array_values($fields); 183 | } 184 | 185 | /** 186 | * Get all the header fields for the current set of fields. 187 | * 188 | * @param array $fields 189 | * @return array 190 | */ 191 | private function getHeaderFields(array $fields): array 192 | { 193 | return array_values($fields); 194 | } 195 | 196 | /** 197 | * Add rows to the CSV. 198 | * 199 | * @param Writer $writer 200 | * @param array $fields 201 | * @param \Illuminate\Support\Collection $collection 202 | * @throws \League\Csv\CannotInsertRecord 203 | */ 204 | private function addCsvRows(Writer $writer, array $fields, Collection $collection): void 205 | { 206 | foreach ($collection as $model) { 207 | $beforeEachCallback = $this->beforeEachCallback; 208 | 209 | // Call hook 210 | if ($beforeEachCallback) { 211 | $return = $beforeEachCallback($model); 212 | 213 | if ($return === false) { 214 | continue; 215 | } 216 | } 217 | 218 | if (!Arr::accessible($model)) { 219 | $model = collect($model); 220 | } 221 | 222 | $csvRow = []; 223 | foreach ($fields as $field) { 224 | $csvRow[] = Arr::get($model, $field); 225 | } 226 | 227 | $writer->insertOne($csvRow); 228 | } 229 | } 230 | 231 | /** 232 | * Adds a header row to the CSV. 233 | * 234 | * @param Writer $writer 235 | * @param array $headers 236 | * @return void 237 | */ 238 | private function addHeader(Writer $writer, array $headers): void 239 | { 240 | if (Arr::get($this->config, 'header', true) !== false) { 241 | $writer->insertOne($headers); 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /tests/Laracsv/ExportTest.php: -------------------------------------------------------------------------------- 1 | get(); 15 | 16 | $fields = ['id', 'title', 'price', 'original_price',]; 17 | 18 | $csvExporter = new Export(); 19 | $csvExporter->build($products, $fields); 20 | $csv = $csvExporter->getReader(); 21 | $lines = explode(PHP_EOL, trim($csv->getContent())); 22 | $firstLine = $lines[0]; 23 | $this->assertEquals("id,title,price,original_price", $firstLine); 24 | $this->assertCount(11, $lines); 25 | $this->assertCount(count($fields), explode(',', $lines[2])); 26 | } 27 | 28 | public function testWithCustomHeaders() 29 | { 30 | $products = Product::limit(5)->get(); 31 | 32 | $fields = ['id', 'title' => 'Name', 'price', 'original_price' => 'Retail Price', 'custom_field' => 'Custom Field']; 33 | 34 | $csvExporter = new Export(); 35 | $csvExporter->build($products, $fields); 36 | $csv = $csvExporter->getReader(); 37 | $lines = explode(PHP_EOL, trim($csv->getContent())); 38 | $firstLine = $lines[0]; 39 | $this->assertSame('id,Name,price,"Retail Price","Custom Field"', $firstLine); 40 | } 41 | 42 | public function testWithBeforeEachCallback() 43 | { 44 | $products = Product::limit(5)->get(); 45 | 46 | $fields = ['id', 'title' => 'Name', 'price', 'original_price' => 'Retail Price', 'custom_field' => 'Custom Field']; 47 | 48 | $csvExporter = new Export(); 49 | $csvExporter->beforeEach(function ($model) { 50 | if ($model->id == 2) { 51 | return false; 52 | } 53 | $model->custom_field = 'Test Value'; 54 | $model->price = 30; 55 | }); 56 | 57 | $csvExporter->build($products, $fields); 58 | 59 | $csv = $csvExporter->getReader(); 60 | $lines = explode(PHP_EOL, trim($csv->getContent())); 61 | $firstLine = $lines[0]; 62 | $thirdRow = explode(',', $lines[2]); 63 | $this->assertSame('id,Name,price,"Retail Price","Custom Field"', $firstLine); 64 | $this->assertEquals(30, $thirdRow[2]); 65 | $this->assertSame('"Test Value"', $thirdRow[4]); 66 | $this->assertCount(5, $lines); 67 | } 68 | 69 | public function testBeforeEachChunkCallback() 70 | { 71 | $export = new Export(); 72 | 73 | // Verify that the categories are not loaded 74 | $export->beforeEach(function ($record) { 75 | $this->assertFalse($record->relationLoaded('categories')); 76 | }); 77 | 78 | $export->buildFromBuilder(Product::select(), ['id']); 79 | 80 | // Eager load the categories per chunk 81 | $export->beforeEachChunk(function ($collection) { 82 | $collection->load('categories'); 83 | }); 84 | 85 | // Verify that the categories are eagerly loaded per chunk 86 | $export->beforeEach(function ($record) { 87 | $this->assertTrue($record->relationLoaded('categories')); 88 | }); 89 | 90 | $export->buildFromBuilder(Product::select(), ['id']); 91 | } 92 | 93 | public function testBuilderChunkSize() 94 | { 95 | $export = new Export(); 96 | 97 | $export->beforeEachChunk(function ($collection) { 98 | $this->assertSame(10, $collection->count()); 99 | }); 100 | 101 | $export->buildFromBuilder(Product::select(), ['formatted_property' => 'Property']); 102 | 103 | $export->beforeEachChunk(function ($collection) { 104 | $this->assertSame(5, $collection->count()); 105 | }); 106 | 107 | $export->buildFromBuilder(Product::select(), ['formatted_property' => 'Property'], ['chunk' => 5]); 108 | } 109 | 110 | public function testBuilderHeader() 111 | { 112 | $export = new Export(); 113 | 114 | $export->buildFromBuilder(Product::select(), ['id' => 'ID Header', 'price']); 115 | 116 | $lines = explode(PHP_EOL, trim($export->getReader()->getContent())); 117 | 118 | $this->assertSame('"ID Header",price', $lines[0]); 119 | 120 | $export = new Export(); 121 | 122 | $export->buildFromBuilder(Product::select(), ['id' => 'ID Header', 'price'], ['header' => false]); 123 | 124 | $lines = explode(PHP_EOL, trim($export->getReader()->getContent())); 125 | 126 | $this->assertNotSame('"ID Header",price', $lines[0]); 127 | } 128 | 129 | public function testBuilder() 130 | { 131 | $export = new Export(); 132 | 133 | $export->beforeEach(function ($record) { 134 | $record->formatted_property = 'id_' . $record->id; 135 | }); 136 | 137 | $export->buildFromBuilder(Product::select(), ['formatted_property'], ['header' => false]); 138 | 139 | $lines = explode(PHP_EOL, trim($export->getReader()->getContent())); 140 | 141 | foreach ($lines as $index => $line) { 142 | $this->assertSame('id_' . ($index + 1), $line); 143 | } 144 | } 145 | 146 | public function testUtf8() 147 | { 148 | foreach (range(11, 15) as $item) { 149 | $product = Product::create([ 150 | 'title' => 'رجا ابو سلامة', 151 | 'price' => 70, 152 | 'original_price' => 80, 153 | ]); 154 | 155 | $product->categories()->attach(Category::find(collect(range(1, 10))->random())); 156 | } 157 | 158 | $products = Product::where('title', 'رجا ابو سلامة')->get(); 159 | $this->assertEquals('رجا ابو سلامة', $products->first()->title); 160 | 161 | $csvExporter = new Export(); 162 | 163 | $csvExporter->build($products, ['title', 'price']); 164 | 165 | $csv = $csvExporter->getReader(); 166 | $lines = explode(PHP_EOL, trim($csv->getContent())); 167 | 168 | $this->assertSame('"رجا ابو سلامة",70', $lines[2]); 169 | } 170 | 171 | public function testCustomLeagueCsvWriters() 172 | { 173 | $products = Product::limit(10)->get(); 174 | 175 | $fields = ['id', 'title', 'price', 'original_price',]; 176 | file_put_contents('test.csv', ''); 177 | $csvExporter = new Export(Writer::createFromPath('test.csv', 'r+')); 178 | $csvExporter->build($products, $fields); 179 | $csv = $csvExporter->getReader(); 180 | 181 | $lines = explode(PHP_EOL, trim($csv->getContent())); 182 | $firstLine = $lines[0]; 183 | $this->assertEquals("id,title,price,original_price", $firstLine); 184 | $this->assertCount(11, $lines); 185 | $this->assertCount(count($fields), explode(',', $lines[2])); 186 | unlink('test.csv'); 187 | } 188 | 189 | public function testCaseSensitiveRelationNames() 190 | { 191 | $cntCategories = 5; 192 | $categories = Category::limit($cntCategories)->with('mainCategory')->get(); 193 | 194 | $csvExporter = new Export(); 195 | 196 | $csvExporter->build($categories, [ 197 | 'id', 198 | 'title', 199 | 'mainCategory.id' => 'Parent Category ID', 200 | ]); 201 | 202 | $csv = $csvExporter->getReader(); 203 | 204 | $secondLine = explode(',', explode(PHP_EOL, trim($csv->getContent()))[1]); 205 | 206 | $this->assertCount(3, $secondLine); // There should be a parent id for each category 207 | $this->assertEquals(1, $secondLine[2]); // Parent ID is always seeded to #1 208 | } 209 | 210 | public function testIlluminateSupportCollection() 211 | { 212 | $faker = \Faker\Factory::create(); 213 | 214 | $csvExporter = new Export(); 215 | 216 | $data = []; 217 | for ($i = 1; $i < 5; $i++) { 218 | $data[] = [ 219 | 'id' => $i, 220 | 'address' => $faker->streetAddress, 221 | 'firstName' => $faker->firstName 222 | ]; 223 | } 224 | $data = collect($data); 225 | $csvExporter->build($data, [ 226 | 'id', 227 | 'firstName', 228 | 'address' 229 | ]); 230 | 231 | $csv = $csvExporter->getWriter(); 232 | $lines = explode(PHP_EOL, trim($csv)); 233 | $this->assertCount(5, $lines); 234 | 235 | $fourthLine = explode(',', explode(PHP_EOL, trim($csv))[4]); 236 | 237 | $this->assertSame('4', $fourthLine[0]); 238 | } 239 | 240 | public function testExportPlainObjects() 241 | { 242 | $faker = \Faker\Factory::create(); 243 | 244 | $csvExporter = new Export(); 245 | 246 | $data = []; 247 | for ($i = 1; $i < 5; $i++) { 248 | $object = new stdClass(); 249 | $object->id = $i; 250 | $object->address = $faker->streetAddress; 251 | $object->firstName = $faker->firstName; 252 | 253 | $data[] = $object; 254 | } 255 | 256 | $data = collect($data); 257 | 258 | $csvExporter->build($data, [ 259 | 'id', 260 | 'firstName', 261 | 'address' 262 | ]); 263 | 264 | $csv = trim($csvExporter->getWriter()->getContent()); 265 | 266 | $lines = explode(PHP_EOL, $csv); 267 | $fourthLine = explode(',', $lines[4]); 268 | 269 | $this->assertCount(5, $lines); 270 | $this->assertSame('4', $fourthLine[0]); 271 | } 272 | 273 | public function testRead() 274 | { 275 | $products = Product::limit(10)->get(); 276 | 277 | $fields = ['id', 'title', 'price', 'original_price',]; 278 | 279 | $csvExporter = new Export(); 280 | $csvExporter->build($products, $fields); 281 | $reader = $csvExporter->getReader(); 282 | $this->assertCount(11, $reader); 283 | $this->assertEquals('title', $reader->fetchOne()[1]); 284 | $this->assertEquals(Product::first()->title, $reader->fetchOne(1)[1]); 285 | } 286 | 287 | public function testJson() 288 | { 289 | $products = Product::limit(10)->get(); 290 | 291 | $fields = ['id', 'title', 'price', 'original_price',]; 292 | 293 | $csvExporter = new Export(); 294 | $csvExporter->build($products, $fields); 295 | $reader = $csvExporter->getReader(); 296 | $this->assertEquals(Product::first()->title, $reader->jsonSerialize()[1][1]); 297 | } 298 | 299 | public function testWriter() 300 | { 301 | $products = Product::limit(10)->get(); 302 | 303 | $fields = ['id', 'title', 'price', 'original_price',]; 304 | 305 | $csvExporter = new Export(); 306 | $csvExporter->build($products, $fields); 307 | $writer = $csvExporter->getWriter(); 308 | $this->assertNotFalse(strstr($writer->getContent(), Product::first()->title)); 309 | } 310 | 311 | public function testWithNoHeader() 312 | { 313 | $products = Product::limit(10)->get(); 314 | 315 | $fields = ['id', 'title', 'price', 'original_price',]; 316 | 317 | $csvExporter = new Export(); 318 | $csvExporter->build($products, $fields, ['header' => false]); 319 | $csv = $csvExporter->getReader(); 320 | $lines = explode(PHP_EOL, trim($csv->getContent())); 321 | $firstLine = $lines[0]; 322 | $this->assertNotEquals("id,title,price,original_price", $firstLine); 323 | $this->assertCount(10, $lines); 324 | $this->assertCount(count($fields), explode(',', $lines[2])); 325 | } 326 | 327 | public function testWithCustomDelimeter() 328 | { 329 | $products = Product::limit(10)->get(); 330 | 331 | $fields = ['id', 'title', 'price', 'original_price',]; 332 | 333 | $csvExporter = new Export(); 334 | $csvExporter->getWriter()->setDelimiter(';'); 335 | $csvExporter->build($products, $fields); 336 | $csv = $csvExporter->getReader(); 337 | $lines = explode(PHP_EOL, trim($csv->getContent())); 338 | $firstLine = $lines[0]; 339 | $this->assertEquals("id;title;price;original_price", $firstLine); 340 | $this->assertCount(11, $lines); 341 | $this->assertCount(count($fields), explode(';', $lines[2])); 342 | } 343 | } 344 | --------------------------------------------------------------------------------