Skip to content

Commit

Permalink
add saving MorphOne and MorphMany relations
Browse files Browse the repository at this point in the history
  • Loading branch information
kolirt committed Sep 11, 2024
1 parent 385366b commit 8b261a0
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 131 deletions.
27 changes: 13 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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'
]
]);
Expand All @@ -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'
],
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
232 changes: 116 additions & 116 deletions src/Traits/MasterModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 8b261a0

Please sign in to comment.