Symfony 筆記 (6): User Login & Access Restriction

雖然說其實翻翻文件就知道要改哪裡,在這邊記錄下來也只是方便自己不用再回去翻。其實在 Symfony 裡面要實作 Access Restriction (存取限制?) 很簡單,真的只是改 2 個 yml 檔案而已:

  1. apps/myapp/modules/mymodule/config/security.yml
  2. apps/myapp/config/settings.yml

apps/myapp/modules/mymodule/config/security.yml 定義著每個 module 下面 action 的存取限制。如果 is_secure = on 的話那就必須要有被認證過 (在 action 下面被 $this->getUser()->setAuthenticated(true);) 才能存取,不然的話依照另一個檔案的設定會被導到預設的 login page。
如果有設定 credentials 的話那除了必須要 authenticated 以外還得有被 addCrendential 過才行。

read:
  is_secure:   off       # All users can request the read action

update:
  is_secure:   on        # The update action is only for authenticated users

delete:
  is_secure:   on        # Only for authenticated users
  credentials: admin     # With the admin credential

all:
  is_secure:  off        # off is the default value anyway

apps/myapp/config/settings.yml 可以設定預設的 login module 和 action

all:
  .actions:
    login_module:           default
    login_action:           login

    secure_module:          default
    secure_action:          secure

簡易的登入登出會是像這樣子

class myAccountActions extends sfActions
{
  public function executeLogin()
  {
    if ($this->getRequestParameter('login') == 'foobar')
    {
      $this->getUser()->setAuthenticated(true);
    }
  }

  public function executeLogout()
  {
    if ($this->getUser()->isAuthenticated())
    {
      $this->getUser()->setAuthenticated(false);
    }
  }
}

然後有關 Credential 的 Demo:

class myAccountActions extends sfActions
{
  public function executeDoThingsWithCredentials()
  {
    $user = $this->getUser();

    // Add one or more credentials
    $user->addCredential('foo');
    $user->addCredentials('foo', 'bar');

    // Check if the user has a credential
    echo $user->hasCredential('foo');                      =>   true

    // Check if the user has both credentials
    echo $user->hasCredential(array('foo', 'bar'));        =>   true

    // Check if the user has one of the credentials
    echo $user->hasCredential(array('foo', 'bar'), false); =>   true

    // Remove a credential
    $user->removeCredential('foo');
    echo $user->hasCredential('foo');                      =>   false

    // Remove all credentials (useful in the logout process)
    $user->clearCredentials();
    echo $user->hasCredential('bar');                      =>   false
  }
}

最後是在 template 裡面要知道 credential 也是透過 $sf_user 這個看過才知道的物件。

<ul>
  <li><?php echo link_to('section1', 'content/section1') ?></li>
  <li><?php echo link_to('section2', 'content/section2') ?></li>
  <?php if ($sf_user->hasCredential('section3')): ?>
  <li><?php echo link_to('section3', 'content/section3') ?></li>
  <?php endif; ?>
</ul>

Symfony 筆記 (5): Session & Cookie

Symfony 裡面的 Session 是透過 sfUser 這個 class 來完成的,可以在 action 裡面透過 $this->getUser() 來取得。關於 cookie 本身的設定則是在 apps/myapp/config/factories.yml 下面。Session 的存活時間 (timeout) 則是在 apps/myapp/config/settings.yml 裡面。

class mymoduleActions extends sfActions
{
  public function executeFirstPage()
  {
    $nickname = $this->getRequestParameter('nickname');

    // Store data in the user session
    $this->getUser()->setAttribute('nickname', $nickname);
  }

  public function executeSecondPage()
  {
    // Retrieve data from the user session with a default value
    $nickname = $this->getUser()->getAttribute('nickname', 'Anonymous Coward');
  }
}

除掉 Session 裡面的資料:

class mymoduleActions extends sfActions
{
  public function executeRemoveNickname()
  {
    $this->getUser()->getAttributeHolder()->remove('nickname');
  }

  public function executeCleanup()
  {
    $this->getUser()->getAttributeHolder()->clear();
  }
}

然後在 Template 裡面則是用 $sf_use 這個預設的物件來存取,沒看 documentation 鬼才知道。

<p>
  Hello, <?php echo $sf_user->getAttribute('nickname') ?>
</p>

Symfony 筆記 (4): sfActions

看到這段 Code 的時候真是有種喔原來有這些喔的感覺,趕快記錄下來。這些都是在 Action 裡面可以用的 method,重點是沒有查 API 誰會知道有這些可以用。[sfActions API]

class mymoduleActions extends sfActions
{
  public function executeIndex()
  {
    // Retrieving request parameters
    $password    = $this->getRequestParameter('password');

    // Retrieving controller information
    $moduleName  = $this->getModuleName();
    $actionName  = $this->getActionName();

    // Retrieving framework core objects
    $request     = $this->getRequest();
    $userSession = $this->getUser(); // 這個最不像
    $response    = $this->getResponse();
    $controller  = $this->getController();
    $context     = $this->getContext();

    // Setting action variables to pass information to the template
    $this->setVar('foo', 'bar');
    $this->foo = 'bar';            // Shorter version

  }
}

以上的 $this 如果在 Template 裡面要呼叫的話,記得使用 $sf_context 這個物件。
sfActions 的不同用法:

class mymoduleActions extends sfActions
{
  public function preExecute()
  {
    // The code inserted here is executed at the beginning of each action call
    ...
  }

  public function executeIndex()
  {
    ...
  }

  public function executeList()
  {
    ...
    $this->myCustomMethod();  // Methods of the action class are accessible
  }

  public function postExecute()
  {
    // The code inserted here is executed at the end of each action call
    ...
  }

  protected function myCustomMethod()
  {
    // You can also add your own methods, as long as they don't start with "execute"
    // In that case, it's better to declare them as protected or private
    ...
  }
}

File Upload 在 Symfony 下面非常簡單就解決,想想看如果指有用 php 寫處理個檔案上傳要寫幾行?

class mymoduleActions extends sfActions
{
  public function executeUpload()
  {
    if ($this->getRequest()->hasFiles())
    {
      foreach ($this->getRequest()->getFileNames() as $fileName)
      {
        $fileSize  = $this->getRequest()->getFileSize($fileName);
        $fileType  = $this->getRequest()->getFileType($fileName);
        $fileError = $this->getRequest()->hasFileError($fileName);
        $uploadDir = sfConfig::get('sf_upload_dir');
        $this->getRequest()->moveFile('file', $uploadDir.'/'.$fileName);
      }
    }
  }
}

Symfony 筆記 (3): MVC Demo

Model-view-controller (MVC) is an architectural pattern used in software engineering. Successful use of the pattern isolates business logic from user interface considerations, resulting in an application where it is easier to modify either the visual appearance of the application or the underlying business rules without affecting the other. In MVC, the Model represents the information (the data) of the application and the business rules used to manipulate the data, the View corresponds to elements of the user interface such as text, checkbox items, and so forth, and the Controller manages details involving the communication to the model of user actions such as keystrokes and mouse movements.


在 Symfony 裡面不用 MVC 來寫程式的話那就完全沒有意義。Model 是資料庫的 Layer,View 是 Template Layer,而 Controller 則是運算 Layer。雖然這些觀念不會太難,但是實際上要把舊有的程式分開還是得仔細想想。下面是書裡面的一段 Demo,算是很好的示範。

Flat PHP File
這種大概是最古老的寫作方式,把所有的程式邏輯與資料庫存取通通包在同一個檔案裡面,HTML 和 PHP 還有 SQL 通通混在一起不但不好修改,而且要維護也比較上多很難。

<?php

// Connecting, selecting database
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);

// Performing SQL query
$result = mysql_query('SELECT date, title FROM post', $link);

?>

<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
   <h1>List of Posts</h1>
   <table>
     <tr><th>Date</th><th>Title</th></tr>
<?php
// Printing results in HTML
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
echo "\t<tr>\n";
printf("\t\t<td> %s </td>\n", $row['date']);
printf("\t\t<td> %s </td>\n", $row['title']);
echo "\t</tr>\n";
}
?>
    </table>
  </body>
</html>

<?php

// Closing connection
mysql_close($link);

?>

MVC - Model
Model 是 Database 的 Abstraction。這裡是一段小 demo,實際上 Symfony 使用的是 Propel,大部分的 accessor method 都會自動生出來。

<?php

function open_connection($host, $user, $password)
{
  return mysql_connect($host, $user, $password);
}

function close_connection($link)
{
  mysql_close($link);
}

function query_database($query, $database, $link)
{
  mysql_select_db($database, $link);

  return mysql_query($query, $link);
}

function fetch_results($result)
{
  return mysql_fetch_array($result, MYSQL_ASSOC);
}

function getAllPosts()
{
  // Connecting to database
  $link = open_connection('localhost', 'myuser', 'mypassword');

  // Performing SQL query
  $result = query_database('SELECT date, title FROM post', 'blog_db', $link);

  // Filling up the array
  $posts = array();
  while ($row = fetch_results($result))
  {
     $posts[] = $row;
  }

  // Closing connection
  close_connection($link);

  return $posts;
}

?>

MVC - Controller
Controller 是邏輯的部份。這個 demo 需要用到的邏輯根本沒有,就只是 “從資料庫裡面找出所有 post” 而已,所以基本上只有一行。

<?php

// Requiring the model
require_once('model.php');

// Retrieving the list of posts
$posts = getAllPosts();

// Requiring the view
require('view.php');

?>

MVC - View (Template)
Symfony 的 view 大致上可以分為 Template 和 Layout,差別在 Layout 比 Template 大,或者說 Template 在 Layout 裡面。

<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post['date'] ?></td>
    <td><?php echo $post['title'] ?></td>
  </tr>
<?php endforeach; ?>
</table>

MVC - View (Layout)

<?php 

$title = 'List of Posts';
$content = include('mytemplate.php');

?>
<html>
  <head>
    <title><?php echo $title ?></title>
  </head>
  <body>
    <?php echo $content ?>
  </body>
</html>

實際在 Symfony 裡面每個檔案所在的地方不同,雖然一開始有點混亂,但是學習後會發現其實 Symfony 把檔案分配的很好,就類似設計很好的作業系統或者Mudlib之類的感覺吧。

Symfony 筆記 (2): YAML

Symfony 的設定檔都是用 YAML 格式來搞定的,當然要用 ini 或者 xml 也是可以,不過 YAML 算是很簡單好用的選擇。選擇使用 YAML 的好處就跟選擇用 Python 寫程式差不多,好讀好編輯然後記得要用 space 不要用 tab。

# Never use tabs
all:
-> mail:
-> -> webmaster:  webmaster@example.com

# Use blanks instead
all:
  mail:
    webmaster: webmaster@example.com
# All valid formats
error1: This field is compulsory
error2: '  This field is compulsory  '
error3: 'Don''t leave this field blank'   # Single quotes must be doubled
# Multiline
# Folded style, introduced by >
# Each line break is folded to a space
# Makes YAML more readable
accomplishment: >
  Mark set a major league
  home run record in 1998.

# Literal style, introduced by |
# All line breaks count
# Indentation doesn't appear in the resulting string
stats: |
  65 Home Runs
  0.278 Batting Average
# Shorthand syntax for arrays
players: [ Mark McGwire, Sammy Sosa, Ken Griffey ]

# Expanded syntax for arrays
players:
  - Mark McGwire
  - Sammy Sosa
  - Ken Griffey
# Incorrect syntax, blanks are missing after the colon
mail: {webmaster:webmaster@example.com,contact:contact@example.com}

# Correct shorthand syntax for associative arrays
mail: { webmaster: webmaster@example.com, contact: contact@example.com }

# Expanded syntax for associative arrays
mail:
  webmaster: webmaster@example.com
  contact:   contact@example.com
# Boolean Values
true_values:   [ on, 1, true ]
false_values:  [ off, 0, false ]
# Header
all:
  .general: # Key starts with . is ignored, 完全是方便閱讀而存在
    tax:        19.6
All Rights Reserved Copyright © 2008 Design by StyleShout and Clazh