さきがけデジタルの越高です。今年もよろしくお願いします。
「そろそろ自社サイト立ち上げ時のAstroの話書きたいな~」と思っておりました。
いろいろぶつかった壁はあるのですが、その中でも苦戦した「特定のページをビルドから除外したかった」話を書きたいと思います。
前置き:サイト制作とAstro学習について
前置きとして、自社サイトは microCMS と Astro を使って構築しています。
また、VSCode を使って作業しています。
詳しいサイトの作り方は、microCMS さんの記事で紹介していますので、こちらが参考になります。
サイト制作にあたっては、Udemyの たにぐちまこと さんの「ちゃんと学ぶ、Astro」を購読し、挑みました。
Udemyで習ったこととをトレースしながら、なんとか形にしていきました。
本題:サイトの仕組みと「やりたかったこと」
自社サイトを構築する際、microCMS上で(いろいろあって)「ブログ」と「お知らせ」を、1つのコンテンツとして登録することになりました。
これらは、まとめて「投稿記事」というコンテンツ(API)に入っています。
APIを呼び出した時に、「ブログ」という値を持っている場合はビルド先を「blog」以下に、「お知らせ」という値を持っている場合は、ビルド先を「news」以下にするように設定しました。
これは学習通り、フォルダ構成を下記のようにすれば達成できます。
pages
|-blog
|--[blogId].astro
|--index.astro
|-news
|--[newsId].astro
|--index.astro
ただし、ストレートに呼び出すと、ここで問題が発生。
例えば、投稿記事内に、ブログ記事A、ブログ記事B、お知らせ記事Cと、
3つ登録した場合にビルドするとどうなったか。
【生成されたページ(イメージ)】
・/blog/ブログ記事A
・/blog/ブログ記事B
・/blog/お知らせ記事C
・/news/ブログ記事A
・/news/ブログ記事B
・/news/お知らせ記事C
このように「ブログ」「お知らせ記事」の両方でページが生成される問題が発生。
URLは違うけど、同じ内容のページが2つあることになります。
「ブログ」と指定された場合は、その記事だけがblog以下に生成されるようにしたいのです。
【実現したい形(イメージ)】
・/blog/ブログ記事A
・/blog/ブログ記事B
・/news/お知らせ記事C
ちなみに、普通にAPIもう1個足せば回避できるのでは…と思いつつ。
ちょっと違った方向でアプローチしていきます。
調査と実際にやったこと
まずは、Astro初心者なので、キーワードがわからない。
調べ方からわからないので、チャットGPTに聞いてみました。3つの回答が返ってきました。
【チャットGPT】
1.
getStaticPaths
でフィルタリングする
2.astro:build:done
フックを使用する
3. 動的ルーティングで除外する
まとめ:このように、getStaticPaths
で事前にフィルタリングする方法が一番簡単で、一般的です。
初心者ゆえ、2つ目がよくわからないので、1つ目と3つ目あたりで考えていきます。
【チャットGPT】
動的ルーティングで特定のページを除外するには、
[slug].astro
のように動的ルートが設定されている場合に、URLパラメータ(例えばslug
)に基づいてページを条件分岐で制御します。特定の条件に当てはまる場合、そのページを404エラーやリダイレクトにすることで事実上除外できます。
// 例: src/pages/blog/[slug].astro
export async function getStaticPaths() {
const posts = await getAllPosts();
// 除外するページをフィルタリング
const filteredPosts = posts.filter(post => post.slug !== "exclude-this-post");
return filteredPosts.map(post => ({
params: { slug: post.slug },
props: { post }
}));
}
やれそうだけど、実際に書いているコードと差異があるため、このままコピペして力業実装では無理そう。しかも、何を書いているのか少し分からないところも…。まずは、各値を紐解いていく方が良さそうです。
このあたりで「なるほど」と思った方は、一番下の結論までスクロールしてください…
初心者なので、考えたことと、紐解いていく過程も書いていきます。
値を紐解く
★ここに書くのは、blogの場合です。newsでも同様に行っています。
console.logを使うと、ローカル環境でも VSCode上のターミナルに値が表示されます。
//見たかった場所
export async function getStaticPaths() {
const response = await getBlogs({ fields:["id"]})
console.log(response); //★まずはここを開けてみる☆
}
【取得結果】
15:15:03 [watch] src/pages/blog/[blogId].astro
{
contents: [
{ id: 'furusatodenpo-oiwai-2024' },
{ id: 'work-with-notionapi-and-powershell' },
{ id: 'website-renewal-2024' }
],
totalCount: 3,
offset: 0,
limit: 10
}
…なるほど、「{ fields:["id"]}」を引っ張ると(当然ながら)こうなるということか。
このブログの場合は「category」として、「ブログ」と「お知らせ」をコンボボックスで1つだけ選択するようにしているので、これを引っ張ってくればよいと思われます。
export async function getStaticPaths() {
const response = await getBlogs({ fields:["id","category"]}) //★ここが引っ張ってくる値
const filteredPosts = response.contents.filter(post => post.category[0] === 'ブログ');
}
「category」の値は、配列として持ってくるので「post.category[0]」と指定。
これが「'ブログ'」であれば、ビルドするようにすればいいんじゃないかなあ…と。
結論
//pages/blog/[blogId].astro
export async function getStaticPaths() {
const response = await getBlogs({ fields:["id","category"]})
const filteredPosts = response.contents.filter(post => post.category[0] === 'ブログ');
return filteredPosts.map((content)=>({
params: {
blogId: content.id
}
}));
}
const filteredPosts = response.contents.filter(post => post.category[0] === 'ブログ');
この辺でフィルタかけて、その結果だけをブログのidとしよう、と考えたわけです。
これでうまくいきました~~。
以上です。おわり~!
Astroネタはあるのですが、
正直なところ「これってホントに正しかった…?」と、ちょっとだけ不安になることもあります。
これからも動くものを作っていきたいなあと思っています。