members()->count(); } /** * Get a count of administrators within this organisation. */ public function getAdminCountAttribute() { return $this->administrators()->count(); } /** * Get a count of members within this organisation (excludes admins). */ public function getTotalMemberCountAttribute() { return $this->members()->count() + $this->administrators()->count(); } /** * Get a list of members within this organisation. */ public function members() { /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ $classMapper = static::$ci->classMapper; return $this ->belongsToMany($classMapper->getClassMapping('user'), 'organisation_members', 'organisation_id', 'user_id') ->where('flag_admin', false) ->withTimestamps(); } /** * Get a list of administrators within this organisation. */ public function administrators() { /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ $classMapper = static::$ci->classMapper; return $this ->belongsToMany($classMapper->getClassMapping('user'), 'organisation_members', 'organisation_id', 'user_id') ->where('flag_admin', true) ->withTimestamps(); } /** * Delete this organisation from the database, along with any linked objects. * * @param bool $hardDelete Set to true to completely remove the organisation and all associated objects. * * @return bool true if the deletion was successful, false otherwise. */ public function delete($hardDelete = false) { /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ $classMapper = static::$ci->classMapper; if ($hardDelete) { static::$ci->get('organisation.beforeDelete')($this); // Remove all member associations $this->members()->detach(); // Delete the organisation $result = $this->forceDelete(); } else { // Soft delete the organisation, leaving all associated records alone $result = parent::delete(); } return $result; } /** * Performs tasks to be done before an organisation is merged * * By default, adds a new sign-in activity and updates any legacy hash. * * @param mixed[] $params Optional array of parameters used for this event handler. * * @todo Transition to Laravel Event dispatcher to handle this */ public function beforeMerge($target, $params = []) { /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ $classMapper = static::$ci->classMapper; /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */ $currentUser = static::$ci->authenticator->user(); /** @var \UserFrosting\Sprinkle\???? $activityLogger */ $activityLogger = static::$ci->userActivityLogger; $logEntry = function($activityLogger, $currentUser, $user, $target) { $activityLogger->info("User {$currentUser->user_name} moved user {$user->user_name} from organisation {$this->name} into {$target->name}.", [ 'type' => 'organisation_merge', 'user_id' => $currentUser->id, ]); $activityLogger->info("User {$user->user_name} was removed from the organisation {$this->name} by {$currentUser->user_name} during an organisation merge.", [ 'type' => 'group_leave', 'user_id' => $user->id, ]); $activityLogger->info("User {$user->user_name} was added to the organisation {$target->name} by {$currentUser->user_name} during an organisation merge.", [ 'type' => 'group_join', 'user_id' => $user->id, ]); }; // Move all the users from this organisation to the target $this->members()->each(function ($user) use ($target, $activityLogger, $currentUser, $logEntry) { $this->members()->detach($user); // NOTE: Should this record be retained? Or is the activity log enough of an audit? $target->members()->attach($user, ['flag_admin' => false]); $logEntry($activityLogger, $currentUser, $user, $target); }); $this->administrators()->each(function ($user) use ($target, $activityLogger, $currentUser, $logEntry) { $this->administrators()->detach($user); // NOTE: Should this record be retained? Or is the activity log enough of an audit? $target->administrators()->attach($user, ['flag_admin' => true]); $logEntry($activityLogger, $currentUser, $user, $target); }); $this->save(); $target->save(); return $this; } /** * Joins the organisation's member count, so we can do things like sort, search, paginate, etc. * * @param Builder $query * * @return Builder */ public function scopeJoinMemberCounts($query) { $memberCountsInner = DB::table('organisation_members') ->selectRaw('organisation_id, COUNT(*) AS member_count') ->where('flag_admin', false) ->groupBy('organisation_id'); $memberCounts = DB::table('organisations') ->leftJoinSub($memberCountsInner, 'member_counts_inner', function ($join) { $join->on('member_counts_inner.organisation_id', '=', 'organisations.id'); }) ->select('id AS organisation_id') ->selectRaw('COALESCE(member_count, 0) AS member_count'); $adminCountsInner = DB::table('organisation_members') ->selectRaw('organisation_id, COUNT(*) AS admin_count') ->where('flag_admin', true) ->groupBy('organisation_id'); $adminCounts = DB::table('organisations') ->leftJoinSub($adminCountsInner, 'admin_counts_inner', function ($join) { $join->on('admin_counts_inner.organisation_id', '=', 'organisations.id'); }) ->select('id AS organisation_id') ->selectRaw('COALESCE(admin_count, 0) AS admin_count'); return $query ->leftJoinSub($memberCounts, 'member_counts', function ($join) { $join->on('member_counts.organisation_id', '=', 'organisations.id'); }) ->leftJoinSub($adminCounts, 'admin_counts', function ($join) { $join->on('admin_counts.organisation_id', '=', 'organisations.id'); }); } }