Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Eloquent/ModelSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,16 @@ public function typename(): string
return $this->getTypename();
}

/**
* Get the plural typename for the model.
*
* @return string
*/
public function pluralTypename(): string
{
return Utils::pluralTypename($this->getModel());
}

/**
* Get the key (ID) field.
*
Expand Down
107 changes: 107 additions & 0 deletions src/Mutations/DeleteManyMutation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

namespace Bakery\Mutations;

use Bakery\Utils\Utils;
use Bakery\Eloquent\ModelSchema;
use Bakery\Support\TypeRegistry;
use Bakery\Traits\FiltersQueries;
use Bakery\Types\Definitions\Type;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Model;
use GraphQL\Type\Definition\ResolveInfo;

class DeleteManyMutation extends Mutation
{
use FiltersQueries;

/** @var \Bakery\Eloquent\ModelSchema */
protected $modelSchema;

/**
* EloquentMutation constructor.
*
* @param \Bakery\Support\TypeRegistry $registry
* @param \Bakery\Eloquent\ModelSchema $modelSchema
*/
public function __construct(TypeRegistry $registry, ModelSchema $modelSchema = null)
{
parent::__construct($registry);

if ($modelSchema) {
$this->modelSchema = $modelSchema;
} elseif (is_string($this->modelSchema)) {
$this->modelSchema = $this->registry->getModelSchema($this->modelSchema);
}

Utils::invariant(
$this->modelSchema instanceof ModelSchema,
'Model schema on '.get_class($this).' should be an instance of '.ModelSchema::class
);
}

/**
* Get the name of the mutation.
*
* @return string
*/
public function name(): string
{
if (isset($this->name)) {
return $this->name;
}

return 'deleteMany'.$this->modelSchema->pluralTypename();
}

/**
* The return type of the mutation.
*
* @return Type
*/
public function type(): Type
{
return $this->registry->int();
}

/**
* The arguments of the mutation.
*
* @return array
*/
public function args(): array
{
return [
'filter' => $this->registry->type($this->modelSchema->typename().'Filter'),
];
}

/**
* Resolve the mutation.
*
* @param mixed $root
* @param mixed $args
* @param mixed $context
* @param \GraphQL\Type\Definition\ResolveInfo $info
* @return int
*/
public function resolve($root, array $args, $context, ResolveInfo $info)
{
$query = $this->modelSchema->getQuery();

$this->applyFilters($query, $args['filter']);

$count = $query->count();

DB::transaction(function () use ($query) {
$query->each(function (Model $model) {
$modelSchema = $this->registry->getSchemaForModel($model);

$modelSchema->authorizeToDelete();
$model->delete();
});
});

return $count;
}
}
111 changes: 111 additions & 0 deletions src/Mutations/UpdateManyMutation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

namespace Bakery\Mutations;

use Bakery\Utils\Utils;
use Bakery\Eloquent\ModelSchema;
use Bakery\Support\TypeRegistry;
use Bakery\Traits\FiltersQueries;
use Bakery\Types\Definitions\Type;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Model;
use GraphQL\Type\Definition\ResolveInfo;

class UpdateManyMutation extends Mutation
{
use FiltersQueries;

/** @var \Bakery\Eloquent\ModelSchema */
protected $modelSchema;

/**
* EloquentMutation constructor.
*
* @param \Bakery\Support\TypeRegistry $registry
* @param \Bakery\Eloquent\ModelSchema $modelSchema
*/
public function __construct(TypeRegistry $registry, ModelSchema $modelSchema = null)
{
parent::__construct($registry);

if ($modelSchema) {
$this->modelSchema = $modelSchema;
} elseif (is_string($this->modelSchema)) {
$this->modelSchema = $this->registry->getModelSchema($this->modelSchema);
}

Utils::invariant(
$this->modelSchema instanceof ModelSchema,
'Model schema on '.get_class($this).' should be an instance of '.ModelSchema::class
);
}

/**
* Get the name of the mutation.
*
* @return string
*/
public function name(): string
{
if (isset($this->name)) {
return $this->name;
}

return 'updateMany'.$this->modelSchema->pluralTypename();
}

/**
* The return type of the mutation.
*
* @return Type
*/
public function type(): Type
{
return $this->registry->int();
}

/**
* The arguments of the mutation.
*
* @return array
*/
public function args(): array
{
$inputTypeName = 'Update'.$this->modelSchema->typename().'Input';

return [
'filter' => $this->registry->type($this->modelSchema->typename().'Filter'),
'input' => $this->registry->type($inputTypeName),
];
}

/**
* Resolve the mutation.
*
* @param mixed $root
* @param mixed $args
* @param mixed $context
* @param \GraphQL\Type\Definition\ResolveInfo $info
* @return int
*/
public function resolve($root, array $args, $context, ResolveInfo $info)
{
$input = $args['input'];

$query = $this->modelSchema->getQuery();

$this->applyFilters($query, $args['filter']);

$count = $query->count();

DB::transaction(function () use ($query, $input) {
$query->each(function (Model $model) use ($input) {
$modelSchema = $this->registry->getSchemaForModel($model);

return $modelSchema->updateIfAuthorized($input);
});
});

return $count;
}
}
8 changes: 8 additions & 0 deletions src/Support/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use Bakery\Queries\SingleEntityQuery;
use GraphQL\Type\Definition\Directive;
use GraphQL\Type\Definition\ObjectType;
use Bakery\Mutations\DeleteManyMutation;
use Bakery\Mutations\UpdateManyMutation;
use Bakery\Mutations\AttachPivotMutation;
use Bakery\Mutations\DetachPivotMutation;
use GraphQL\Type\Schema as GraphQLSchema;
Expand Down Expand Up @@ -421,8 +423,14 @@ protected function getModelMutations(): Collection
$updateMutation = new UpdateMutation($this->registry, $modelSchema);
$mutations->put($updateMutation->getName(), $updateMutation);

$updateManyMutation = new UpdateManyMutation($this->registry, $modelSchema);
$mutations->put($updateManyMutation->getName(), $updateManyMutation);

$deleteMutation = new DeleteMutation($this->registry, $modelSchema);
$mutations->put($deleteMutation->getName(), $deleteMutation);

$deleteManyMutation = new DeleteManyMutation($this->registry, $modelSchema);
$mutations->put($deleteManyMutation->getName(), $deleteManyMutation);
}

foreach ($pivotRelations as $relation) {
Expand Down
56 changes: 56 additions & 0 deletions tests/Feature/DeleteManyMutationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Bakery\Tests\Feature;

use Bakery\Tests\IntegrationTest;
use Bakery\Tests\Fixtures\Models\Article;

class DeleteManyMutationTest extends IntegrationTest
{
public function setUp(): void
{
parent::setUp();

$this->authenticate();
}

/** @test */
public function it_can_delete_many_models()
{
[$article, $articleTwo, $articleThree] = factory(Article::class, 3)->create();

$response = $this->graphql('mutation($filter: ArticleFilter!) { deleteManyArticles(filter: $filter) }', [
'filter' => [
'title' => $article->title,
],
]);

$response->assertJsonFragment(['deleteManyArticles' => 1]);

$articles = Article::all();
$this->assertFalse($articles->contains($article));
$this->assertTrue($articles->contains($articleTwo));
$this->assertTrue($articles->contains($articleThree));
}

/** @test */
public function it_can_delete_many_models_with_relation_filter()
{
[$article, $articleTwo, $articleThree] = factory(Article::class, 3)->create();

$response = $this->graphql('mutation($filter: ArticleFilter!) { deleteManyArticles(filter: $filter) }', [
'filter' => [
'user' => [
'email' => $article->user->email,
],
],
]);

$response->assertJsonFragment(['deleteManyArticles' => 1]);

$articles = Article::all();
$this->assertFalse($articles->contains($article));
$this->assertTrue($articles->contains($articleTwo));
$this->assertTrue($articles->contains($articleThree));
}
}
62 changes: 62 additions & 0 deletions tests/Feature/UpdateManyMutationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace Bakery\Tests\Feature;

use Bakery\Tests\IntegrationTest;
use Bakery\Tests\Fixtures\Models\Article;

class UpdateManyMutationTest extends IntegrationTest
{
public function setUp(): void
{
parent::setUp();

$this->authenticate();
}

/** @test */
public function it_can_update_many_models()
{
[$article] = factory(Article::class, 3)->create();

$response = $this->graphql('mutation($input: UpdateArticleInput!, $filter: ArticleFilter!) { updateManyArticles(input: $input, filter: $filter) }', [
'input' => [
'title' => 'Hello world',
],
'filter' => [
'title' => $article->title,
],
]);

$response->assertJsonFragment(['updateManyArticles' => 1]);

[$article, $articleTwo, $articleThree] = Article::all();
$this->assertEquals('Hello world', $article->title);
$this->assertNotEquals('Hello world', $articleTwo->title);
$this->assertNotEquals('Hello world', $articleThree->title);
}

/** @test */
public function it_can_update_many_models_with_relation_filter()
{
[$article] = factory(Article::class, 3)->create();

$response = $this->graphql('mutation($input: UpdateArticleInput!, $filter: ArticleFilter!) { updateManyArticles(input: $input, filter: $filter) }', [
'input' => [
'title' => 'Hello world',
],
'filter' => [
'user' => [
'email' => $article->user->email,
],
],
]);

$response->assertJsonFragment(['updateManyArticles' => 1]);

[$article, $articleTwo, $articleThree] = Article::all();
$this->assertEquals('Hello world', $article->title);
$this->assertNotEquals('Hello world', $articleTwo->title);
$this->assertNotEquals('Hello world', $articleThree->title);
}
}