From b3e4cde12b977d407b625c1625bb8f6857c60098 Mon Sep 17 00:00:00 2001 From: Cory Dransfeldt Date: Fri, 24 May 2024 10:02:31 -0700 Subject: [PATCH] chore: post --- ...ng-pages-from-data-in-eleventy-preview.png | Bin 0 -> 28812 bytes .../building-pages-from-data-in-eleventy.md | 163 ++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 src/assets/img/ogi/building-pages-from-data-in-eleventy-preview.png create mode 100644 src/posts/2024/building-pages-from-data-in-eleventy.md diff --git a/src/assets/img/ogi/building-pages-from-data-in-eleventy-preview.png b/src/assets/img/ogi/building-pages-from-data-in-eleventy-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..13fd079035098fbe409b70bcb47d69695edc94c1 GIT binary patch literal 28812 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 + +The motivation for this has been to move further towards hosting data that I feel is core to my site on infrastructure I control. My book data is in a versioned JSON file, my music, movie and TV data reside in a database. When my site is built, each book, musician, movie and show has a page built for it. + +Aggregating this data was, to be fair, extremely tedious up front. Importing plays for music was fairly straightforward, while gathering artist bios and genres was much more time-consuming. + +With the data for each type of media in place, I fetched it from Supabase: + +```javascript +import { createClient } from '@supabase/supabase-js' + +const SUPABASE_URL = process.env.SUPABASE_URL || 'YOUR_SUPABASE_URL' +const SUPABASE_KEY = process.env.SUPABASE_KEY || 'YOUR_SUPABASE_KEY' +const supabase = createClient(SUPABASE_URL, SUPABASE_KEY) + +const regionNames = new Intl.DisplayNames(['en'], { type: 'region' }) +const getCountryName = (countryCode) => regionNames.of(countryCode.trim()) || countryCode.trim() + +const parseCountryField = (countryField) => { + if (!countryField) return null + + const delimiters = [',', '/', '&', 'and'] + let countries = [countryField] + + delimiters.forEach(delimiter => { + countries = countries.flatMap(country => country.split(delimiter)) + }) + + return countries.map(getCountryName).join(', ') +} + +const PAGE_SIZE = 50 + +const fetchPaginatedData = async (table, selectFields) => { + let data = [] + let page = 0 + let hasMoreRecords = true + + while (hasMoreRecords) { + const { data: pageData, error } = await supabase + .from(table) + .select(selectFields) + .order('id', { ascending: true }) + .range(page * PAGE_SIZE, (page + 1) * PAGE_SIZE - 1) + + if (error) { + console.error(`Error fetching ${table}:`, error) + break + } + + data = data.concat(pageData) + + if (pageData.length < PAGE_SIZE) { + hasMoreRecords = false + } else { + page++ + } + } + + return data +} + +export default async function () { + const artists = await fetchPaginatedData('artists', 'mbid, name_string, image, genre, total_plays, country, description, favorite') + const albums = await fetchPaginatedData('albums', 'mbid, name, release_year, artist_mbid, total_plays') + + const albumsByArtist = albums.reduce((acc, album) => { + if (!acc[album.artist_mbid]) acc[album.artist_mbid] = [] + acc[album.artist_mbid].push({ + id: album.id, + name: album.name, + release_year: album.release_year, + total_plays: album.total_plays > 0 ? album.total_plays : '-' + }) + return acc + }, {}) + + artists.forEach(artist => { + artist.albums = albumsByArtist[artist.mbid]?.sort((a, b) => a['release_year'] - b['release_year']) || [] + artist.country = parseCountryField(artist.country) + }) + + return artists +} +``` + +This pages through my artists table, fetches the metadata I've assembled it and returns it as an array of artists with a child array of albums. This can then be used to generate a static page for each artist: + +{% raw %} +```liquid +--- +layout: default +pagination: + data: artists + size: 1 + alias: artist +permalink: /music/artists/{{ artist.name_string | slugify | downcase }}-{{ artist.country | slugify | downcase}}/ +updated: "now" +schema: artist +--- +{%- capture alt -%} + {{ artist.name_string }} • {{ artist.country }} +{%- endcapture -%} +{% capture js %} + {% render "../../../../assets/scripts/text-toggle.js" %} +{% endcapture %} + + +{% tablericon "arrow-left" "Go back" %} Go back +
+
+ {{ alt }} +
+

{{ artist.name_string }}

+ {%- if artist.favorite -%} +

{% tablericon "heart" "Favorite" %} This is one of my favorite artists!

+ {%- endif -%} + {%- if artist.total_plays > 0 -%} +

{{ artist.total_plays }} plays

+ {%- endif -%} +

{{ artist.genre }}

+

+ {% tablericon "brain" "MusicBrainz" %} +

+
+
+ {%- if artist.description -%} +
{{ artist.description | markdown }}
+ + {%- endif -%} + + + + + + + {% for album in artist.albums %} + + + + + + {% endfor %} +
YearTitlePlays
{{ album.release_year }}{{ album.name }}{{ album.total_plays }}
+

These are the album by this artist that are in my collection, not necessarily a comprehensive discography.

+
+``` +{% endraw %} + +Each artist has an image, a name, a play count (hopefully), a genre, location and an indication that they're a favorite if I've manually toggled the appropriate `boolean` to true. The most important part of this template is the frontmatter: pagination is set to a size of one, the data for each page is aliased to `artist` and the `permalink` logic provides a unique URL for each page. + +Each link from a music page or item is pointed at the appropriate artist by generating the appropriate URL using identical logic. + +I've taken the same approach for all of my media, allowing me to syndicate links out that point back to content on my site, while still linking out from those pages as appropriate. \ No newline at end of file