$attributes * @param iterable $companies */ public function create(array $attributes, iterable $companies): User { $member = User::create($attributes); $member->setSettings(['language' => 'default']); $memberships = collect($companies); $member->companies()->sync($memberships->pluck('id')); $this->grantRoles($member, $memberships); return $member; } /** * Overwrite an account and re-point it at the listed companies. * * Memberships are replaced wholesale, so an edit that omits a company both * detaches the account from it and leaves the roles it held there behind — * the role sync below only visits companies still on the list. * * @param array $attributes * @param iterable $companies */ public function update(User $user, array $attributes, iterable $companies): User { $user->update($attributes); $memberships = collect($companies); $user->companies()->sync($memberships->pluck('id')); $this->grantRoles($user, $memberships); return $user; } /** * Erase the named accounts, one after another. * * An id naming nobody is skipped rather than reported. Everything the * account authored outlives it: invoices, estimates, contacts, recurring * invoices, expenses, payments and catalog entries are left standing with * no author against them, and only the preferences rows and the account * itself actually go. * * @param array $ids */ public function delete(array $ids): bool { foreach ($ids as $id) { $member = User::find($id); if ($member === null) { continue; } $this->memberReferencesCleaner->clear($member); if ($member->settings()->exists()) { $member->settings()->delete(); } $member->delete(); } return true; } /** * Give the account exactly the one role each company named, discarding any * role it already held in that company. * * @param Collection $memberships */ private function grantRoles(User $member, Collection $memberships): void { foreach ($memberships as $membership) { BouncerFacade::scope()->to($membership['id']); BouncerFacade::sync($member)->roles([$membership['role']]); } } }