diff --git a/locale/en_US/messages.php b/locale/en_US/messages.php index 285352c..6f34695 100644 --- a/locale/en_US/messages.php +++ b/locale/en_US/messages.php @@ -24,12 +24,15 @@ return [ 'EDIT' => 'Edit organistion', 'UPDATE' => 'Details updated for organistion {{name}}', - + 'DELETE' => 'Delete organisation', 'DELETE_CONFIRM' => 'Are you sure you want to delete the organisation {{name}}?', 'DELETE_YES' => 'Yes, delete organisation', 'DELETION_SUCCESSFUL' => 'Successfully deleted organisation {{name}}', + 'TOTAL_MEMBER_COUNT' => '# Members (inc admins)', + 'ADMIN_COUNT' => '# Admins', + 'NAME' => [ 1 => 'Organisation name', diff --git a/src/Database/Migrations/v001/OrganisationMembersTable.php b/src/Database/Migrations/v001/OrganisationMembersTable.php new file mode 100644 index 0000000..460cbd9 --- /dev/null +++ b/src/Database/Migrations/v001/OrganisationMembersTable.php @@ -0,0 +1,58 @@ +schema->hasTable('organisation_members')) { + $this->schema->create('organisation_members', function (Blueprint $table) { + $table->integer('user_id')->unsigned(); + $table->integer('organisation_id')->unsigned(); + $table->boolean('flag_admin'); + $table->timestamps(); + + $table->engine = 'InnoDB'; + $table->collation = 'utf8_unicode_ci'; + $table->charset = 'utf8'; + $table->foreign('user_id')->references('id')->on('users'); + $table->foreign('organisation_id')->references('id')->on('organisations'); + $table->index('user_id'); + $table->index('organisation_id'); + }); + } + } + + /** + * {@inheritdoc} + */ + public function down() + { + $this->schema->drop('organisation_members'); + } +} diff --git a/src/Database/Models/Organisation.php b/src/Database/Models/Organisation.php index e55ce00..3ed5f41 100644 --- a/src/Database/Models/Organisation.php +++ b/src/Database/Models/Organisation.php @@ -9,6 +9,7 @@ namespace UserFrosting\Sprinkle\Organisations\Database\Models; +use Illuminate\Database\Capsule\Manager as DB; use UserFrosting\Sprinkle\Core\Database\Models\Model; /** @@ -53,8 +54,123 @@ class Organisation extends Model 'deleted_at', ]; + /** + * The accessors to append to the model's array form. + * + * @var array + */ + protected $appends = [ + 'member_count', + 'admin_count', + 'non_admin_count' + ]; + + /** + * The attributes that should be hidden from toArray/toJson. + * + * @var array + */ + protected $hidden = [ + 'organisation_id' + ]; + /** * @var bool Enable timestamps for this class. */ public $timestamps = true; + + + /** + * Get a count of members within this organisation (includes admins). + */ + public function getMemberCountAttribute() + { + return $this->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 getNonAdminCountAttribute() + { + return $this->members(true)->count(); + } + + /** + * Get a list of members within this organisation. + */ + public function members($exclude_admins = false) + { + /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ + $classMapper = static::$ci->classMapper; + + $qry = $this + ->belongsToMany($classMapper->getClassMapping('user'), 'organisation_members', 'organisation_id', 'user_id') + ->withTimestamps(); + + if ($exclude_admins) { + return $qry->where('flag_admin', false); + } else { + return $qry; + } + } + + /** + * 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(); + } + + /** + * 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) + { + $memberCounts = DB::table('organisation_members') + ->selectRaw('organisation_id, count(*) as member_count') + ->groupBy('organisation_id'); + + $adminCounts = DB::table('organisation_members') + ->selectRaw('organisation_id, count(*) as admin_count') + ->where('flag_admin', true) + ->groupBy('organisation_id'); + + $nonAdminCounts = DB::table('organisation_members') + ->selectRaw('organisation_id, count(*) as non_admin_count') + ->where('flag_admin', false) + ->groupBy('organisation_id'); + + 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'); + }) + ->leftJoinSub($nonAdminCounts, 'non_admin_counts', function ($join) { + $join->on('non_admin_counts.organisation_id', '=', 'organisations.id'); + }); + } } diff --git a/src/Sprunje/OrganisationSprunje.php b/src/Sprunje/OrganisationSprunje.php index ca17f45..c9093a5 100644 --- a/src/Sprunje/OrganisationSprunje.php +++ b/src/Sprunje/OrganisationSprunje.php @@ -25,11 +25,15 @@ class OrganisationSprunje extends Sprunje protected $sortable = [ 'name', 'description', + 'member_count', + 'admin_count' ]; protected $filterable = [ 'name', 'description', + 'member_count', + 'admin_count' ]; /** @@ -37,6 +41,6 @@ class OrganisationSprunje extends Sprunje */ protected function baseQuery() { - return $this->classMapper->createInstance('organisation')->newQuery(); + return $this->classMapper->createInstance('organisation')->newQuery()->joinMemberCounts(); } } diff --git a/templates/tables/organisations.html.twig b/templates/tables/organisations.html.twig index eb600c6..122ecea 100644 --- a/templates/tables/organisations.html.twig +++ b/templates/tables/organisations.html.twig @@ -13,6 +13,8 @@