ASP.NET MVC から wordpress のデータを扱う(4)

ASP.NET MVC では LINQ to Entities を使えるので、あらかじめリレーションをしておくと楽…なんですが、逆にリレーションが設定されていない場合は大変ってのがありますね。CakePHP の場合は Model::$belongsTo 等で後からリレーション/アソシエーションを設定するのですが、LINQ to Entities の場合はこれが大変、というか、これはどう設定すればいんでしょう???な状態なのですが、ええ、実は意外と簡単でした。

デザイナの背景をクリックして「追加」→「アソシエーション」ですね。

そして、アソシエーションのプロパティで「参照に関する制約」を忘れずに設定

という訳です。詳しくは後ほどブログに書きますが、ひとまず(個人的に)速報まで。

さて、お次はカテゴリ内の記事の一覧を作ります。関連するテーブルは以下の4つ。

  • terms: カテゴリ名などの名称
  • term_taxonomy: terms の分類(category, link_category など)
  • term_relationships: カテゴリとの親子関係
  • posts: 記事自体

CakePHP から wordpress のデータを扱う(3) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/1922

と同じように書いていきます。

■ビューを作る

目標となるビューはこんな感じです。

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<MvcWordpress.Models.CategoryModel>>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
	Index
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>カテゴリ一覧</h2>
    <table>
        <tr>
            <th>term_id</th>
            <th>term_taxonomy_id</th>
            <th>name</th>
            <th>slug</th>
        </tr>

    <% foreach (var item in Model) { %>
        <tr>
            <td><%: item.Term.term_id %></td>
            <td><%: item.TermTaxonomy.term_taxonomy_id %></td>
            <td><%: item.Term.name %></td>
            <td><%: item.Term.slug %></td>
        </tr>
    <% } %>
    </table>
    <h2>カテゴリ一覧(リンク版)</h2>
    <ul>
    <% foreach (var item in Model) { %>
        <li><a href="/Categories/Item/<%: item.Term.slug %>"><%: item.Term.name %></a></li>
    <% } %>
    </ul>
</asp:Content>

CakePHP と同じように CategoryModel というモデルクラスを独自に作ります。理由は同じで、wp_terms も wp_term_taxonomy も名前として適切ではないからです。そうそう、後から書きますが、実は今回の場合はデータベースに View を作ったほうがよいです。と言うのも、wp_term の扱いがメタ情報データ的に扱われているので、wp_posts や wp_comments のような通常のテーブルと扱いが違うためです。このあたりの理由は、時間があれば別途。

カテゴリのそれぞれの参照は、「item.Term.term_id」に、元テーブル名.カラム名 のようにアクセスできる形にしておきます。カテゴリのための別名を作っても良いのですが、プログラミングするときに覚えることが増えてしまう(再マッピングのために)ので、この程度の複雑度ならばこのまま使います。カラム名がちょっと不明な感じ(T200idとか)の場合であれば、別名を定義しますが。

■コントローラーを作る

コントローラーはこんな感じにシンプルになります。

public class CategoriesController : Controller
{
    //
    // GET: /Categories/

    public ActionResult Index()
    {
        Models.CategoryModel cate = new Models.CategoryModel();
        var model = cate.GetCategories();
        return View(model);
    }
}

最初作っていたときは、ここに LINQ が入っていたのですが、やめました。理由としては、

  • データベースで外部キー/リレーションを使っているのに、有効活用できていない。
  • リレーションの複雑さをコントローラーに持ち込むのは適切ではない。モデルとしてデータベースに近いところに置くべきだろう。

としたためです。ちょっとした試行錯誤の結果ですが、簡単なクエリであればコントローラーにおいてもよいのですが、複雑になるとちょっとという感じがしますね。このあたり、ASP.NET MVC の場合は、コントローラーとモデルの役割分担が難しいという感じがします。逆に言えば、CakePHP のほうは、モデルでやるのが適切という強制力が働いています。

■モデルを作る

モデルでは、このように LINQ to Entities のクラスを活用しています。

public class CategoryModel
{
    public wp_terms Term { get; set; }
    public wp_term_taxonomy TermTaxonomy { get; set; }

    /// <summary>
    /// カテゴリ一覧を取得
    /// </summary>
    /// <returns></returns>
    public List<CategoryModel> GetCategories()
    {
        wordpressEntities ent = new wordpressEntities();
        List<CategoryModel> model = new List<CategoryModel>();
        var items = from t in ent.wp_terms
                    where t.wp_term_taxonomy.FirstOrDefault().taxonomy == "category"
                    select t;
        foreach ( var item in items )
        {
            model.Add(new CategoryModel
            {
                Term = item,
                TermTaxonomy = item.wp_term_taxonomy.First()
            });
        }
        return model; 
    }
}

カテゴリ一覧を表示する時に必要なテーブルは2つ(wp_termとwp_term_taxonomy)なので、wp_term を中心にして検索しています。
カテゴリだけを表示するための LINQ に工夫があって、アソシエーションを利用しています。

こんな風に既に EDM(Entity Data Model)のほうには外部キーが設定してあるので、相互にテーブルが参照できます。具体的には、t.wp_term_taxonomy.taxonomy として、wp_term_taxnomy テーブルのカラムを指定できる訳ですね。ここでは、wp_term(1)-(*)wp_term_taxnomy という関係なので(私の外部キーの設定が間違っているのでこんな風になっています。本来は 1対1 です)、FirstOrDefault メソッドを使ってコレクションの最初を拾ってきていますが、アソシエーションを設定しておくとデータベースの外部キーを LINQ で再度 join しなくて済みます。
と言いますか、データベースに指定してあるのに、再び LINQ で join するのは O/R マッピング的に本末転倒ですよね。関係というロジックが2箇所に分散されてしまいます。

これを実行した結果が次です。

綺麗に表示されていますが、実際の作成手順は CakePHP と逆になりますね。ASP.NET MVC だとどうしてもコンパイルエラーに引かれてしまうのと、インテリセンスを使ってのコーディングが普通なので、次の順序で作ります(実際作ったし)。

  1. wordpress のデータベースに外部キーを設定。
  2. データベースから EDM へ取り込む(モデルの作成)
  3. EDM を使って コントローラーを仮に作成。LINQ 版を作る。
  4. ビューを作成して、LINQ のテスト。
  5. EDM を使って CategoryModel を作成。
  6. コントローラーの LINQ を CategoryModel に移してリファクタリング

な手順です。いきなり、CategoryModel を作ろうとすると失敗するので、まずはコントローラーに書いて実験するところから始めました。そして、方針が固まったら CategoryModel に移してという流れですね。

お次は、カテゴリ内の一覧を ASP.NET MVC で作ります。

カテゴリー: 開発, ASP.NET, Wordpress パーマリンク