Skip to content
Open
168 changes: 168 additions & 0 deletions src/Concerns/Relations/PerformsRelationOperations.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<?php

namespace Lomkit\Rest\Concerns\Relations;

use Illuminate\Database\Eloquent\Model;
use Lomkit\Rest\Contracts\QueryBuilder;
use Lomkit\Rest\Relations\Relation;

trait PerformsRelationOperations
{
/**
* @param Model $model
* @param Relation $relation
* @param array $mutation
* @param array $attributes
*
* @return void
*/
public function create(Model $model, Relation $relation, array $mutation = [], array $attributes = [])
{
$toPerformActionModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->applyMutation($mutation, $attributes);

$this->resource()->authorizeToAttach($model, $toPerformActionModel);

$model
->{$relation->relation}()
->syncWithoutDetaching(
[
$toPerformActionModel->getKey() => $mutation['pivot'] ?? [],
]
);
}

/**
* @param Model $model
* @param Relation $relation
* @param array $mutation
* @param array $attributes
*
* @return void
*/
public function update(Model $model, Relation $relation, array $mutation = [], array $attributes = [])
{
$toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->mutations($mutation, $attributes);

foreach ($toPerformActionModels as $toPerformActionModel) {
$this->resource()->authorizeToAttach($model, $toPerformActionModel);
}

$model->{$relation->relation}()
->syncWithoutDetaching(
collect($toPerformActionModels)
->mapWithKeys(
fn (Model $toPerformActionModel) => [$toPerformActionModel->getKey() => $mutation['pivot'] ?? []]
)->toArray()
);
}

/**
* @param Model $model
* @param Relation $relation
* @param array $mutation
* @param array $attributes
*
* @return void
*/
public function attach(Model $model, Relation $relation, array $mutation = [], array $attributes = [])
{
$toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->mutations($mutation, $attributes);

foreach ($toPerformActionModels as $toPerformActionModel) {
$this->resource()->authorizeToAttach($model, $toPerformActionModel);
}

$model->{$relation->relation}()
->attach(
collect($toPerformActionModels)
->mapWithKeys(
fn (Model $toPerformActionModel) => [$toPerformActionModel->getKey() => $mutation['pivot'] ?? []]
)->toArray()
);
}

/**
* @param Model $model
* @param Relation $relation
* @param array $mutation
* @param array $attributes
*
* @return void
*/
public function detach(Model $model, Relation $relation, array $mutation = [], array $attributes = [])
{
$toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->mutations($mutation, $attributes);

foreach ($toPerformActionModels as $toPerformActionModel) {
$this->resource()->authorizeToDetach($model, $toPerformActionModel);
}

$model->{$relation->relation}()->detach($toPerformActionModels);
}

/**
* @param Model $model
* @param Relation $relation
* @param array $mutation
* @param array $attributes
*
* @return void
*/
public function toggle(Model $model, Relation $relation, array $mutation = [], array $attributes = [])
{
$toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->mutations($mutation, $attributes);

$results = $model->{$relation->relation}()
->toggle(
collect($toPerformActionModels)
->mapWithKeys(
fn (Model $toPerformActionModel) => [$toPerformActionModel->getKey() => $mutation['pivot'] ?? []]
)->toArray(),
);

foreach ($results['attached'] as $attached) {
$this->resource()->authorizeToAttach($model, $relation->resource()::$model::find($attached));
}

foreach ($results['detached'] as $detached) {
$this->resource()->authorizeToDetach($model, $relation->resource()::$model::find($detached));
}
}

/**
* @param Model $model
* @param Relation $relation
* @param array $mutation
* @param array $attributes
* @param bool $withoutDetaching
*
* @return void
*/
public function sync(Model $model, Relation $relation, array $mutation = [], array $attributes = [], $withoutDetaching = false)
{
$toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->mutations($mutation, $attributes);

$results = $model->{$relation->relation}()
->sync(
collect($toPerformActionModels)
->mapWithKeys(
fn (Model $toPerformActionModel) => [$toPerformActionModel->getKey() => $mutation['pivot'] ?? []]
)->toArray(),
$withoutDetaching
);

foreach ($results['attached'] as $attached) {
$this->resource()->authorizeToAttach($model, $relation->resource()::$model::find($attached));
}

foreach ($results['detached'] as $detached) {
$this->resource()->authorizeToDetach($model, $relation->resource()::$model::find($detached));
}
}
}
24 changes: 22 additions & 2 deletions src/Query/Traits/PerformMutation.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ public function mutate(array $parameters = [])
*
* @return Model The mutated model.
*/
public function applyMutation(array $mutation = [], $attributes = [])
public function applyMutation(array $mutation = [], $attributes = [], $key = null)
{
$allAttributes = array_merge($attributes, $mutation['attributes'] ?? []);

if ($mutation['operation'] === 'create') {
$model = $this->resource::newModel();
} else {
$model = $this->resource::newModel()::findOrFail($mutation['key']);
$model = $this->resource::newModel()::query()->findOrFail($key ?? $mutation['key']);
}

if ($mutation['operation'] === 'create') {
Expand All @@ -74,6 +74,26 @@ public function applyMutation(array $mutation = [], $attributes = [])
);
}

/**
* Apply a mutation to the model based on the provided mutation parameters.
*
* @param array $mutation An array of mutation parameters.
* @param array $attributes Additional attributes to apply to the model.
*
* @return array<Model> The mutated model.
*/
public function mutations(array $mutation = [], $attributes = [])
{
$mutations = [];
$keys = $mutation['keys'] ?? [$mutation['key']];

foreach ($keys as $key) {
$mutations[] = $this->applyMutation($mutation, $attributes, $key);
}

return $mutations;
}

/**
* Mutate the model by applying attributes and relations.
*
Expand Down
89 changes: 12 additions & 77 deletions src/Relations/BelongsToMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
namespace Lomkit\Rest\Relations;

use Illuminate\Database\Eloquent\Model;
use InvalidArgumentException;
use Lomkit\Rest\Concerns\Relations\HasPivotFields;
use Lomkit\Rest\Contracts\QueryBuilder;
use Lomkit\Rest\Concerns\Relations\PerformsRelationOperations;
use Lomkit\Rest\Contracts\RelationResource;
use Lomkit\Rest\Http\Resource;
use Lomkit\Rest\Relations\Traits\HasMultipleResults;
Expand All @@ -14,6 +15,7 @@ class BelongsToMany extends Relation implements RelationResource
{
use HasPivotFields;
use HasMultipleResults;
use PerformsRelationOperations;

/**
* Define validation rules for the BelongsToMany relation.
Expand Down Expand Up @@ -46,82 +48,15 @@ public function rules(Resource $resource, string $prefix)
public function afterMutating(Model $model, Relation $relation, array $mutationRelations)
{
foreach ($mutationRelations[$relation->relation] as $mutationRelation) {
if ($mutationRelation['operation'] === 'detach') {
$toDetachModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->applyMutation($mutationRelation);

$this->resource()->authorizeToDetach($model, $toDetachModel);

$model
->{$relation->relation}()
->detach(
$toDetachModel->getKey()
);
} elseif ($mutationRelation['operation'] === 'attach') {
$toAttachModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->applyMutation($mutationRelation);

$this->resource()->authorizeToAttach($model, $toAttachModel);

$model
->{$relation->relation}()
->attach(
[
$toAttachModel->getKey() => $mutationRelation['pivot'] ?? [],
]
);
} elseif ($mutationRelation['operation'] === 'toggle') {
$results = $model
->{$relation->relation}()
->toggle(
[
app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->applyMutation($mutationRelation)
->getKey() => $mutationRelation['pivot'] ?? [],
]
);

foreach ($results['attached'] as $attached) {
$this->resource()->authorizeToAttach($model, $relation->resource()::$model::find($attached));
}

foreach ($results['detached'] as $detached) {
$this->resource()->authorizeToDetach($model, $relation->resource()::$model::find($detached));
}
} elseif ($mutationRelation['operation'] === 'sync') {
$results = $model
->{$relation->relation}()
->sync(
[
app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->applyMutation($mutationRelation)
->getKey() => $mutationRelation['pivot'] ?? [],
],
!isset($mutationRelation['without_detaching']) || !$mutationRelation['without_detaching']
);

foreach ($results['attached'] as $attached) {
$this->resource()->authorizeToAttach($model, $relation->resource()::$model::find($attached));
}

foreach ($results['detached'] as $detached) {
$this->resource()->authorizeToDetach($model, $relation->resource()::$model::find($detached));
}
} elseif (in_array($mutationRelation['operation'], ['create', 'update'])) {
$toAttachModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->applyMutation($mutationRelation);

$this->resource()->authorizeToAttach($model, $toAttachModel);

$model
->{$relation->relation}()
->syncWithoutDetaching(
[
$toAttachModel
->getKey() => $mutationRelation['pivot'] ?? [],
]
);
}
match ($mutationRelation['operation']) {
'create' => $this->create($model, $relation, $mutationRelation),
'update' => $this->update($model, $relation, $mutationRelation),
'attach' => $this->attach($model, $relation, $mutationRelation),
'detach' => $this->detach($model, $relation, $mutationRelation),
'toggle' => $this->toggle($model, $relation, $mutationRelation),
'sync' => $this->sync($model, $relation, $mutationRelation, withoutDetaching: !isset($mutationRelation['without_detaching']) || !$mutationRelation['without_detaching']),
default => throw new InvalidArgumentException("Unknown operation: {$mutationRelation['operation']}"),
};
}
}
}
25 changes: 15 additions & 10 deletions src/Relations/HasMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,23 @@ public function afterMutating(Model $model, Relation $relation, array $mutationR
$model->{$relation->relation}()->getForeignKeyName() => $mutationRelation['operation'] === 'detach' ? null : $model->{$relation->relation}()->getParentKey(),
];

$toPerformActionModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->applyMutation($mutationRelation, $attributes);
if ($mutationRelation['operation'] === 'create') {
$toPerformActionModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->applyMutation($mutationRelation, $attributes);

switch ($mutationRelation['operation']) {
case 'create':
case 'update':
case 'attach':
$this->resource()->authorizeToAttach($model, $toPerformActionModel);
break;
case 'detach':
$this->resource()->authorizeToAttach($model, $toPerformActionModel);
continue;
}

$toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->mutations($mutationRelation, $attributes);

foreach ($toPerformActionModels as $toPerformActionModel) {
if ($mutationRelation['operation'] === 'detach') {
$this->resource()->authorizeToDetach($model, $toPerformActionModel);
break;
} else {
$this->resource()->authorizeToAttach($model, $toPerformActionModel);
}
}
}
}
Expand Down
25 changes: 15 additions & 10 deletions src/Relations/MorphMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,23 @@ public function afterMutating(Model $model, Relation $relation, array $mutationR
$model->{$relation->relation}()->getMorphType() => $model::class,
];

$toPerformActionModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->applyMutation($mutationRelation, $attributes);
if ($mutationRelation['operation'] === 'create') {
$toPerformActionModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->applyMutation($mutationRelation, $attributes);

switch ($mutationRelation['operation']) {
case 'create':
case 'update':
case 'attach':
$this->resource()->authorizeToAttach($model, $toPerformActionModel);
break;
case 'detach':
$this->resource()->authorizeToAttach($model, $toPerformActionModel);
continue;
}

$toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()])
->mutations($mutationRelation, $attributes);

foreach ($toPerformActionModels as $toPerformActionModel) {
if ($mutationRelation['operation'] === 'detach') {
$this->resource()->authorizeToDetach($model, $toPerformActionModel);
break;
} else {
$this->resource()->authorizeToAttach($model, $toPerformActionModel);
}
}
}
}
Expand Down
Loading