. * * @link https://www.librenms.org * * @copyright 2019 Tony Murray * @author Tony Murray */ namespace LibreNMS\DB; use App\Models\Device; use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Support\Collection; use LibreNMS\Interfaces\Models\Keyable; trait SyncsModels { /** * Sync several models for a device's relationship * Model must implement \LibreNMS\Interfaces\Models\Keyable interface * * @param \Illuminate\Database\Eloquent\Model $parentModel * @param string $relationship * @param \Illuminate\Support\Collection $models \LibreNMS\Interfaces\Models\Keyable * @return \Illuminate\Support\Collection */ protected function syncModels($parentModel, $relationship, $models, $existing = null): Collection { $models = $models->keyBy->getCompositeKey(); $existing = ($existing ?? $parentModel->$relationship)->groupBy->getCompositeKey(); foreach ($existing as $exist_key => $existing_rows) { if ($models->offsetExists($exist_key)) { // update foreach ($existing_rows as $index => $existing_row) { if ($index == 0) { $existing_row->fill($models->get($exist_key)->getAttributes())->save(); } else { // delete extra rows at this key $existing_row->delete(); $existing_rows->forget($index); } } } else { // delete $existing_rows->each->delete(); $existing->forget($exist_key); } } $new = $models->diffKeys($existing); if (is_a($parentModel->$relationship(), HasManyThrough::class)) { // if this is a distant relation, the models need the intermediate relationship set // just save assuming things are correct $new->each->save(); } else { $parentModel->$relationship()->saveMany($new); } return $existing->map->first()->merge($new); } /** * Sync a sub-group of models to the database * * @param Collection $models */ public function syncModelsByGroup(Device $device, string $relationship, Collection $models, array $where): Collection { $filter = function ($models, $params) { foreach ($params as $key => $value) { $models = $models->where($key, '=', $value); } return $models; }; return $this->syncModels($device, $relationship, $models->when($where, $filter), $device->$relationship->when($where, $filter)); } /** * Combine a list of existing and potentially new models * If the model exists fill any new data from the new models * * @param \Illuminate\Support\Collection $existing \LibreNMS\Interfaces\Models\Keyable * @param \Illuminate\Support\Collection $discovered \LibreNMS\Interfaces\Models\Keyable * @return \Illuminate\Support\Collection */ protected function fillNew(Collection $existing, Collection $discovered): Collection { $all = $existing->keyBy->getCompositeKey(); foreach ($discovered as $new) { if ($found = $all->get($new->getCompositeKey())) { $found->fill($new->getAttributes()); } else { $all->put($new->getCompositeKey(), $new); } } return $all; } }