Skip to content

Commit

Permalink
feat: added a possibility to create rows without unique attributes (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
lapaliv committed Jun 6, 2023
1 parent 1944084 commit 497491d
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 18 deletions.
34 changes: 27 additions & 7 deletions src/Bulk.php
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,14 @@ public function updateAllExcept(array $attributes): static
public function create(iterable $rows, bool $ignoreConflicts = false): static
{
$storageKey = $ignoreConflicts ? 'createOrIgnore' : 'create';
$this->accumulate($storageKey, $rows);
$this->accumulate(
$storageKey,
$rows,
$this->getEventDispatcher()->hasListeners(BulkEventEnum::saved())
|| $this->getEventDispatcher()->hasListeners(BulkEventEnum::created())
|| $this->getEventDispatcher()->hasListeners(BulkEventEnum::deleted())
|| !empty($this->model->getTouchedRelations())
);

foreach ($this->getReadyChunks($storageKey, force: true) as $accumulation) {
$this->runCreateScenario($accumulation, $ignoreConflicts);
Expand All @@ -394,7 +401,13 @@ public function create(iterable $rows, bool $ignoreConflicts = false): static
public function createOrAccumulate(iterable $rows, bool $ignoreConflicts = false): static
{
$storageKey = $ignoreConflicts ? 'createOrIgnore' : 'create';
$this->accumulate($storageKey, $rows);
$this->accumulate(
$storageKey,
$rows,
$this->getEventDispatcher()->hasListeners(BulkEventEnum::saved())
|| $this->getEventDispatcher()->hasListeners(BulkEventEnum::created())
|| $this->getEventDispatcher()->hasListeners(BulkEventEnum::deleted())
);

foreach ($this->getReadyChunks($storageKey) as $accumulation) {
$this->runCreateScenario($accumulation, $ignoreConflicts);
Expand Down Expand Up @@ -660,19 +673,26 @@ public function withTrashed(): static
*
* @param string $storageKey
* @param iterable<int|string, array<string, mixed>|Model|object|stdClass|TModel> $rows
* @param bool $uniqueAttributesAreRequired
*
* @return void
*
* @throws BulkException
* @throws BulkBindingResolution
*/
private function accumulate(string $storageKey, iterable $rows): void
private function accumulate(string $storageKey, iterable $rows, bool $uniqueAttributesAreRequired = true): void
{
foreach ($rows as $row) {
$model = $this->convertRowToModel($row);
[$uniqueAttributesIndex, $uniqueAttributes] = $this->getUniqueAttributesForModel($row, $model);

$this->storage[$storageKey]['i' . $uniqueAttributesIndex] ??= new BulkAccumulationEntity($uniqueAttributes);
$this->storage[$storageKey]['i' . $uniqueAttributesIndex]->rows[] = new BulkAccumulationItemEntity($row, $model);
if ($uniqueAttributesAreRequired) {
[$uniqueAttributesIndex, $uniqueAttributes] = $this->getUniqueAttributesForModel($row, $model);

$this->storage[$storageKey]['i' . $uniqueAttributesIndex] ??= new BulkAccumulationEntity($uniqueAttributes);
$this->storage[$storageKey]['i' . $uniqueAttributesIndex]->rows[] = new BulkAccumulationItemEntity($row, $model);
} else {
$this->storage[$storageKey]['no_unique_attributes'] ??= new BulkAccumulationEntity([]);
$this->storage[$storageKey]['no_unique_attributes']->rows[] = new BulkAccumulationItemEntity($row, $model);
}
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/Exceptions/BulkIdentifierDidNotFind.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ class BulkIdentifierDidNotFind extends RuntimeException implements BulkException
{
public function __construct(private mixed $row, private array $uniqueAttributes)
{
parent::__construct('Unique attributes did not find for the row');
parent::__construct(
'Unique attributes did not find for the row. ' .
'Please pass them via the `uniqueBy` method or ' .
'turn off -ed events if you try to use `create`/`createOrAccumulate` methods'
);
}

public function getRow(): mixed
Expand Down
13 changes: 4 additions & 9 deletions src/Features/GetInsertBuilderFeature.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ public function handle(
continue;
}

$columns = [];
$array = $this->convertModelToArray($row, $columns, $dateFields, $deletedAtColumn);
$result->addValue($array);
}
Expand Down Expand Up @@ -100,15 +99,11 @@ private function freshTimestamps(
}

if ($row->model->usesTimestamps()) {
if (!isset($columns[$this->createdAtColumn])) {
$columns[$this->createdAtColumn] = $this->createdAtColumn;
$result[$this->createdAtColumn] = $this->createdAt;
}
$columns[$this->createdAtColumn] ??= $this->createdAtColumn;
$columns[$this->updatedAtColumn] ??= $this->updatedAtColumn;

if (!isset($columns[$this->updatedAtColumn])) {
$columns[$this->updatedAtColumn] = $this->updatedAtColumn;
$result[$this->updatedAtColumn] = $this->updatedAt;
}
$result[$this->updatedAtColumn] ??= $this->updatedAt;
$result[$this->createdAtColumn] ??= $this->createdAt;
}
}
}
53 changes: 53 additions & 0 deletions tests/Unit/Bulk/Create/CreateOrAccumulateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

use JsonException;
use Lapaliv\BulkUpsert\Contracts\BulkException;
use Lapaliv\BulkUpsert\Exceptions\BulkIdentifierDidNotFind;
use Lapaliv\BulkUpsert\Tests\App\Models\MySqlUser;
use Lapaliv\BulkUpsert\Tests\App\Models\PostgreSqlUser;
use Lapaliv\BulkUpsert\Tests\App\Models\User;
use Lapaliv\BulkUpsert\Tests\App\Observers\UserObserver;
use Lapaliv\BulkUpsert\Tests\TestCase;
use Lapaliv\BulkUpsert\Tests\Unit\UserTestTrait;

Expand Down Expand Up @@ -135,6 +137,57 @@ public function testSaveAccumulation(string $model): void
);
}

/**
* @param class-string<User> $model
*
* @return void
*
* @throws BulkException
*
* @dataProvider userModelsDataProvider
*/
public function testCreatingWithoutUniqueAttributesWithEvents(string $model): void
{
// arrange
$users = $this->userGenerator->makeCollection(2);
$model::observe(UserObserver::class);
$sut = $model::query()->bulk();

// assert
$this->expectException(BulkIdentifierDidNotFind::class);

// act
$sut->createOrAccumulate($users);
}

/**
* @param class-string<User> $model
*
* @return void
*
* @throws BulkException
*
* @dataProvider userModelsDataProvider
*/
public function testCreatingWithoutUniqueAttributesWithoutEvents(string $model): void
{
// arrange
$users = $this->userGenerator
->setModel($model)
->makeCollection(2);
$sut = $model::query()
->bulk()
->chunk(2);

// act
$sut->createOrAccumulate($users);

// assert
$users->each(
fn (User $user) => $this->userWasCreated($user)
);
}

public function userModelsDataProvider(): array
{
return [
Expand Down
51 changes: 51 additions & 0 deletions tests/Unit/Bulk/Create/CreateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
use Illuminate\Support\Str;
use JsonException;
use Lapaliv\BulkUpsert\Contracts\BulkException;
use Lapaliv\BulkUpsert\Exceptions\BulkIdentifierDidNotFind;
use Lapaliv\BulkUpsert\Tests\App\Models\MySqlUser;
use Lapaliv\BulkUpsert\Tests\App\Models\PostgreSqlUser;
use Lapaliv\BulkUpsert\Tests\App\Models\User;
use Lapaliv\BulkUpsert\Tests\App\Observers\UserObserver;
use Lapaliv\BulkUpsert\Tests\TestCase;
use Lapaliv\BulkUpsert\Tests\Unit\UserTestTrait;

Expand Down Expand Up @@ -170,6 +172,55 @@ public function toArray(): array {
$this->userWasCreated($user);
}

/**
* @param class-string<User> $model
*
* @return void
*
* @throws BulkException
*
* @dataProvider userModelsDataProvider
*/
public function testCreatingWithoutUniqueAttributesWithEvents(string $model): void
{
// arrange
$users = $this->userGenerator->makeCollection(2);
$model::observe(UserObserver::class);
$sut = $model::query()->bulk();

// assert
$this->expectException(BulkIdentifierDidNotFind::class);

// act
$sut->create($users);
}

/**
* @param class-string<User> $model
*
* @return void
*
* @throws BulkException
*
* @dataProvider userModelsDataProvider
*/
public function testCreatingWithoutUniqueAttributesWithoutEvents(string $model): void
{
// arrange
$users = $this->userGenerator
->setModel($model)
->makeCollection(2);
$sut = $model::query()->bulk();

// act
$sut->create($users);

// assert
$users->each(
fn (User $user) => $this->userWasCreated($user)
);
}

public function userModelsDataProvider(): array
{
return [
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/UserTestTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protected function userWasCreated(User $user): void
'posts_count' => $user->posts_count,
'is_admin' => $user->is_admin,
'balance' => $user->balance,
'birthday' => $user->birthday,
'birthday' => $user->birthday?->toDateString(),
'phones' => $user->phones,
'last_visited_at' => $user->last_visited_at,
'created_at' => Carbon::now()->toDateTimeString(),
Expand Down

0 comments on commit 497491d

Please sign in to comment.