morphToMany(NirObject::class, 'objectable')->withPivot(['ord', 'group'])->orderByPivot('ord')->withTimestamps(); } public function objectsByGroup($group = null): MorphToMany { return $this->objects()->wherePivot('group', '=', $group ?? 'default'); } public function getObjectAttribute() { return $this->objects()->first(); } public function getObject($typeName): ?Model { return ($type = ObjectType::byName($typeName)->first()) ? $this->objects()->firstOrCreate(['type_id' => $type->id]) : null; } public function createObject($typeName, $ord = null, $group = null): ?Model { if (($type = ObjectType::byName($typeName)->first()) && ($object = NirObject::create(['type_id' => $type->id]))) { $this->attachObject($object, $ord, $group); return $object; } return null; } public function attachObject(NirObject $object, $ord = null, $group = null) { $ord = ($ord === null) ? $this->getMaxOrd($group) : $ord; $this->moveObjectsSet('forward', $ord, null, $group); $this->objects()->attach($object->id, ['ord' => $ord ?? 0, 'group' => $group ?? 'default']); } public function getMaxOrd($group = null): int { $res = $this->objectsByGroup($group)->max('ord'); return ($res !== null) ? ($res + 1) : 0; } public function moveObject(NirObject $object, $ord, $group = null) { $currentGroup = $object->currentGroup($this); $group = $group ?? $currentGroup; $currentOrd = $object->currentOrd($this); if ($group === $currentGroup) { ($ord > $currentOrd) ? $this->moveObjectsSet('backward', $currentOrd, $ord, $group) : $this->moveObjectsSet('forward', $ord, $currentOrd, $group); } else $this->moveObjectsSet('forward', $ord, null, $group); $this->objects()->updateExistingPivot($object, ['ord' => $ord, 'group' => $group ?? 'default']); $this->trimIndexes([$group, $currentGroup]); } public function moveObjectsSet($dir = 'forward', $ordFrom = null, $ordTo = null, $group = null) { $query = $this->objectsByGroup($group); if ($ordFrom !== null) $query->wherePivot('ord', '>=', $ordFrom); if ($ordTo !== null) $query->wherePivot('ord', '<=', $ordTo); $query->get()->each(function($object) use($dir) { $this->objects()->updateExistingPivot($object, ['ord' => ($dir === 'forward') ? ($object->pivot->ord + 1) : ($object->pivot->ord - 1)]); }); } public function trimIndexes($groups) { collect(is_array($groups) ? $groups : [$groups])->unique()->each(function($group) { $this->objectsByGroup($group)->each(function($object, $index) { if ($object->pivot->ord !== $index) $this->objects()->updateExistingPivot($object, ['ord' => $index]); }); }); } public function getValue($fieldName) { return $this->object ? $this->object->getValue($fieldName) : null; } public function setValue($fieldName, $value) { return $this->object ? $this->object->setValue($fieldName, $value) : null; } public function setValues(array $values) { return $this->object ? $this->object->setValues($values) : null; } public function addValue($fieldName, $value) { return $this->object ? $this->object->addValue($fieldName, $value) : null; } }