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 だとどうしてもコンパイルエラーに引かれてしまうのと、インテリセンスを使ってのコーディングが普通なので、次の順序で作ります(実際作ったし)。
- wordpress のデータベースに外部キーを設定。
- データベースから EDM へ取り込む(モデルの作成)
- EDM を使って コントローラーを仮に作成。LINQ 版を作る。
- ビューを作成して、LINQ のテスト。
- EDM を使って CategoryModel を作成。
- コントローラーの LINQ を CategoryModel に移してリファクタリング
な手順です。いきなり、CategoryModel を作ろうとすると失敗するので、まずはコントローラーに書いて実験するところから始めました。そして、方針が固まったら CategoryModel に移してという流れですね。
お次は、カテゴリ内の一覧を ASP.NET MVC で作ります。