Separating Dashboards Dynamically
Hey Bobby,
You probably do not remember but I am working on a virtual/real-life gaming platform. At this time, I am trying to dynamically separate each clubs dashboard to prevent the need for like 64 blade files. Each club has a row in the clubs table.
I am also using Spatie Permissions package. I have a gate defined for the role 'team_owner' for the club dashboard routes as you will see. I never actually define the gate because you do not need too with spatie. I just put the middleware call within the route structure and it puts a gate on the route for me.
Upon user registration 3 actions take place, the creation of the user, player and club table (only if they selected team_owner as their role and this is enforced with an if statement in the controller). If they selected team_owner as their role the system should redirect the club owner to the routes below. They visit the first route to verify their club (livewire) and once verified (external request is made to an endpoint) they are redirected to the 2nd route as the application is written to use the clubId from the external request moving forward.
Route::middleware(['auth:web'])->group(function () {
Route::get('/clubs/{clubName}/dashboard', [TeamDashboardController::class, 'index'])
->middleware('can:manage team')
->name('team.owner.dashboard');
Route::get('/clubs/{club_id}/dashboard', [TeamDashboardController::class, 'indexByClubId'])
->middleware('can:manage team')
->middleware('can:manage entire league')
->name('team owner dashboard byClubId');
});
My issue is that I cannot isolate the routes to each users club. My controller code is below. This is the only part I am having issues with. Everything else works just fine.
public function index(User $user, Club $club, Player $player, $clubName)
{
\Log::info('Controller Club Name: ' . $clubName);
\Log::info('Verify Team button clicked.');
$user = Auth::user();
$players = Auth::user()->player->club()->with('players.user')->get();
$clubs = Club::with('players.user')
->with('players.user')
->firstOrFail()
->get();
$topGoalScorers = Player::orderBy('skgoals', 'desc')->take(3)->get();
$topAssists = Player::orderBy('skassists', 'desc')->take(3)->get();
$topPoints = Player::selectRaw('*, (skgoals + skassists) as total_points')
->orderBy('total_points', 'desc')
->take(3)
->get();
return view('teams.dashboard', compact('club', 'clubs', 'players', 'user', 'topGoalScorers', 'topAssists', 'topPoints'));
}
---
public function indexByClubId($club_id)
{
$user = Auth::user();
$club = Club::findOrFail($club_id);
$players = Player::where('club_id', $club->id)->orWhereNull('club_id')->get();
$clubs = Club::with('players.user')->get();
$topGoalScorers = Player::orderBy('skgoals', 'desc')->take(3)->get();
$topAssists = Player::orderBy('skassists', 'desc')->take(3)->get();
$topPoints = Player::selectRaw('*, (skgoals + skassists) as total_points')
->orderBy('total_points', 'desc')
->take(3)
->get();
return view('teams.dashboard', compact('club', 'clubs', 'players', 'topGoalScorers', 'topAssists', 'topPoints'));
}
Hey there,
I remember you mentioning this project before! Based on what you've shared, it looks like you're having trouble isolating the routes to each user's club.
Both routes use the same URL pattern ('/clubs/{parameter}/dashboard
'), which can lead to conflicts.
To avoid conflicts, differentiate the routes clearly. For example, you can use different patterns or methods to ensure they are unique.
Route::middleware(['auth:web'])->group(function () {
// Route for accessing dashboard by club name
Route::get('/clubs/name/{clubName}/dashboard', [TeamDashboardController::class, 'index'])
->middleware('can:manage team')
->name('team.owner.dashboard');
// Route for accessing dashboard by club ID
Route::get('/clubs/id/{club_id}/dashboard', [TeamDashboardController::class, 'indexByClubId'])
->middleware('can:manage team')
->name('team.owner.dashboard.byClubId');
});
Controller Methods
Make sure the controller methods properly handle and verify the club and user details.
index
Method
public function index($clubName)
{
\Log::info('Controller Club Name: ' . $clubName);
\Log::info('Verify Team button clicked.');
$user = Auth::user();
$club = Club::where('name', $clubName)->firstOrFail();
// Ensure user is part of the club
if (!$user->clubs->contains($club)) {
abort(403, 'Unauthorized action.');
}
$players = $club->players()->with('user')->get();
$topGoalScorers = Player::orderBy('skgoals', 'desc')->take(3)->get();
$topAssists = Player::orderBy('skassists', 'desc')->take(3)->get();
$topPoints = Player::selectRaw('*, (skgoals + skassists) as total_points')
->orderBy('total_points', 'desc')
->take(3)
->get();
return view('teams.dashboard', compact('club', 'players', 'user', 'topGoalScorers', 'topAssists', 'topPoints'));
}
indexByClubId
Method
public function indexByClubId($club_id)
{
$user = Auth::user();
$club = Club::findOrFail($club_id);
// Ensure user is part of the club
if (!$user->clubs->contains($club)) {
abort(403, 'Unauthorized action.');
}
$players = Player::where('club_id', $club->id)->orWhereNull('club_id')->get();
$topGoalScorers = Player::orderBy('skgoals', 'desc')->take(3)->get();
$topAssists = Player::orderBy('skassists', 'desc')->take(3)->get();
$topPoints = Player::selectRaw('*, (skgoals + skassists) as total_points')
->orderBy('total_points', 'desc')
->take(3)
->get();
return view('teams.dashboard', compact('club', 'players', 'topGoalScorers', 'topAssists', 'topPoints'));
}
Explanation
-
Routes: By using
/clubs/name/{clubName}/dashboard
and/clubs/id/{club_id}/dashboard
, we avoid conflicts and make the routes more explicit. - Controller Methods: Both methods now ensure the user is part of the club before proceeding. This way only authorized users can access the dashboard for their respective clubs.
Let me know how it goes!
- Bobby
Bobby,
I got it!! This line of code along with a few other lines:
public function index(User $user, Club $club, Player $player, $clubName)
{
\Log::info('Controller Club Name: ' . $clubName);
\Log::info('Verify Team button clicked.');
$players = Auth::user()->player->club()->with('owner')->get();
$club = Club::where('name', $clubName)->first();
$clubs = Club::where('owner_id', Auth::user()->id)->get();
$topGoalScorers = Player::orderBy('skgoals', 'desc')->take(3)->get();
$topAssists = Player::orderBy('skassists', 'desc')->take(3)->get();
$topPoints = Player::selectRaw('*, (skgoals + skassists) as total_points')
->orderBy('total_points', 'desc')
->take(3)
->get();
return view('teams.dashboard', compact('clubs', 'players', 'topGoalScorers', 'topAssists', 'topPoints'));
}
public function indexByClubId($clubId, Club $club)
{
$club = Club::where('id', $clubId)->first();
$players = Player::where('owner_id')->orWhereNull('owner_id')->get();
$user = Auth::user();
$clubs = Club::where('owner_id', $user->id)->with('players.user')->get();
$topGoalScorers = Player::orderBy('skgoals', 'desc')->take(3)->get();
$topAssists = Player::orderBy('skassists', 'desc')->take(3)->get();
$topPoints = Player::selectRaw('*, (skgoals + skassists) as total_points')
->orderBy('total_points', 'desc')
->take(3)
->get();
return view('teams.dashboard', compact('club', 'clubs', 'players', 'topGoalScorers', 'topAssists', 'topPoints'));
}
This is what i needed to separate the dashboards to the current user/owner_id. Now, the user can change the URL all they want and it'll only show the team they are the owner of. I think changing the routes helped as well.
Thanks for the help, it got me the answer! Now, I need to clean up the unneccessary code, any advice? I am using $club and $clubs because of the variables i have defined in the blade files. changing it one or the other crashes the page and i haven't gotten around to making it cleaner. There are two methods for before and after team verification. Either way they needed isolated to their own dashboard if they selected the team_owner role during registration and it works well but I am sure it could better, do you have any advice there or do you need to see my codebase?