symfony表單與頁面實現(xiàn)技巧
本文實例講述了symfony表單與頁面實現(xiàn)技巧。分享給大家供大家參考。具體如下:
symfony開發(fā)很簡潔,但是功能的數(shù)量仍然很缺乏?,F(xiàn)在是時候進行一些askeet站點與用戶之間的交互了。而HTML交互的根本--除了起鏈接--就是表單了。
這里我們的目標(biāo)是允許用戶登陸,并在主頁的問題列表中進行翻閱。這對于開發(fā)而言是很快的,并且可以讓我們回憶起前面的內(nèi)容。
登陸表單
在測試數(shù)據(jù)中存在用戶,但是程序卻沒有辦法來進行驗證。下面我們要在程序的每一個頁面添加一個登陸表單。打開全局的布局文件askeet/apps/frontend/templates/layout.php,并且在到about的連接之前添加下面的代碼行:
當(dāng)前的布局將這些鏈接放在web調(diào)試工具欄之后。要看到這些鏈接,點擊'Sf'圖標(biāo)折疊起調(diào)試工具欄就可以看到了。
現(xiàn)在需要創(chuàng)建user模塊。而question模塊是在第二天生成的,這一次我們只是叫symfony來創(chuàng)建模塊框架,而我們將會自己來編寫這些代碼。
這個框架包含一個默認的index動作與一個indexSuccess.php模板。刪除他們,因為我們并不需要他們。
創(chuàng)建user/login動作
public function executeLogin()
{
$this->getRequest()->setAttribute('referer', $this->getRequest()->getReferer());
return sfView::SUCCESS;
}
這個動作將referer保存在請求屬性中。然后這個屬性可為模塊所用存放在一個隱藏區(qū)域中,從而這個表單的目的動作可以在成功登陸后重定向到原始的referer。
語句return sfView::SUCCESS將動作執(zhí)行結(jié)果傳遞到loginSuccess.php模塊。這條語句是在一個不包含返回語句的動作中實現(xiàn)的,這也就是一個動作的默認模塊被稱之為actionnameSuccess.php的原因。
在動作上開始更多的工作之前,我們先來看一下模塊。
創(chuàng)建loginSuccess.php模塊
web上的許多人機交互使用表單,而Symfony通過提供一個form幫助器集合來組織表單的創(chuàng)建與管理。
在askeet/apps/frontend/modules/user/templates/目錄下,創(chuàng)建下面的loginSuccess.php模塊:
<fieldset>
<div class="form-row">
<label for="nickname">nickname:</label>
<?php echo input_tag('nickname', $sf_params->get('nickname')) ?>
</div>
<div class="form-row">
<label for="password">password:</label>
<?php echo input_password_tag('password') ?>
</div>
</fieldset>
<?php echo input_hidden_tag('referer', $sf_request->getAttribute('referer')) ?>
<?php echo submit_tag('sign in') ?>
</form>
這個模塊是我們第一次使用表單幫助器。這些Symfony函數(shù)可以幫助我們自動化編寫表單標(biāo)簽。form_tag()打開一從此標(biāo)簽,使用POST作為默認的動作,并且指向作為參數(shù)傳遞的動作。input_tag()幫助器產(chǎn)生一個<input>標(biāo)簽,并且依據(jù)所傳遞的第一個參數(shù)自動添加一個id屬性;而默認值則是由第二個參數(shù)得到。我們可以在Symfony一書的相關(guān)章節(jié)查找到更多的關(guān)于表單幫助器與他們所產(chǎn)生的HTML代碼的內(nèi)容。
這里的實質(zhì)是當(dāng)表單提交時則會調(diào)用這個動作。所以我們返回來看一下這個動作。
處理表單提交
用下面的代碼來替換我們剛才所編寫的登陸動作:
{
if ($this->getRequest()->getMethod() != sfRequest::POST)
{
// display the form
$this->getRequest()->setAttribute('referer', $this->getRequest()->getReferer());
}
else
{
// handle the form submission
$nickname = $this->getRequestParameter('nickname');
$c = new Criteria();
$c->add(UserPeer::NICKNAME, $nickname);
$user = UserPeer::doSelectOne($c);
// nickname exists?
if ($user)
{
// password is OK?
if (true)
{
$this->getUser()->setAuthenticated(true);
$this->getUser()->addCredential('subscriber');
$this->getUser()->setAttribute('subscriber_id', $user->getId(), 'subscriber');
$this->getUser()->setAttribute('nickname', $user->getNickname(), 'subscriber');
// redirect to last page
return $this->redirect($this->getRequestParameter('referer', '@homepage'));
}
}
}
}
登陸動作可以同時用來顯示登陸表單并且進行處理。相應(yīng)的,他必須知道所調(diào)用的環(huán)境。如果這個動作并沒有在POST模式下調(diào)用(因為是由一個鏈接來請求的):而這正是我們在前面所討論的情況。如果是在POST模式下請求的,那么則會由表單調(diào)用這個動作并進行相應(yīng)的處理。
這個動作會由請求參數(shù)得到nickname域的值,并且查詢User表來查看在數(shù)據(jù)庫是否存在此用戶。
將來一個密碼控制將會為用戶分配憑證。但是現(xiàn)在,這個動作所做的只是在一個會話屬性中存儲用戶的id與nickname屬性。最后,這個動作重定向到表單中隱藏中的原始referer域,這是作為一個請求參數(shù)傳遞的。如果這個域是空的,則會使用默認值。
這里我們需要注意這個例子中兩種類型的屬性集合之間的區(qū)別:request attributes($this->getRequest()->setAttribute())是為模板所保存的,而且只要答案發(fā)送到referer則會被忘記。session attributes($this->getUser()->setAttribute())是在整個用戶會話生命期被保存的,而且在將來其他的動作也可以訪問他們。如果我們希望了解更多的關(guān)于屬性的內(nèi)容,我們可以查看Symfony一書的參數(shù)保存器一節(jié)。
分配權(quán)限
用戶可以登陸進askeet網(wǎng)站是一件好事,但是用戶并不僅是因為好玩而登陸。發(fā)表一個新問題,對某一個問題表示興趣,評價一個評論都需要登陸。而其他的動作將會向非登陸用戶開放。
要將一個用戶設(shè)置為經(jīng)過驗證的,我們需要調(diào)用sfUser對象的->setAuthenticated()方法。這個對象同時提供了一個證書機制(->addCredential()),來通過配置限制訪問。Symfony一書的用戶證書一節(jié)對此進行了詳細的解釋。
這就是下面兩行的目的:
$this->getContext()->getUser()->addCredential('subscriber');
當(dāng)nickname被識別后,不僅用戶數(shù)據(jù)被存放在會話屬性中,而且這個用戶也會被分配網(wǎng)站限制部分的訪問權(quán)限。在明天我們將會看到如何限制驗證用戶的程序訪問。
添加user/logout動作
關(guān)于->setAttribute()方法還有最后一個竅門:最后一個參數(shù)(上面例子中的subscriber)定義了屬性存放的名字空間。一個名字空間不僅允許一個在另一個名字空間存在的名字指定給一個屬性,而且可以使用一個命令快速移除所有這些屬性:
{
$this->getUser()->setAuthenticated(false);
$this->getUser()->clearCredentials();
$this->getUser()->getAttributeHolder()->removeNamespace('subscriber');
$this->redirect('@homepage');
}
使用名字空間可以省去我們一個一個移除這些屬性的麻煩:這只是一行語句。
更新布局
當(dāng)前這個布局即使用戶已經(jīng)登陸仍然顯示一個'login'鏈接。讓我們來修正這一點。在askeet/apps/frontend/templates/layout.php文件中,修改我們在今天的指南開始時所修改的代碼:
<li><?php echo link_to('sign out', 'user/logout') ?></li>
<li><?php echo link_to($sf_user->getAttribute('nickname', '', 'subscriber').' profile', 'user/profile') ?></li>
<?php else: ?>
<li><?php echo link_to('sign in/register', 'user/login') ?></li>
<?php endif ?>
現(xiàn)在是時候進行測試了,我們可以顯示程序的任何一頁,點擊'login'鏈接,輸入一個可用的昵稱('anonymous'為例)并且進行驗證。如果窗口頂部的'login'變?yōu)?sign out',則我們所做的一切都是正確的。最后,試著注銷來查看'login'鏈接是否再次出現(xiàn)。
問題組織
隨著數(shù)以千計的Symfony愛好者訪問askeet網(wǎng)站,在主頁上顯示的問題就會逐漸變多。為了避免變慢的請求速度,問題列的隨意翻閱就成為必須解決的問題。
Symfony為這一目的提供了一個對象:sfPropelPager。他會封裝到數(shù)據(jù)的請求,從而只會查詢當(dāng)前頁面所顯示的記錄。例如,如果一個頁面初始化時每頁只顯示10個問題,則到數(shù)據(jù)的請求只會限制為10個結(jié)果,并且會設(shè)置偏移來在頁面中進行匹配。
修改question/list動作
在前面的練習(xí)中,我們看到了問題模塊的顯示動作:
{
$this->questions = QuestionPeer::doSelect(new Criteria());
}
我們將會修改這個動作來向模板傳遞一個sfPropelPager而不是傳遞一個數(shù)組。同時,我們會依據(jù)感興趣的數(shù)量來對問題進行排序:
{
$pager = new sfPropelPager('Question', 2);
$c = new Criteria();
$c->addDescendingOrderByColumn(QuestionPeer::INTERESTED_USERS);
$pager->setCriteria($c);
$pager->setPage($this->getRequestParameter('page', 1));
$pager->setPeerMethod('doSelectJoinUser');
$pager->init();
$this->question_pager = $pager;
}
sfPropelPager對象的初始化指明了他包含哪個對象類,以及在一個頁面中可以放置的對象的最大數(shù)目(在這個例子中為2)。->setPage()方法使用一個請求參數(shù)來設(shè)置當(dāng)前頁面。例如,如果這個頁面參數(shù)的值為2,sfPropelPager將會返回3到5的結(jié)果。頁面請求參數(shù)的值變?yōu)?,則頁面默認會返回1到2的結(jié)果。我們可以在Symfony一書的頁面章節(jié)中了解到關(guān)于sfPropelPager對象及其方法的更多信息。
使用一個默認參數(shù)
將常量放在我們所使用的配置文件中是一個好主意。例如,每頁的結(jié)果(在這個例子為2)可以由一個在我們自定義的程序配置中的參數(shù)來代替。用下面的代碼來改變上面的sfPropelPager行:
$pager = new sfPropelPager('Question', sfConfig::get('app_pager_homepage_max'));
這里的pager關(guān)鍵字是作為名字空間使用的,這也就是為什么在參數(shù)名字中出現(xiàn)的原因。我們可以在Symfony一書的配置一節(jié)中查看到更多的關(guān)于自定義配置與命名自定義參數(shù)規(guī)則的更多的內(nèi)容。
修改listSuccess.php模板
在listSuccess.php模板中,將下面的代碼行:
替換為
從而頁面顯示存儲在頁面中的結(jié)果列表。
添加頁面瀏覽
在這個模板中還需要做另外一件事:頁面瀏覽?,F(xiàn)在,模板所做的只是顯示前兩個問題,但是我們應(yīng)添加到下一個頁面的功能,以及回到前一個頁面的功能。要完成添加這些功能,我們需要在模板后面添加下面的代碼:
<?php if ($question_pager->haveToPaginate()): ?>
<?php echo link_to('«', 'question/list?page=1') ?>
<?php echo link_to('<', 'question/list?page='.$question_pager->getPreviousPage()) ?>
<?php foreach ($question_pager->getLinks() as $page): ?>
<?php echo link_to_unless($page == $question_pager->getPage(), $page, 'question/list?page='.$page) ?>
<?php echo ($page != $question_pager->getCurrentMaxLink()) ? '-' : '' ?>
<?php endforeach; ?>
<?php echo link_to('>', 'question/list?page='.$question_pager->getNextPage()) ?>
<?php echo link_to('»', 'question/list?page='.$question_pager->getLastPage()) ?>
<?php endif; ?>
</div>
這段代碼利用了sfPropelPager對象的各種方法,以及->haveToPaginate(),這個函數(shù)只有在請求的結(jié)果數(shù)目超過了頁面尺寸時才會返回真;而->getPreviousPage(),->getNextPage(),->getLastPage()都具有明顯示的意義;->getLinks()函數(shù)提供了一個頁面號的數(shù)組;而->getCurrentMaxLink()函數(shù)返回最后的頁面號。
這個例子同時顯示了一個Symfony鏈接幫助器:link_to_unless()會在作為第一個參數(shù)的測試為假的情況下輸出一個常規(guī)link_to(),否則會輸出一個非鏈接的文本,并使用簡單的<span>包裝。
我們測試這個頁面了嗎?我們應(yīng)進行測試。直到我們用我們自己的眼睛來驗證,這個修改才算結(jié)束。要進行測試,打開在第三天所創(chuàng)建的測試數(shù)據(jù)文件,并且為要顯示的頁面瀏覽添加一些問題。重新運行導(dǎo)入數(shù)據(jù)批處理文件,然后再一次請求主頁。
為子頁添加路由規(guī)則
默認情況下,頁面規(guī)則如下:
http://askeet/frontend_dev.php/question/list/page/XX
現(xiàn)在我們利用路由規(guī)則使用這些頁面更易于理解:
http://askeet/frontend_dev.php/index/XX
打開apps/frontend/config/routing.yml文件并且在頂部添加下面內(nèi)容:
url: /index/:page
param: { module: question, action: list }
并且為登陸頁面添加另外的路由規(guī)則:
url: /login
param: { module: user, action: login }
重構(gòu)
模型
question/list動作執(zhí)行與模型相關(guān)的代碼,這也就是我們?yōu)槭裁匆獙⑦@些代碼移動到模塊中的原因。用下面的代碼來代替question/list動作:
{
$this->question_pager = QuestionPeer::getHomepagePager($this->getRequestParameter('page', 1));
}
并且在lib/model中的QuestionPeer.php類中添加下面的方法:
{
$pager = new sfPropelPager('Question', sfConfig::get('app_pager_homepage_max'));
$c = new Criteria();
$c->addDescendingOrderByColumn(self::INTERESTED_USERS);
$pager->setCriteria($c);
$pager->setPage($page);
$pager->setPeerMethod('doSelectJoinUser');
$pager->init();
return $pager;
}
同樣的想法也適用于我們昨天編寫的question/show動作:Propel對象由其剝離的標(biāo)題取回問題的用法應(yīng)屬于這個模塊。所以用下面的代碼來變更question/show動作代碼:
{
$this->question = QuestionPeer::getQuestionFromTitle($this->getRequestParameter('stripped_title'));
$this->forward404Unless($this->question);
}
在QuestionPeer.php文件中添加下面的代碼:
{
$c = new Criteria();
$c->add(QuestionPeer::STRIPPED_TITLE, $title);
return self::doSelectOne($c);
}
模板
在question/templates/listSuccess.php中顯示的問題列表在將來的某些地方還會用到。所以我們將顯示問題列表的模板代碼放在一個_list.php片段中,并且用下面的簡單代碼來代替listSuccess.php的內(nèi)容:
<?php echo include_partial('list',array('question_pager'=>$question_pager)) ?>
希望本文所述對大家的symfony框架程序設(shè)計有所幫助。
- Symfony2聯(lián)合查詢實現(xiàn)方法
- Symfony2使用Doctrine進行數(shù)據(jù)庫查詢方法實例總結(jié)
- 高性能PHP框架Symfony2經(jīng)典入門教程
- Symfony頁面的基本創(chuàng)建實例詳解
- Symfony學(xué)習(xí)十分鐘入門經(jīng)典教程
- Symfony2實現(xiàn)從數(shù)據(jù)庫獲取數(shù)據(jù)的方法小結(jié)
- Symfony2學(xué)習(xí)筆記之模板用法詳解
- Symfony數(shù)據(jù)校驗方法實例分析
- Symfony2框架學(xué)習(xí)筆記之表單用法詳解
- Symfony2創(chuàng)建頁面實例詳解
- Symfony2安裝的方法(2種方法)
- Symfony查詢方法實例小結(jié)
相關(guān)文章
PHP中把stdClass Object轉(zhuǎn)array的幾個方法
PHP和JS通訊通常都用json,但用 json 傳過來的數(shù)組并不是標(biāo)準(zhǔn)的array,而是 stdClass 類型。那么我們可以參考下面的幾個方法進行轉(zhuǎn)換。2014-05-05PHP配置把錯誤日志以郵件方式發(fā)送方法(Windows系統(tǒng))
這篇文章主要介紹了PHP配置把錯誤日志以郵件方式發(fā)送方法(Windows系統(tǒng)),本文給出了配置示例和使用例子,需要的朋友可以參考下2015-06-06SQL注入寬字節(jié)注入由淺到深學(xué)習(xí)
這篇文章主要為大家介紹了SQL注入寬字節(jié)注入由淺到深學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02TP5框架實現(xiàn)的數(shù)據(jù)庫備份功能示例
這篇文章主要介紹了TP5框架實現(xiàn)的數(shù)據(jù)庫備份功能,結(jié)合實例形式分析了TP5數(shù)據(jù)庫備份功能相關(guān)原理及實現(xiàn)方法,需要的朋友可以參考下2020-04-04