Skip to content

Commit

Permalink
New concern with count (#1682)
Browse files Browse the repository at this point in the history
* Added new concern WithCount. Allows export classes that implement the FromQuery concern, to provide their custom count implementation, instead of relying to the query's count.

* Added tests for the WithCount concern.

* Updated docs regarding the WithCount concern.

* Removed code duplication and reduced LOC.

* Fixed test on Laravel 5.5.

* Package orchestra/database is needed for Laravel 5.5 builds.

* Installing orchestra/database unconditionally.

* Renamed trait WithCount to the more expressive WithCustomQuerySize.

* Fixed some code styling issues.

* ConsoleServiceProvider is always available. No need to check for its existence.

* Renamed `count` to `querySize`.

* Added explanatory comments in the `querySize` method's docblock.
  • Loading branch information
lasmanis authored and patrickbrouwers committed Jun 12, 2018
1 parent 203ec61 commit 61bb806
Show file tree
Hide file tree
Showing 11 changed files with 243 additions and 8 deletions.
12 changes: 6 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ matrix:
include:
# Laravel 5.5
- php: 7.0
env: LARAVEL=5.5.* TESTBENCH=3.5.* PHPUNIT=~6.0 COVERAGE=0
env: LARAVEL=5.5.* TESTBENCH=3.5.* ORCHESTRA_DATABASE=3.5.* PHPUNIT=~6.0 COVERAGE=0
- php: 7.1
env: LARAVEL=5.5.* TESTBENCH=3.5.* PHPUNIT=~6.0 COVERAGE=0
env: LARAVEL=5.5.* TESTBENCH=3.5.* ORCHESTRA_DATABASE=3.5.* PHPUNIT=~6.0 COVERAGE=0
- php: 7.2
env: LARAVEL=5.5.* TESTBENCH=3.5.* PHPUNIT=~6.0 COVERAGE=0
env: LARAVEL=5.5.* TESTBENCH=3.5.* ORCHESTRA_DATABASE=3.5.* PHPUNIT=~6.0 COVERAGE=0

# Laravel 5.6
- php: 7.1
env: LARAVEL=5.6.* TESTBENCH=3.6.* PHPUNIT=~7.0 COVERAGE=0
env: LARAVEL=5.6.* TESTBENCH=3.6.* ORCHESTRA_DATABASE=3.6.* PHPUNIT=~7.0 COVERAGE=0
- php: 7.2
env: LARAVEL=5.6.* TESTBENCH=3.6.* PHPUNIT=~7.0 COVERAGE=1
env: LARAVEL=5.6.* TESTBENCH=3.6.* ORCHESTRA_DATABASE=3.6.* PHPUNIT=~7.0 COVERAGE=1

before_install:
- composer self-update --stable --no-interaction
- composer require orchestra/testbench:$TESTBENCH phpunit/phpunit:$PHPUNIT --no-update --no-interaction --dev
- composer require orchestra/testbench:$TESTBENCH orchestra/database:$ORCHESTRA_DATABASE phpunit/phpunit:$PHPUNIT --no-update --no-interaction --dev
- mysql -e 'CREATE DATABASE IF NOT EXISTS laravel_excel;'

install:
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
},
"require-dev": {
"phpunit/phpunit": "~7.0",
"orchestra/testbench": "^3.6"
"orchestra/testbench": "^3.6",
"orchestra/database": "^3.6"
},
"autoload": {
"psr-4": {
Expand Down
1 change: 1 addition & 0 deletions docs/export/concerns.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
|`Maatwebsite\Excel\Concerns\ShouldAutoSize`| Auto-size the columns in the worksheet. |
|`Maatwebsite\Excel\Concerns\WithStrictNullComparison`| Uses strict comparisions when testing cells for null value. |
|`Maatwebsite\Excel\Concerns\WithEvents`| Register events to hook into the PhpSpreadsheet process. |
|`Maatwebsite\Excel\Concerns\WithCustomQuerySize`| Allows Exportables that implement the FromQuery concern, to provide their own custom query size. |

### Traits

Expand Down
17 changes: 17 additions & 0 deletions src/Concerns/WithCustomQuerySize.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Maatwebsite\Excel\Concerns;

interface WithCustomQuerySize
{
/**
* Queued exportables are processed in chunks; each chunk being a job pushed to the queue by the QueuedWriter.
* In case of exportables that implement the FromQuery concern, the number of jobs is calculated by dividing the $query->count() by the chunk size.
* Depending on the implementation of the query() method (eg. When using a groupBy clause), this calculation might not be correct.
*
* When this is the case, you should use this method to provide a custom calculation of the query size.
*
* @return int
*/
public function querySize(): int;
}
5 changes: 4 additions & 1 deletion src/QueuedWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Jobs\AppendQueryToSheet;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Maatwebsite\Excel\Concerns\WithCustomQuerySize;

class QueuedWriter
{
Expand Down Expand Up @@ -130,8 +131,10 @@ private function exportQuery(
) {
$query = $export->query();

$count = $export instanceof WithCustomQuerySize ? $export->querySize() : $query->count();
$spins = ceil($count / $this->chunkSize);

$jobs = new Collection();
$spins = ceil($query->count() / $this->chunkSize);

for ($page = 1; $page <= $spins; $page++) {
$serializedQuery = new SerializedQuery(
Expand Down
58 changes: 58 additions & 0 deletions tests/Concerns/WithCustomQuerySizeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace Maatwebsite\Excel\Tests\Concerns;

use Maatwebsite\Excel\Tests\TestCase;
use Maatwebsite\Excel\Tests\Data\Stubs\Database\User;
use Maatwebsite\Excel\Tests\Data\Stubs\Database\Group;
use Maatwebsite\Excel\Tests\Data\Stubs\AfterQueueExportJob;
use Maatwebsite\Excel\Tests\Data\Stubs\FromQueryWithCustomQuerySize;

class WithCustomQuerySizeTest extends TestCase
{
/**
* Setup the test environment.
*/
protected function setUp()
{
parent::setUp();

$this->loadLaravelMigrations(['--database' => 'testing']);
$this->loadMigrationsFrom(dirname(__DIR__) . '/Data/Stubs/Database/Migrations');
$this->withFactories(dirname(__DIR__) . '/Data/Stubs/Database/Factories');

factory(Group::class)->times(5)->create()->each(function ($group) {
$group->users()->attach(factory(User::class)->times(rand(1, 3))->create());
});

config()->set('excel.exports.chunk_size', 2);
}

/**
* @test
*/
public function can_export_with_custom_count()
{
$export = new FromQueryWithCustomQuerySize();

$export->queue('export-from-query-with-count.xlsx', null, 'Xlsx')->chain([
new AfterQueueExportJob(dirname(__DIR__) . '/Data/Disks/Local/export-from-query-with-count.xlsx'),
]);

$actual = $this->readAsArray(dirname(__DIR__) . '/Data/Disks/Local/export-from-query-with-count.xlsx', 'Xlsx');

$this->assertCount(Group::count(), $actual);
}

/**
* {@inheritdoc}
*/
protected function getPackageProviders($app)
{
return [
\Orchestra\Database\ConsoleServiceProvider::class,
];

return parent::getPackageAliases($app);
}
}
20 changes: 20 additions & 0 deletions tests/Data/Stubs/Database/Factories/GroupFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

use Faker\Generator as Faker;
use Maatwebsite\Excel\Tests\Data\Stubs\Database\Group;

/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/
$factory->define(Group::class, function (Faker $faker) {
return [
'name' => $faker->word,
];
});
16 changes: 16 additions & 0 deletions tests/Data/Stubs/Database/Group.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Maatwebsite\Excel\Tests\Data\Stubs\Database;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Group extends Model
{
protected $guarded = [];

public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateGroupsTable extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
Schema::create('groups', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}

/**
* Reverse the migrations.
*/
public function down()
{
Schema::dropIfExists('groups');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateGroupUserTable extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
Schema::create('group_user', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('group_id');
$table->unsignedInteger('user_id');

$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');

$table->foreign('group_id')
->references('id')
->on('groups')
->onDelete('cascade');
});
}

/**
* Reverse the migrations.
*/
public function down()
{
Schema::dropIfExists('group_user');
}
}
53 changes: 53 additions & 0 deletions tests/Data/Stubs/FromQueryWithCustomQuerySize.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace Maatwebsite\Excel\Tests\Data\Stubs;

use Illuminate\Support\Facades\DB;
use Illuminate\Database\Query\Builder;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\Exportable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithCustomQuerySize;
use Maatwebsite\Excel\Tests\Data\Stubs\Database\Group;

class FromQueryWithCustomQuerySize implements FromQuery, WithCustomQuerySize, WithMapping, ShouldQueue
{
use Exportable;

/**
* @return Builder
*/
public function query()
{
$query = Group::with('users')
->join('group_user', 'groups.id', '=', 'group_user.group_id')
->select('groups.*', DB::raw('count(group_user.user_id) as number_of_users'))
->groupBy('groups.id')
->orderBy('number_of_users');

return $query;
}

/**
* @return int
*/
public function querySize(): int
{
return Group::has('users')->count();
}

/**
* @param Group $row
*
* @return array
*/
public function map($row): array
{
return [
$row->id,
$row->name,
$row->number_of_users,
];
}
}

0 comments on commit 61bb806

Please sign in to comment.