From 8b261a07df8249d39ef68758d0b88c5dd754de79 Mon Sep 17 00:00:00 2001 From: kolirt Date: Wed, 11 Sep 2024 12:39:30 +0300 Subject: [PATCH] add saving MorphOne and MorphMany relations --- README.md | 27 +++-- composer.json | 2 +- src/Traits/MasterModel.php | 232 ++++++++++++++++++------------------- 3 files changed, 130 insertions(+), 131 deletions(-) diff --git a/README.md b/README.md index b720c13..f48b358 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ code complexity and enhancing performance. - [Use cases](#use-cases) - [Saving files](#saving-files) - [Deleting files](#deleting-files) - - [Saving `HasOne` relation](#saving-hasone-relation) - - [Saving `HasMany` relations](#saving-hasmany-relations) - - [Saving `HasMany` relations with `sync` mode](#saving-hasmany-relations-with-sync-mode) + - [Saving `HasOne`, `MorphOne` relations](#saving-hasone-morphone-relations) + - [Saving `HasMany`, `MorphMany` relations](#saving-hasmany-morphmany-relations) + - [Saving `HasMany`, `MorphMany` relations with `sync` mode](#saving-hasmany-morphmany-relations-with-sync-mode) - [FAQ](#faq) - [License](#license) - [Other packages](#other-packages) @@ -144,14 +144,14 @@ $item->delete(); ``` -### Saving `HasOne` relation -You can **save** `HasOne` relation in the same way as a file. If relation exists, it will be updated, otherwise it will be created +### Saving `HasOne`, `MorphOne` relations +You can **save** `HasOne`, `MorphOne` relations in the same way as a file. If relation exists, it will be updated, otherwise it will be created ```php $item = Item::query()->first(); $item->update([ - 'phone' => [ // hasOne relation + 'phone' => [ // hasOne, morphOne relation 'number' => '1234567890' ] ]); @@ -163,19 +163,18 @@ You can also **delete** the relation by setting it to `null` $item = Item::query()->first(); $item->update([ - 'phone' => null // hasOne relation + 'phone' => null // hasOne, morphOne relation ]); ``` - -### Saving `HasMany` relations -You can **save** `HasMany` relations in the same way as a file. If relations exists, it will be updated, otherwise it will be created +### Saving `HasMany`, `MorphMany` relations +You can **save** `HasMany`, `MorphMany` relations in the same way as a file. If relations exists, it will be updated, otherwise it will be created ```php $item = Item::query()->first(); $item->update([ - 'phones' => [ // hasMany relations + 'phones' => [ // hasMany, morphMany relations [ // will be created 'number' => '1234567890' ], @@ -188,14 +187,14 @@ $item->update([ ``` -### Saving `HasMany` relations with `sync` mode -You can also **sync** `HasMany` relations. Unspecified relations will be deleted +### Saving `HasMany`, `MorphMany` relations with `sync` mode +You can also **sync** `HasMany`, `MorphMany` relations. Unspecified relations will be deleted ```php $item = Item::query()->first(); $item->update([ - 'phones' => [ // hasMany relations + 'phones' => [ // hasMany, morphMany relations 'mode' => 'sync', // not specified relations will be deleted 'value' => [ [ // will be created diff --git a/composer.json b/composer.json index d8fdbe3..f0288e3 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ ], "homepage": "https://github.com/kolirt/laravel-master-model", "license": "MIT", - "version": "4.0.0", + "version": "4.0.1", "authors": [ { "name": "kolirt" diff --git a/src/Traits/MasterModel.php b/src/Traits/MasterModel.php index d5d4a7d..df8dd08 100644 --- a/src/Traits/MasterModel.php +++ b/src/Traits/MasterModel.php @@ -88,22 +88,18 @@ public function fill(array $attributes) foreach ($attributes as $key => $value) { if ($this->isRelation($key)) { $relation = $this->$key(); - if ( - $relation instanceof \Illuminate\Database\Eloquent\Relations\HasOne - ) { - $this->relations_to_save[] = [ - 'relation_name' => $key, - 'relation' => $relation, - 'value' => $value - ]; - } else if ( - $relation instanceof \Illuminate\Database\Eloquent\Relations\HasMany - ) { - $this->relations_to_save[] = [ - 'relation_name' => $key, - 'relation' => $relation, - 'value' => $value - ]; + + switch (true) { + case $relation instanceof \Illuminate\Database\Eloquent\Relations\HasOne: + case $relation instanceof \Illuminate\Database\Eloquent\Relations\MorphOne: + case $relation instanceof \Illuminate\Database\Eloquent\Relations\HasMany: + case $relation instanceof \Illuminate\Database\Eloquent\Relations\MorphMany: + $this->relations_to_save[] = [ + 'relation_name' => $key, + 'relation' => $relation, + 'value' => $value + ]; + break; } } } @@ -165,119 +161,123 @@ public function save(array $options = []) 'value' => $value, ] = $relation_to_save; - /*dd( - $relation->getParentKey(), - $relation->getForeignKeyName(), - $relation->getLocalKeyName(), - $relation->getExistenceCompareKey(), - $relation->getQualifiedForeignKeyName(), - $relation->getQualifiedParentKeyName(), - $value, - );*/ - - /** - * Save HasOne relation - */ - if ( - $relation instanceof \Illuminate\Database\Eloquent\Relations\HasOne - ) { - $parent = $relation->getParent(); - - if (is_null($value)) { - if ( - $parent->relationLoaded($relation_name) && - $parent->getRelation($relation_name) - ) { - $parent->getRelation($relation_name)->delete(); - $this->unsetRelation($relation_name); - } else { - $relation->delete(); - } - } else { - if ( - $parent->relationLoaded($relation_name) && - $parent->getRelation($relation_name) - ) { - $parent->getRelation($relation_name)->update($value); - } else { - $relation_model = $relation->getRelated()->newQuery() - ->where($relation->getForeignKeyName(), $relation->getParentKey()) - ->first(); - - if ($relation_model) { - $relation_model->update($value); + switch (true) { + /** + * Save HasOne relation + * Save MorphOne relation + */ + case $relation instanceof \Illuminate\Database\Eloquent\Relations\HasOne: + case $relation instanceof \Illuminate\Database\Eloquent\Relations\MorphOne: + $parent = $relation->getParent(); + + if (is_null($value)) { + if ( + $parent->relationLoaded($relation_name) && + $parent->getRelation($relation_name) + ) { + $parent->getRelation($relation_name)->delete(); + $this->unsetRelation($relation_name); } else { - $relation_model = $relation->create($value); + $relation->delete(); } + } else { + if ( + $parent->relationLoaded($relation_name) && + $parent->getRelation($relation_name) + ) { + $parent->getRelation($relation_name)->update($value); + } else { + $relation_model = $relation->first(); - $this->setRelation($relation_name, $relation_model); - } - } - - continue; - } - - /** - * Save HasMany relation - */ - if ( - $relation instanceof \Illuminate\Database\Eloquent\Relations\HasMany - ) { - $mode = $value['mode'] ?? null; - $value = $value['value'] ?? $value; - - $parent = $relation->getParent(); - $loaded_relations = match (true) { - $parent->relationLoaded($relation_name) => $parent->getRelation($relation_name), - $mode === 'sync' => $relation->get(), - default => collect() - }; - - $values_to_update = array_filter($value, fn($item) => isset($item[$relation->getLocalKeyName()])); - $new_values = array_filter($value, fn($item) => !isset($item[$relation->getLocalKeyName()])); - - $updated_items = collect(); - - if (count($values_to_update)) { - foreach ($values_to_update as $value_to_update) { - $relation_model = $loaded_relations->firstWhere($relation->getLocalKeyName(), $value_to_update[$relation->getLocalKeyName()]); + if ($relation_model) { + $relation_model->update($value); + } else { + $relation_model = $relation->create($value); + } - if (!$relation_model) { - $relation_model = $relation - ->where($relation->getLocalKeyName(), $value_to_update[$relation->getLocalKeyName()]) - ->first(); + $this->setRelation($relation_name, $relation_model); } + } - unset($value_to_update[$relation->getLocalKeyName()]); + break; + + /** + * Save HasMany relation + * Save MorphMany relation + */ + case $relation instanceof \Illuminate\Database\Eloquent\Relations\HasMany: + case $relation instanceof \Illuminate\Database\Eloquent\Relations\MorphMany: + $mode = $value['mode'] ?? null; + $value = key_exists('value', $value) ? $value['value'] : $value; + + $parent = $relation->getParent(); + + if (is_null($value)) { + if ($mode === 'sync') { + if ( + $parent->relationLoaded($relation_name) && + $parent->getRelation($relation_name) + ) { + $parent->getRelation($relation_name)->each->delete(); + $this->unsetRelation($relation_name); + } else { + $relation->delete(); + } + } + } else { + $loaded_relations = match (true) { + $parent->relationLoaded($relation_name) => $parent->getRelation($relation_name), + $mode === 'sync' => $relation->get(), + default => collect() + }; + + $values_to_update = array_filter($value, fn($item) => isset($item[$relation->getLocalKeyName()])); + $new_values = array_filter($value, fn($item) => !isset($item[$relation->getLocalKeyName()])); + + $updated_items = collect(); + + if (count($values_to_update)) { + foreach ($values_to_update as $value_to_update) { + $relation_model = $loaded_relations->firstWhere($relation->getLocalKeyName(), $value_to_update[$relation->getLocalKeyName()]); + + if (!$relation_model) { + $relation_model = $relation + ->where($relation->getLocalKeyName(), $value_to_update[$relation->getLocalKeyName()]) + ->first(); + } + + unset($value_to_update[$relation->getLocalKeyName()]); + + if ($relation_model) { + $relation_model->update($value_to_update); + $updated_items[] = $relation_model; + } else { + $new_values[] = $value_to_update; + } + } + } - if ($relation_model) { - $relation_model->update($value_to_update); - $updated_items[] = $relation_model; - } else { - $new_values[] = $value_to_update; + if (count($new_values)) { + $items = $relation->createMany($new_values); + $loaded_relations->push(...$items); + $updated_items->push(...$items); } - } - } - if (count($new_values)) { - $items = $relation->createMany($new_values); - $loaded_relations->push(...$items); - $updated_items->push(...$items); - } + if ($mode === 'sync') { + $updated_items_keyed = collect($updated_items)->keyBy($relation->getLocalKeyName()); - if ($mode === 'sync') { - $updated_items_keyed = collect($updated_items)->keyBy($relation->getLocalKeyName()); + $relations_to_delete = $loaded_relations->filter(function ($item) use ($relation, $updated_items_keyed) { + return !$updated_items_keyed->has($item->getAttribute($relation->getLocalKeyName())); + }); + $relations_to_delete->each->delete(); - $relations_to_delete = $loaded_relations->filter(function ($item) use ($relation, $updated_items_keyed) { - return !$updated_items_keyed->has($item->getAttribute($relation->getLocalKeyName())); - }); - $relations_to_delete->each->delete(); + $loaded_relations = $updated_items; + } - $loaded_relations = $updated_items; - } + $this->setRelation($relation_name, $loaded_relations); + } - $this->setRelation($relation_name, $loaded_relations); - continue; + break; } } } else {