Skip to content
JG /

Today I Learned

Small snippets of things I have learned on my coding adventures

28 May 2026

·
phplaravel

whereHas() for relationship existence

If you want to check that at least one related record exists with certain criteria, use whereHas(). I needed to find all players who had at least one attended tournament this season. Without it, you'd fetch all players and filter the collection in PHP. whereHas() keeps the constraint at the database level so only the records you actually want are returned.

PHP
$players = Player::whereHas('playerScores', function ($query) use ($season) {
    $query->where('attended', true);
})->get();

28 May 2026

·
css

Table borders and sticky cells

Table borders use border-collapse by default, meaning adjacent cells share borders and the browser decides which cell "owns" them — not always the one you styled. A border-l on a sticky cell can get assigned to the neighbouring scrolling cell and scroll away with it. box-shadow is painted directly onto the element that declares it and is never shared with neighbours, so it always stays put.

CSS
/* border-l won't stick — browser may assign it to the neighbour */
.sticky-cell {
  position: sticky;
  left: 0;
  border-left: 1px solid;
}
 
/* inset box-shadow always stays with the element */
.sticky-cell {
  position: sticky;
  left: 0;
  box-shadow: inset 1px 0 0 0 currentColor;
}

28 May 2026

·
php

Passing by reference in PHP closures

By default, PHP passes variables into a closure via use() by value — any changes made inside the closure don't affect the original variable for the next iteration. If you need changes to persist across iterations, prefix the variable with & inside the use() call.

PHP
$rank = 1;
$previousScore = 0;
 
$players->map(function ($player) use (&$rank, &$previousScore) {
    // changes to $rank and $previousScore persist to the next iteration
});

27 May 2026

·
phplaravel

through() vs map() on Laravel Paginators

When transforming paginated results in Laravel, reach for through() instead of map().

Calling map() on a paginator converts it to a plain Collection, stripping all the pagination metadata — current page, total, next page URL, etc.

map() - loses pagination metadata
$players = Player::paginate()->map(function ($player) use ($currentSeason) {
    $player->current_season_total = $player->currentSeasonTotal($currentSeason);
    return $player;
});

through() does the same transformation but keeps the paginator intact, so anything that depends on that metadata — like Inertia's infinite scroll — keeps working.

through() - pagination intact
$players = Player::paginate()->through(function ($player) use ($currentSeason) {
    $player->current_season_total = $player->currentSeasonTotal($currentSeason);
    return $player;
});

15 May 2026

·
typescript

Using a status order map to sort projects

I wanted to order projects by status but they're stored in a flat array. This method assigns a numeric value to each status and sorts accordingly.

TypeScript
const allProjects: Project[] = getAllProjects();
 
const statusOrder: Record<string, number> = { building: 0, personal: 1, client: 2 };
 
const projects = allProjects
  .filter((project) => project.draft !== true)
  .sort((a, b) => statusOrder[a.status] - statusOrder[b.status]);

13 May 2026

·
phplaravelbash

Using route:list to check routing

Found this out the hard way, trying to debug why I wasn't hitting a route (it didn't exist). This command shows all registered routes in the application.

Adding | grep round to the end filters the list to routes that contain the word given. | is a pipe — a shell operator which takes the output of the command on the left and passes it as input to the command on the right.

Terminal
php artisan route:list | grep round

12 May 2026

·
phplaravel

Using load() within the show method

In the show method of my controller I was returning the full tournaments table as I used with() to get the teams and players.

PHP
$fullTournament = $tournament->with(['teams.playerTeams'])->get();

Using load() is the same but works on an already retrieved model instance rather than starting a fresh query (which is what with() does).

PHP
$fullTournament = $tournament->load(['teams.playerTeams']);