WordPress 開発者向け EmDash

このページ

EmDash は、投稿・固定ページ・タクソノミー・メニュー・ウィジェット・メディアライブラリといったおなじみの WordPress の概念を、モダンな Astro スタックに持ち込みます。コンテンツ管理の知識はそのまま活かせます。

変わらないところ

WordPress で知っている概念は、EmDash では第一級の機能です。

  • Collections は Custom Post Types のように動作します—コンテンツ構造を定義し、テンプレートでクエリします
  • Taxonomies も同様—階層型(カテゴリのように)とフラット型(タグのように)
  • Menus はドラッグ&ドロップの並び替えとネストした項目
  • Widget Areas はサイドバーや動的コンテンツ領域向け
  • Media library はアップロード・整理・画像管理
  • コードに触れずに使える Admin UI

違うところ

実装は変わりますが、頭の中のモデルは同じです。

PHP の代わりに TypeScript

テンプレートは Astro コンポーネントです。構文はすっきりしていますが、考え方は同じ—HTML を出力するサーバー側コードです。

WP_Query の代わりに Content API

getEmDashCollection() のようなクエリ関数が WP_Query の代わりになります。SQL はなく、関数呼び出しだけです。

ファイルベースのルーティング

src/pages/ のファイルが URL になります。リライトルールやテンプレート階層を暗記する必要はありません。

テンプレートパーツの代わりにコンポーネント

コンポーネントを import して使います。get_template_part() と同じ発想で、整理しやすくなります。

早見表

WordPressEmDashメモ
Custom Post TypesCollections管理 UI または API で定義
WP_QuerygetEmDashCollection()フィルタ・件数・タクソノミークエリ
get_post()getEmDashEntry()エントリまたは null を返す
Categories/TagsTaxonomies階層サポートはそのまま
register_nav_menus()getMenu()第一級のメニュー対応
register_sidebar()getWidgetArea()第一級のウィジェットエリア
bloginfo('name')getSiteSetting("title")サイト設定 API
the_content()<PortableText />構造化コンテンツのレンダリング
ShortcodesPortable Text blocksカスタムコンポーネント
add_action/filter()Plugin hookscontent:beforeSave など
wp_optionsctx.kvキー・バリュー保存
Theme directorysrc/ directoryコンポーネント、レイアウト、ページ
functions.phpastro.config.mjs + EmDash configビルドとランタイム設定

Content API

コレクションのクエリ

WordPress は WP_Query やヘルパーでクエリします。EmDash は型付きクエリ関数を使います。

WordPress

<?php
$posts = new WP_Query([
  'post_type' => 'post',
  'posts_per_page' => 10,
  'post_status' => 'publish',
  'category_name' => 'news',
]);

while ($posts->have_posts()) :
$posts->the_post();
?>

  <h2><?php the_title(); ?></h2>
  <?php the_excerpt(); ?>
<?php endwhile; ?>

EmDash

---
import { getEmDashCollection } from "emdash";

const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
limit: 10,
where: { category: "news" },
});

---

{posts.map((post) => (

  <article>
    <h2>{post.data.title}</h2>
    <p>{post.data.excerpt}</p>
  </article>
))}

単一エントリの取得

WordPress

<?php
$post = get_post($id);
?>
<article>
  <h1><?php echo $post->post_title; ?></h1>
  <?php echo apply_filters('the_content', $post->post_content); ?>
</article>

EmDash

---
import { getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";

const { slug } = Astro.params;
const { entry: post } = await getEmDashEntry("posts", slug);

## if (!post) return Astro.redirect("/404");

<article>
  <h1>{post.data.title}</h1>
  <PortableText value={post.data.content} />
</article>

テンプレート階層

WordPress はテンプレート階層でどのファイルがページを描画するか選びます。Astro は明示的なファイルベースのルーティングを使います。

WordPress TemplateEmDash での相当
index.phpsrc/pages/index.astro
single.phpsrc/pages/posts/[slug].astro
single-{type}.phpsrc/pages/{type}/[slug].astro
page.phpsrc/pages/pages/[slug].astro
archive.phpsrc/pages/posts/index.astro
archive-{type}.phpsrc/pages/{type}/index.astro
category.phpsrc/pages/categories/[slug].astro
tag.phpsrc/pages/tags/[slug].astro
search.phpsrc/pages/search.astro
404.phpsrc/pages/404.astro
header.php / footer.phpsrc/layouts/Base.astro
sidebar.phpsrc/components/Sidebar.astro

テンプレートパーツ → コンポーネント

WordPress のテンプレートパーツは Astro コンポーネントになります。

WordPress

// In template:
get_template_part('template-parts/content', 'post');

// template-parts/content-post.php:

<article class="post">
  <h2><?php the_title(); ?></h2>
  <?php the_excerpt(); ?>
</article>

EmDash

---
const { post } = Astro.props;
---

<article class="post">
	<h2>{post.data.title}</h2>
	<p>{post.data.excerpt}</p>
</article>
---
import PostCard from "../components/PostCard.astro";
import { getEmDashCollection } from "emdash";

const { entries: posts } = await getEmDashCollection("posts");
---

{posts.map((post) => <PostCard {post} />)}

メニュー

EmDash は URL 解決が自動の、第一級のメニュー対応があります。

WordPress

<?php
wp_nav_menu([
  'theme_location' => 'primary',
  'container' => 'nav',
]);
?>

EmDash

---
import { getMenu } from "emdash";

## const menu = await getMenu("primary");

<nav>
  <ul>
    {menu?.items.map((item) => (
      <li>
        <a href={item.url}>{item.label}</a>
      </li>
    ))}
  </ul>
</nav>

メニューは管理 UI、シードファイル、または WordPress インポートで作成します。

ウィジェットエリア

ウィジェットエリアは WordPress のサイドバーのように動きます。

WordPress

<?php if (is_active_sidebar('sidebar-1')) : ?>
  <aside>
    <?php dynamic_sidebar('sidebar-1'); ?>
  </aside>
<?php endif; ?>

EmDash

---
import { getWidgetArea } from "emdash";
import { PortableText } from "emdash/ui";

## const sidebar = await getWidgetArea("sidebar");

{sidebar && (

  <aside>
    {sidebar.widgets.map((widget) => {
      if (widget.type === "content") {
        return <PortableText value={widget.content} />;
      }
      // Handle other widget types
    })}
  </aside>
)}

サイト設定

サイトオプションやカスタマイザー設定は getSiteSetting() に対応します。

WordPressEmDash
bloginfo('name')getSiteSetting("title")
bloginfo('description')getSiteSetting("tagline")
get_custom_logo()getSiteSetting("logo")
get_option('date_format')getSiteSetting("dateFormat")
home_url()Astro.site
import { getSiteSetting } from "emdash";

const title = await getSiteSetting("title");
const logo = await getSiteSetting("logo"); // Returns { mediaId, alt, url }

タクソノミー

概念は同じ—階層型(カテゴリのように)かフラット型(タグのように)です。

import { getTaxonomyTerms, getEntryTerms, getTerm } from "emdash";

// Get all categories
const categories = await getTaxonomyTerms("categories");

// Get a specific term
const news = await getTerm("categories", "news");

// Get terms for a post
const postCategories = await getEntryTerms("posts", postId, "categories");

Hooks → プラグインシステム

WordPress のフック(add_actionadd_filter)は EmDash のプラグインフックになります。

WordPress HookEmDash Hook目的
save_postcontent:beforeSave保存前にコンテンツを変更
the_contentPortableText componentsレンダリング結果を変換
pre_get_postsQuery optionsクエリをフィルタ
wp_headLayout <head>head にコンテンツを追加
wp_footerLayout before </body>フッターにコンテンツを追加

EmDash が優れている点

型安全

全体で TypeScript。コレクション・クエリ・コンポーネントはすべて型付き。フィールド名や戻り値を推測する必要はありません。

パフォーマンス

PHP のオーバーヘッドなし。デフォルトは静的生成。必要ならサーバーレンダリング。エッジデプロイにも向きます。

モダンな DX

Hot module replacement。コンポーネント指向。Vite、TypeScript、ESLint など現代的なツールチェーン。

Git ベースのデプロイ

コードとテンプレートは git。コンテンツは DB。FTP やファイル権限、乗っ取られたサイトの心配がありません。

プレビューリンク

EmDash は HMAC 署名トークン付きの安全なプレビュー URL を生成します。編集者は本番にログインせず下書きを確認できます—リンクを共有するだけで、認証情報は不要です。

プラグイン競合がない

WordPress のようなプラグイン競合は起きにくくなります。EmDash のプラグインは明示的な API を持つ隔離されたコンテキストで動きます。グローバル状態の汚染がありません。

コンテンツ編集者の体験

編集者は wp-admin に近い EmDash 管理パネルを使います。

  • 最近の活動がある Dashboard
  • 検索・フィルタ・一括操作がある コレクション一覧
  • コンテンツ用の リッチエディタ(Portable Text。Gutenberg ではありません)
  • ドラッグ&ドロップアップロードの メディアライブラリ
  • ドラッグ&ドロップ並び替えの メニュービルダー
  • サイドバーコンテンツ用の ウィジェットエリアエディタ

編集体験は馴染みやすく、その下の技術はモダンです。

移行の流れ

EmDash は WordPress コンテンツを直接インポートします。

  1. WordPress からエクスポート(ツール → エクスポート)
  2. .xml を EmDash 管理画面にアップロード
  3. 投稿タイプをコレクションにマッピング
  4. コンテンツとメディアをインポート

投稿・固定ページ・タクソノミー・メニュー・メディアが移ります。Gutenberg ブロックは Portable Text に変換されます。カスタムフィールドは解析してマッピングされます。

手順の詳細は WordPress 移行ガイド を参照してください。

次のステップ