鍍金池/ 教程/ PHP/ Yii Framework 開發(fā)教程(4) Hangman 猜單詞游戲?qū)嵗?/span>
Yii Framework 開發(fā)教程(16) UI 組件 StarRating 示例
Yii Framework 開發(fā)教程(2) Yii Web 應(yīng)用基礎(chǔ)
Yii Framework 開發(fā)教程(19) UI 組件 TreeView 示例
Yii Framework 開發(fā)教程(39) Zii 組件-Slider 示例
Yii Framework 開發(fā)教程(45) Zii 組件-Selectable 示例
Yii Framework 開發(fā)教程(44) Zii 組件-Resizable 示例
Yii Framework 開發(fā)教程(8) 使用 FormModel
Yii Framework 開發(fā)教程(42) Zii 組件-Draggable 示例
Yii Framework 開發(fā)教程(18) UI 組件 TextHighlighter 示例
Yii Framework 開發(fā)教程(32) Zii 組件-GridView 示例
Yii Framework 開發(fā)教程(30) Zii 組件-ListView 示例
Yii Framework 開發(fā)教程(9) UI 組件 Widget 概述
Yii Framework 開發(fā)教程(17) UI 組件 TabView 示例
Yii Framework 開發(fā)教程(24) 數(shù)據(jù)庫-DAO 示例
Yii Framework 開發(fā)教程(25) 數(shù)據(jù)庫-Query Builder 示例
Yii Framework 開發(fā)教程(21) UI 組件 自定義 Captcha 示例
Yii Framework 開發(fā)教程(38) Zii 組件-ProgressBar 示例
Yii Framework 開發(fā)教程(20) UI 組件 Captcha 示例
Yii Framework 開發(fā)教程(14) UI 組件 MaskedTextField 示例
Yii Framework 開發(fā)教程(22) UI 組件 Zii 組件簡介
Yii Framework 開發(fā)教程(31) Zii 組件-DetailView 示例
Yii Framework 開發(fā)教程(33) Zii 組件-Accordion 示例
Yii Framework 開發(fā)教程(36) Zii 組件-DatePicker 示例
Yii Framework 開發(fā)教程(6) CComponent 組件
Yii Framework 開發(fā)教程(37) Zii 組件-Dialog 示例
Yii Framework 開發(fā)教程(26) 數(shù)據(jù)庫-Active Record 示例
Yii Framework 開發(fā)教程(29) Zii組件-Menu 示例
Yii Framework 開發(fā)教程(46) Zii 組件-Sortable 示例
Yii Framework 開發(fā)教程(10) UI 組件 自定義組件
Yii Framework 開發(fā)教程(11) UI 組件 ActiveForm 示例
Yii Framework 開發(fā)教程(43) Zii 組件-Droppable 示例
Yii Framework 開發(fā)教程(27) 數(shù)據(jù)庫-關(guān)聯(lián) Active Record 示例
Yii Framework 開發(fā)教程(47) 主題 Theme 示例
Yii Framework 開發(fā)教程(48) 多國語言示例
Yii Framework 開發(fā)教程(35) Zii 組件-Button 示例
Yii Framework 開發(fā)教程(3) 為應(yīng)用添加日志
Yii Framework 開發(fā)教程(23) 數(shù)據(jù)庫-概述
Yii Framework 開發(fā)教程(12) UI 組件 ClipWidget 示例
Yii Framework 開發(fā)教程(41) Zii 組件-Tabs 示例
Yii Framework 開發(fā)教程(34) Zii 組件-AutoComplete 示例
Yii Framework 開發(fā)教程(40) Zii 組件-SliderInput 示例
Yii Framework 開發(fā)教程(5) URL 管理
Yii Framework 開發(fā)教程(4) Hangman 猜單詞游戲?qū)嵗?/span>
Yii Framework 開發(fā)教程(15) UI 組件 MultiFileUpload 示例
Yii Framework 開發(fā)教程(7) 使用 CHtml 創(chuàng)建 Form
Yii Framework 開發(fā)教程(28) Data Provider 簡介
Yii Framework 開發(fā)教程(1) 第一個應(yīng)用 Hello World
Yii Framework 開發(fā)教程(13) UI 組件 ContentDecorator 示例

Yii Framework 開發(fā)教程(4) Hangman 猜單詞游戲?qū)嵗?/h1>

有了前面“Hello,World”的例子和對 Yii Framework Web 應(yīng)用基礎(chǔ)的介紹,可以開始介紹一個簡單而相對而有比較完整的Web應(yīng)用-Hangman(猜單詞游戲),這個例子是隨 Yii 開發(fā)包發(fā)布的。通過這個例子可以了解開發(fā) Yii 應(yīng)用的基本步驟.

說起“Hangman”,讓我想起 80 年代末期高中時在 CPC464 計(jì)算機(jī)上完過的“猜單詞游戲”-Hangman,每猜錯一次,就把一個小人離絞刑架前進(jìn)一步。當(dāng)時 DOS 才剛剛出來:-)。

開發(fā)一個 Web 應(yīng)用,首先是進(jìn)行需求分析,這個不在本教程之內(nèi),但為完整起見,還是把“猜單詞游戲”的規(guī)則列在下面:

猜單詞游戲(英文:Hangman,“上吊的人”之意)是一個雙人游戲。一位玩家想一個字,另一位嘗試猜該玩家所想的字中的每一個字母。

要猜的字以一列橫線表示,讓玩家知道該字有多少個字母。如果猜字的玩家猜中其中一個字母,另一位便須于該字母出現(xiàn)的所有位置上寫上該字母。如果猜的字母沒有于該字中出現(xiàn),另一位玩家便會畫吊頸公仔的其中一筆。游戲會在以下情況結(jié)束:

“我要 t 字?!薄坝校?在第八和第十一位。”

  • 猜字的玩家猜完所有字母,或猜中整個字
  • 另一位玩家畫完整幅圖:

今天給出的例子就不畫出“上吊人”了,猜對了顯示“You Win”,猜錯了顯示“You Lose”。 因此我們可以設(shè)計(jì)四個頁面:

http://wiki.jikexueyuan.com/project/yii-development-tutorial/images/4.1.jpg" alt="picture4.1" />

這四個頁面對應(yīng)到 Yii Framework 為 四個 View,可以分別取名為 play, guess, win,lose ,每個頁面都顯示了“Hangman Game”的標(biāo)題,因此可以設(shè)計(jì)一個”MasterPage”,在 Yii 中成為 Layout 布局的模板以供四個 View 共享。Yii 應(yīng)用采用了 MVC 設(shè)計(jì)模式,因此我們可以為四個 View 設(shè)計(jì)一個 Controller–>GameController.

前面的教程說過 Yii 應(yīng)用使用缺省的目錄結(jié)構(gòu)來存放應(yīng)用的不同部分,可以使用 Yii提供的工具來參加一個缺省的項(xiàng)目目錄。不過我個人還是比較喜歡自己創(chuàng)建各個目錄,因此根據(jù)上面的需求和界面設(shè)計(jì),可以創(chuàng)建項(xiàng)目的目錄結(jié)構(gòu)如下:

http://wiki.jikexueyuan.com/project/yii-development-tutorial/images/4.2.jpg" alt="picture4.2" />

  • 創(chuàng)建的 GameController.php 放在 protected/controller 目錄下。
  • 創(chuàng)建的四個 View guess.php, lose.php, play.php, win.php 放在 protected/views/game 目錄下 。目錄名 game 對應(yīng)到所使用到 GameController.
  • 創(chuàng)建的共享的 Layout 放在 protected/views/layout 目錄下,缺省的布局名稱為 main.php
  • 應(yīng)用的配置文件放在 protected/config ,缺省配置文件為 main.php
  • 應(yīng)用的入口腳本為 index.php
  • 此外,供猜單詞的文本文件為 word.txt

1.首先來看看配置文件 protected/config/main.php

    return array(
        'name'=>'Hangman Game',
        'defaultController'=>'game',
        'components'=>array(
            'urlManager'=>array(
                'urlFormat'=>'path',
                'rules'=>array(
                    'game/guess/<g:\w>'=>'game/guess',
                ),
            ),

        ),
    );

CWebApplication 應(yīng)用的所有可寫的屬性都可以通過配置文件來定義,我們看到配置文件定義了應(yīng)用的名稱為”Hangman Game” ,然后修改 Web 應(yīng)用缺省的 Controller 名字為 game 對應(yīng)到 GameController, 如果沒有重新定義 defaultController,則缺省的 Controller 名字為 SiteController,這樣對于的 View 就要存放到 protected/views/site 目錄下。另外這個 Yii 應(yīng)用打開了 urlManager 組件,這個組件的功能就在后面介紹,主要是用來定義用戶可以訪問的 URL 的格式(路由格式)。

2.有了這個配置文件,就可以在入口腳本中使用它,每個 Yii 應(yīng)用的入口腳本 index.php 都是大同小異的,大部分情況下都是 Copy & Paste :-)

    $yii=dirname(__FILE__).'/../../framework/yii.php';
    $config=dirname(__FILE__).'/protected/config/main.php';
    require_once($yii);
    Yii::createWebApplication($config)->run();

3.然后定義 View 使用的布局文件 protected/views/layout/main.php main.php 為缺省的布局模板,應(yīng)用可以修改 View 使用的布局,本例就是要缺省的布局名稱main.

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Hangman Game</title>
    </head>

    <body>
    <h1>Hangman Game</h1>

    <?php echo $content; ?>

    </body>
    </html>

布局基本上就是 HTML 文件,其中 作為 view 的 placeholder ,也就是在顯示具體的 View 時,比如 play.php 就用 play.php 的內(nèi)容來替代 $content。從而實(shí)現(xiàn)了類似“MasterPage” 的功能。

4.下面就可以逐一定義四個 View,這里就不一一列出了,以 play.php 為例:

    <p>This is the game of Hangman.
    You must guess a word, a letter at a time.
    If you make too many mistakes, you lose the game!</p>

    <?php echo CHtml::beginForm(); ?>

    <?php echo CHtml::radioButtonList('level', null, $levels); ?>

    <br/>
    <?php echo CHtml::submitButton('Play!'); ?>

    <?php if($error): ?>
    <span style="color:red">You must choose a difficulty level!</span>
    <?php endif; ?>

    <?php echo CHtml::endForm(); ?>

可以看到基本上也是 HTML ,其中 CHtml 為 Yii 框架支持的一個輔助類,用來幫助生成 HTML 代碼。 Hangman 比較簡單,因此沒有使用單獨(dú)的 Model,而是通過 render 推送的方式傳入?yún)?shù)。

需通過傳遞視圖的名稱調(diào)用 CController::render()。這個方法將在 protected/views/ControllerID 目錄下尋找對應(yīng)的視圖文件.

在視圖腳本內(nèi)部,我們可以通過 $this 來訪問控制器實(shí)例.我們可以在視圖里以 $this->propertyName 的方式 拉取 控制器的任何屬性.

我們也可以用以下推送的方式傳遞數(shù)據(jù)到視圖里:

       $this->render('edit', array(
        'var1'=>$value1,
        'var2'=>$value2,
    ));

在以上的方式中, render() 方法將提取數(shù)組的第二個參數(shù)到變量里.其產(chǎn)生的結(jié)果是,在視圖腳本里,我們可以直接訪問變量 $var1 和 $var2.

5.定義好布局和 View 之后,就可以寫 GameController 了,

        class GameController extends CController
    {
        /**
         * @var string sets the default action to be 'play'
         */
        public $defaultAction='play';

        /**
         * The 'play' action.
         * In this action, users are asked to choose a difficulty level
         * of the game.
         */
        public function actionPlay()
        {
            ...
        } 

        /**
         * The 'guess' action.
         * This action is invoked each time when the user makes a guess.
         */
         public function actionGuess()
        {
            ...
        }

        /**
         * The 'guess' action.
         * This action is invoked when the user gives up the game.
         */
        public function actionGiveup()
        {
            ...
        }

        ...
    }

一般情況下 Controller 缺省的 action 為 index ,可以通過$defaultAction 修改缺省的 Action,本例修改為 play. 因此如果本例的 url 為 http://127.0.0.1:8888/yii/demos/hangman/ 那么使用 http://127.0.0.1:8888/yii/demos/hangman/index.php 和使用 http://127.0.0.1:8888/yii/demos/hangman/index.php?game/play 的效果是一樣的。缺省的 Controller 為 GameController,GameController 缺省的 action 為 play.

Action (動作),動作可以被定義為一個以 action 單詞作為前綴命名的方法。Hangman 定義了三個 action ,actionPlay ,actionGuess, actionGiveup ,GameController 其它方法和屬性和生成單詞,判斷是否猜對等為具體的游戲邏輯和 Yii 框架關(guān)系不大,就不介紹了。

6.首先看看缺省的 playAction ,這是用戶調(diào)用的缺省方法,也就是說當(dāng)用戶組地址欄輸入 http://127.0.0.1:8888/yii/demos/hangman/index.php (或http://127.0.0.1:8888/yii/demos/hangman/index.php?game/play)所調(diào)用的 Action。

    public function actionPlay()
    {
        static $levels=array(
            '10'=>'Easy game; you are allowed 10 misses.',
            '5'=>'Medium game; you are allowed 5 misses.',
            '3'=>'Hard game; you are allowed 3 misses.',
        );

        // if a difficulty level is correctly chosen
        if(isset($_POST['level'])
           && isset($levels[$_POST['level']]))
        {
            $this->word=$this->generateWord();
            $this->guessWord=str_repeat('_',strlen($this->word));
            $this->level=$_POST['level'];
            $this->misses=0;
            $this->setPageState('guessed',null);
            // show the guess page
            $this->render('guess');
        }
        else
        {
            $params=array(
                'levels'=>$levels,
                // if this is a POST request,
                //it means the level is not chosen
                'error'=>Yii::app()->request->isPostRequest,
            );
            // show the difficulty level page
            $this->render('play',$params);
        }
    }

這個方法定義了游戲的三個難度等級$levels, 有兩個分支,如果沒有選擇難易等級,則調(diào)用$this->render(‘play’,$params),顯示 Play 頁面,就$params (Array)推送到對應(yīng)的 View ,protected/views/play.php,參考上面 View 的定義:

    <?php echo CHtml::radioButtonList('level', null, $levels); ?>

View 使用 Radiobutton來顯示 $levels 定義的列表。

如果用戶選擇了難易等級,在把 Level,單詞等存放到 GameController 所定義的屬性中,如 word,level 等。GameController 拍手與 CController 也是 CComponent 的子類,CComponent 支持了類似 C#,Java 的屬性功能。具體后面再介紹。 然后調(diào)用$this->render(‘guess’); 顯示 Guess 頁面。

Guess 頁面 guess.php 定義如下:

    <h2>Please make a guess</h2>

    <h3 style="letter-spacing: 4px;">
      <?php echo $this->guessWord; ?></h3>

    <p>You have made
      <?php echo $this->misses; ?>
      bad guesses out of a maximum of
      <?php echo $this->level; ?>.</p>

    <?php echo CHtml::statefulForm(); ?>

    <p>Guess:
    <?php
    for($i=ord('A');$i<=ord('Z');++$i)
    {
        if(!$this->isGuessed(chr($i)))
            echo "\n".CHtml::linkButton(chr($i),
            array('submit'=>array('guess','g'=>chr($i))));
    }
    ?>
    </p>

    <p><?php echo CHtml::linkButton('Give up?',
    array('submit'=>array('giveup'))); ?></p>

    </form>

在 View 中可以直接通過 $this 來訪問對應(yīng)的 Controller 實(shí)例對象的方法和屬性。 如 $this->guessWord,$this->isGuessed(chr($i))等。 其中點(diǎn)擊 26 個字母觸發(fā) guessAction (array(‘submit’=>array(‘guess’,’g’=>chr($i))))).

7.下面為 guessAction 的定義

    public function actionGuess()
    {
        // check to see if the letter is guessed correctly
        if(isset($_GET['g'][0]) && ($result=$this->guess($_GET['g'][0]))!==null)
            $this->render($result ? 'win' : 'lose');
        else // the letter is guessed correctly, but not win yet
        {
            $guessed=$this->getPageState('guessed',array());
            $guessed[$_GET['g'][0]]=true;
            $this->setPageState('guessed',$guessed,array());
            $this->render('guess');
        }
    }

其中參數(shù) ‘g’由 guess 頁面提交是傳入, 如果單詞全部猜對在顯示”You win” 或用完所有次數(shù)猜錯顯示“You lose” , $this->render($result ? ‘win’ : ‘lose’), 如果還有機(jī)會猜還是回到 guess 頁面 $this->render(‘guess’);

8.在 Guess 頁面上還有一個“Give up” 按鈕,用戶點(diǎn)擊則觸發(fā) giveupAction.這個方法比較簡單,直接顯示 lose 頁面

    public function actionGiveup()
    {
        $this->render('lose');
    }

至此 Hangman 游戲基本就完成了。游戲雖然簡單,但說明了使用 Yii 開發(fā)應(yīng)用的基本流程,下面給出 Yii 開發(fā)文檔給出的開發(fā)流程,Hangman 比較簡單,沒有用到數(shù)據(jù)庫和國際化等。

此處的開發(fā)流程假設(shè)我們已經(jīng)完成了對應(yīng)用的需求分析和必要的設(shè)計(jì)分析。

  1. 創(chuàng)建目錄結(jié)構(gòu)骨架。創(chuàng)建第一個 Web 應(yīng)用 中講到的 yiic 工具可以快速實(shí)現(xiàn)此步驟。
  2. 配置此應(yīng)用。這是通過修改應(yīng)用配置文件實(shí)現(xiàn)的。 此步驟可能也需要編寫一些應(yīng)用組件(例如用戶組件)。
  3. 為所管理的每個類型的數(shù)據(jù)創(chuàng)建一個 模型 類。 Creating First Yii ApplicationAutomatic Code Generation 講述的 Gii 工具可以用于快速為每個數(shù)據(jù)表創(chuàng)建 active record 類。
  4. 為每個類型的用戶請求 創(chuàng)建一個 控制器 類。 具體如何對用戶請求歸類要看實(shí)際需求??傮w來說,如果一個模型類需要被用戶訪問,他就應(yīng)該有一個相應(yīng)的控制器類。 Gii 工具也可以自動實(shí)現(xiàn)這一步驟。
  5. 實(shí)現(xiàn) 動作 和他們相應(yīng)的 視圖。 這是真正所需要做的工作。
  6. 在控制器類中配置必要的動作 過濾器
  7. 如果需要主題功能,創(chuàng)建 主題 。
  8. 如果需要 國際化(I18N) ,創(chuàng)建翻譯信息。
  9. 對可緩存的數(shù)據(jù)點(diǎn)和視圖點(diǎn)應(yīng)用適當(dāng)?shù)?緩存 技術(shù)。
  10. 最終 調(diào)整 與部署。