0 フォロワー

投稿モデルのカスタマイズ

Giiツールによって生成されたPostモデルクラスは、主に2箇所を変更する必要があります。

  • rules()メソッド:モデル属性の検証ルールを指定します。
  • relations()メソッド:関連オブジェクトを指定します。

情報:モデルは、属性のリストで構成され、それぞれ対応するデータベーステーブルの列に関連付けられています。属性は、クラスメンバ変数として明示的に宣言することも、宣言せずに暗黙的に宣言することもできます。

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

まず、ユーザーが入力した属性値がデータベースに保存される前に正しいことを保証する検証ルールを指定します。たとえば、Poststatus属性は、整数1、2、または3である必要があります。Giiツールは、各モデルの検証ルールも生成します。ただし、これらのルールはテーブル列の情報に基づいており、適切ではない場合があります。

要件分析に基づいて、rules()メソッドを次のように変更します。

public function rules()
{
    return array(
        array('title, content, status', 'required'),
        array('title', 'length', 'max'=>128),
        array('status', 'in', 'range'=>array(1,2,3)),
        array('tags', 'match', 'pattern'=>'/^[\w\s,]+$/',
            'message'=>'Tags can only contain word characters.'),
        array('tags', 'normalizeTags'),
 
        array('title, status', 'safe', 'on'=>'search'),
    );
}

上記では、titlecontentstatus属性が必要であること、titleの長さが128を超えてはならないこと、status属性値が1(下書き)、2(公開)、または3(アーカイブ)であること、tags属性に含まれるのは英数字とコンマのみであることを指定しています。さらに、normalizeTagsを使用してユーザーが入力したタグを正規化し、タグを一意にし、コンマで適切に区切ります。最後のルールは、後で説明する検索機能で使用されます。

requiredlengthinmatchなどのバリデータは、Yiiによって提供される組み込みバリデータです。normalizeTagsバリデータは、Postクラスで定義する必要があるメソッドベースのバリデータです。検証ルールの指定方法の詳細については、ガイドを参照してください。

public function normalizeTags($attribute,$params)
{
    $this->tags=Tag::array2string(array_unique(Tag::string2array($this->tags)));
}

ここで、array2stringstring2arrayは、Tagモデルクラスで定義する必要がある新しいメソッドです。

public static function string2array($tags)
{
    return preg_split('/\s*,\s*/',trim($tags),-1,PREG_SPLIT_NO_EMPTY);
}
 
public static function array2string($tags)
{
    return implode(', ',$tags);
}

rules()メソッドで宣言されたルールは、モデルインスタンスのvalidate()メソッドまたはsave()メソッドを呼び出すと、1つずつ実行されます。

注記:rules()に表示される属性は、エンドユーザーが入力する属性であることを覚えておくことが非常に重要です。Postモデルのidcreate_timeなど、コードまたはデータベースによって設定されるその他の属性は、rules()に含めるべきではありません。詳細については、属性の割り当ての保護を参照してください。

これらの変更を加えた後、投稿作成ページに再びアクセスして、新しい検証ルールが有効になっていることを確認できます。

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

最後に、relations()メソッドをカスタマイズして、投稿の関連オブジェクトを指定します。これらの関連オブジェクトをrelations()で宣言することで、複雑なSQL JOIN文を書く必要なく、作成者やコメントなど、投稿の関連オブジェクト情報にアクセスするために、強力なRelational ActiveRecord (RAR)機能を利用できます。

relations()メソッドを次のようにカスタマイズします。

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),
    );
}

上記メソッドで使用される2つの定数をCommentモデルクラスにも導入します。

class Comment extends CActiveRecord
{
    const STATUS_PENDING=1;
    const STATUS_APPROVED=2;
    ......
}

relations()で宣言されたリレーションは、次のことを示しています。

  • 投稿は、クラスがUserであり、投稿のauthor_id属性値に基づいて関係が確立される作成者に属します。
  • 投稿には、クラスがCommentであり、コメントのpost_id属性値に基づいて関係が確立される多くのコメントがあります。これらのコメントは作成時刻に従ってソートされ、コメントは承認済みである必要があります。
  • commentCountリレーションはやや特殊で、投稿が持つコメント数の集計結果を返します。

上記のリレーション宣言により、次のように投稿の作成者とコメントに簡単にアクセスできます。

$author=$post->author;
echo $author->username;
 
$comments=$post->comments;
foreach($comments as $comment)
    echo $comment->content;

リレーションの宣言方法と使用方法の詳細については、ガイドを参照してください。

3. urlプロパティの追加

投稿は、表示するための固有のURLに関連付けられたコンテンツです。コードのいたるところでCWebApplication::createUrlを呼び出してこのURLを取得する代わりに、Postモデルにurlプロパティを追加して、同じURL作成コードを再利用できるようにすることができます。後でURLの整形方法について説明しますが、このプロパティを追加すると非常に便利になります。

urlプロパティを追加するには、次のようなゲッターメソッドを追加してPostクラスを変更します。

class Post extends CActiveRecord
{
    public function getUrl()
    {
        return Yii::app()->createUrl('post/view', array(
            'id'=>$this->id,
            'title'=>$this->title,
        ));
    }
}

投稿IDに加えて、URLに投稿タイトルもGETパラメーターとして追加することに注意してください。URLの整形で説明するように、これは主に検索エンジン最適化(SEO)を目的としています。

CComponentPostの最終的な祖先クラスであるため、ゲッターメソッドgetUrl()を追加すると、$post->urlのような式を使用できます。$post->urlにアクセスすると、ゲッターメソッドが実行され、その結果が式の値として返されます。このようなコンポーネント機能の詳細については、ガイドを参照してください。

4. テキストでのステータスの表現

投稿のステータスはデータベースに整数として保存されているため、エンドユーザーに表示する際に直感的に理解できるように、テキスト表現を提供する必要があります。大規模システムでは、同様の要件が非常に一般的です。

一般的なソリューションとして、tbl_lookupテーブルを使用して、整数値と他のデータオブジェクトが必要とするテキスト表現間のマッピングを保存します。テーブル内のテキストデータに簡単にアクセスできるように、Lookupモデルクラスを次のように変更します。

class Lookup extends CActiveRecord
{
......
 
    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;
    }
}

新しいコードは、主に2つの静的メソッド、Lookup::items()Lookup::item()を提供します。前者は指定されたデータ型に属する文字列のリストを返し、後者は指定されたデータ型とデータ値の特定の文字列を返します。

ブログデータベースには、PostStatusCommentStatusという2つのルックアップタイプが事前に設定されています。前者は可能な投稿ステータスを表し、後者はコメントステータスを表します。

コードを読みやすくするために、ステータス整数値を表す一連の定数を宣言します。対応するステータス値を参照する際には、コード全体でこれらの定数を使用する必要があります。

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

したがって、Lookup::items('PostStatus')を呼び出して、可能な投稿ステータスのリスト(対応する整数値によってインデックス付けされたテキスト文字列)を取得し、Lookup::item('PostStatus', Post::STATUS_PUBLISHED)を呼び出して、公開ステータスの文字列表現を取得できます。

タイプミスを見つけたり、このページの改善が必要だと思われる場合は、
GitHubで編集してください !