Yii ブログシステム作成(6)

Postモデルのカスタマイズ

【Gii】ツールによって生成された”Post”モデルクラスは、2つの点を修正する必要がある。

rules()メソッド:モデルの属性に対する検証ルールを規定
relations()メソッド:リレーショナルオブジェクトを規定

■rules()メソッドのカスタマイズ

ユーザが入力した内容が正しいものであることを保障するため検証ルールを指定する。

例えば要求分析にあった”Post”クラスの”status”属性は、整数の『1,2,3』のいずれかである必要がある。

以上を踏まえた上で”Post”クラスを修正する。
場所:/blog/protected/models/post.php 33行目付近

  public function rules()
  {
    return array(
      // 必須設定(タイトル、記事本文、記事状態)
      array('title, content, status', 'required'),
      // 記事タイトルは128文字迄
      array('title', 'length', 'max'=>128),
      // 記事状態は1(下書き),2(公開),3(アーカイブ)のいずれか
      array('status', 'in', 'range'=>array(1, 2, 3)),
      // 単語構成文字とカンマを許可
      array('tags', 'match', 'pattern'=>'/^[\w\s,]+$/', 'message'=>'タグは単語構成文字だけを含む事'),
      // ※1
      array('tags', 'normalizeTags'),
      // サーチ機能で使用
      array('title, status', 'safe', 'on'=>'search'),
    );
    
  }

※normalizeTagsについて

ユーザが入力したタグの文字列を正規化し、ユニークなタグがカンマで正しく分離されている文字列になるようにする。
"normalizeTags"バリデータはメソッドベースのバリデータであり、"Post"クラスにおいて定義する必要がある。

検証ルールを定義する方法に関する詳細の情報をは ガイド を参照。

■normalizeTagsを実装
場所:/blog/protected/models/Post.php 新規追加

  /**
   * 
   * タグ文字列を正規化
   * 
   **/
  public function normalizeTags($attribute, $params)
  {
    $this->tags = Tag::array2string(array_unique(Tag::string2array($this->tags)));
  }

場所:/blog/protected/models/Tag.php 新規追加

  /**
   * 
   * 正規表現で文字列を分割
   * 
   **/
  public static function string2array($tags)
  {
    return preg_split('/\s*,\s*/', trim($tags), -1, PREG_SPLIT_NO_EMPTY);
  }
  
  /**
   * 
   * 文字列を配列化
   * 
   **/
  public static function array2string()
  {
    return implode(', ', $tags);
  }

以上で新しいルールが機能していることが確認できる。

yii028

■relations()メソッドのカスタマイズ

relations()で関連オブジェクトを宣言する
場所:/blog/protected/models/Post.php 50行目付近

  public function relations()
  {
    return array(
      'author'=>array(
        self::BELONGS_TO,
        'User',
        'author_id',
      ),
      'comments'=>array(
        self::HAS_MANY,
        'Comment',
        'post_id',
        'condition'=>'comments.status='.Comment::STATUS_APPROVED,
        'order'=>'comments.create_time DESC',
      ),
      'commentCount'=>array(
        self::STAT,
        'Comment',
        'post_id',
        'condition'=>'status='.Comment::STATUS_APPROVED
      ),
    );
  }

※リレーション宣言について

・BELONG_TO:テーブルAとBの関係が1対多ならば、BはAに属する。
・HAS_MANY:同じくテーブルAとBの関係が1対多ならば、Aは多くのBを持っている。
・STAT:集計クエリを実行するリレーション。詳細は  統計クエリ 

同時に、上記メソッドで使用されている2つの定数を”Comment”モデルに追加する
場所:/blog/protected/models/Comment.php 20行目付近 新規追加

class Comment extends CActiveRecord
{
  const STATUS_PENDING = 1;
  const STATUS_APPROVED = 2;
  
  ・・・
}
・一つの記事は、一つの投稿者に所属する。投稿者のクラスは"User"で、記事の"author_id"属性で結び付けられる。
・一つの記事は、多数のコメントを持つ。コメントのクラスは"Comment"で、コメントの"post_id"属性で結び付けられる。
コメントは作成日順にソートされ、承認済みのコメントだけで構成される。
・"commentCount"は集計結果を返す少し特殊なリレーションで記事が持つコメンの数を表す。

上記のリレーションを宣言することで、データにアクセスが出来る。

// 投稿者名を出力
$author = $post->author;
echo $author->username;

// コメントを出力
$comments = $post->comments;

foreach($comments as $comment)
{
  echo $comment->content;
}

■urlプロパティの追加

記事のURLを取得するためのコードCWebApplication::createUrlをいたるところに書かず
モデルにurlプロパティを追加することでURLを生成する同一のコードを再利用出来るようにする。

getterメソッドgetUrlを追加する
場所:/blog/protected/models/Post.php 新規追加

  /**
   * 
   * URL取得
   * 
   **/
  public function getUrl()
  {
    return Yii::app()->createUrl(
      'post/view',
      array(
        'id'=>$this->id,
        'title'=>$this->title,
      )
    );
  }
URLのGETパラメータとして、postのIDに加えてtitleを追加することで検索エンジン(SEO)最適化を目的としている。
Postの最上位の親クラスは"CComponent"なので、『getUrl()』というgetterメソッドが実行され、その結果が式の値
として返される。

■ステータスをテキストで表現する

記事のステータスは整数(1~3)でデータベースに保存されているためエンドユーザには解りづらい。
このためステータスを”tbl_lookup”テーブルを利用してテキスト形式での表現を提供する。

“Lookup”モデルを下記のように修正する
場所:/blog/protected/models/Lookup.php 新規追加

  private static $_items = array();
  
  /**
   * 
   * 指定したデータタイプに属するリストを返す
   * 
   **/
  public static function items($type)
  {
    if(!isset(self::$_items[$type]))
    {
      self::loadItems($type);
    }
    
    return self::$_items[$type];
  }
  
  /**
   * 
   * 指定したデータのタイプと値に対応する文字列を返す
   * 
   **/
  public static function item($type, $code)
  {
    if(!isset(self::$_items[$type]))
    {
      self::loadItems($type);
    }
    
    return isset(self::$_items[$type][$code]) ? self::$_items[$type][$code] : false;
  }
  
  /**
   * 
   * アイテム読み込み
   * 
   **/
  private static function loadItems($type)
  {
    self::$_items[$type] = array();
    
    // 属するタイプの文字列リストを取得する
    $models = self::model()->findAll(
      array(
        'condition'=>'type=:type',
        'params'=>array(':type'=>$type),
        'order'=>'position',
      )
    );
    // 配列にデータを詰める
    foreach($models as $model)
    {
        self::$_items[$type][$model->code] = $model->name;
    }
  }
ブログのデータベースには、"Lookup"のタイプとして下記のデータが登録されている。

"PostStatus":記事のステータスがとり得る値
"CommentStatus"コメントのステータスがとり得る値

さらに、コードを読みやすくするために、一連のステータスを表す整数値の定数を宣言する。
場所:/blog/protected/models/Post.php 新規追加

class Post extends CActiveRecord
{
  const STATUS_DRAFT = 1;
  const STATUS_PUBLISHED = 2;
  const STATUS_ARCHIVED = 3;
  
  ・・・
  
}

■記事ステータス呼び出し例
記事の一覧ページでステータスを文字列表示する
場所:/blog/protected/views/post/index.php

<?php
// 記事データ取得
$posts = Post::model()->findAll(
	array(
		'order'=>'create_time DESC',
	)
);

// 記事データ出力
foreach($posts as $post)
{
	echo '<p><b>ID: </b>'.$post->id.'</p>';
	echo '<b>Title: </b>'.$post->title.'<br />';
	echo '<b>Content: </b>'.$post->content.'<br />';
	echo '<b>Tags: </b>'.$post->tags.'<br />';
	// 記事ステータスの文字列を取得
	echo '<b>Status: </b>'.Lookup::item('PostStatus', $post->status).'<br />';
	echo '<b>Create Time: </b>'.$post->create_time.'<br />';
	echo '<b>Update Time: </b>'.$post->update_time.'<br />';
	echo '<hr />';
}
?>

yii029

これで記事のステータスが整数値ではなく文字列で表示される

参考サイト:
http://www.yiiframework.com/doc/blog/1.1/ja/prototype.auth

Comments are closed.