Last active
March 7, 2023 23:51
-
-
Save sebastiaanluca/79a59adf4eb08de7974a4874e365462c to your computer and use it in GitHub Desktop.
A HasMany relation sync method implementation using macros
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$business->locations()->sync([1, 42, 16, 8]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Providers; | |
use Illuminate\Database\Eloquent\Collection; | |
use Illuminate\Database\Eloquent\Model; | |
use Illuminate\Database\Eloquent\Relations\HasMany; | |
use Illuminate\Database\Query\Builder; | |
use Illuminate\Support\Collection as BaseCollection; | |
use Illuminate\Support\ServiceProvider; | |
class EloquentServiceProvider extends ServiceProvider | |
{ | |
public function boot() : void | |
{ | |
/** | |
* Detach any models not in the given array and attach any new models. | |
* | |
* @param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model|array $ids | |
* @param bool $detaching | |
* | |
* @return array | |
*/ | |
HasMany::macro('sync', function ($ids, bool $detaching = true) : array { | |
$changes = [ | |
'attached' => [], 'detached' => [], 'updated' => [], | |
]; | |
$current = $this->getResults()->modelKeys(); | |
$records = $this->parseIds($ids); | |
$detach = array_diff($current, $records); | |
if ($detaching && count($detach) > 0) { | |
$this->detach($detach); | |
$changes['detached'] = $detach; | |
} | |
$changes = array_merge( | |
$changes, | |
$this->syncNew($records, $current) | |
); | |
return $changes; | |
}); | |
/**0 | |
* Get all of the IDs from the given mixed value. | |
* | |
* @param mixed $value | |
* | |
* @return array | |
*/ | |
HasMany::macro('parseIds', function ($value) { | |
if ($value instanceof Model) { | |
return [$value->{$this->relatedKey}]; | |
} | |
if ($value instanceof Collection) { | |
return $value->pluck($this->relatedKey)->all(); | |
} | |
if ($value instanceof BaseCollection) { | |
return $value->toArray(); | |
} | |
return (array) $value; | |
}); | |
HasMany::macro('detach', function (array $detach) : void { | |
$related = $this->getRelated(); | |
$models = $related::withoutGlobalScopes()->findMany($detach); | |
$models->each(function (Model $model) : void { | |
$model->{$this->getForeignKeyName()} = null; | |
$model->save(); | |
}); | |
}); | |
/** | |
* Attach all of the records that aren't in the given current records. | |
* | |
* @param array $records | |
* @param array $current | |
* @param bool $touch | |
* | |
* @return array | |
*/ | |
HasMany::macro('syncNew', function (array $records, array $current) : array { | |
$changes = ['attached' => [], 'updated' => []]; | |
$related = $this->getRelated(); | |
$models = $related::withoutGlobalScopes()->findMany($records); | |
$this->saveMany($models); | |
foreach ($models as $model) { | |
if (! in_array($model->getKey(), $current)) { | |
$changes['attached'][] = $model->getKey(); | |
} else { | |
$changes['updated'][] = $model->getKey(); | |
} | |
} | |
return $changes; | |
}); | |
} | |
} |
Could have changed or could've been that way forever, no idea 😄 TBH this is old, hacky code from 2 years ago, so wouldn't use it anymore.
But it looks really neat, i can change it in order to work. I think it's a good point to start
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I may be wrong but it seems that HasMany does not have a relatedKey property ?