Инструменты пользователя

Инструменты сайта


limb3_2007_3:ru:tutorials:shop:step4-1

Шаг4.1 Управление списком пользователей

Создание класса User

Реализация пользователей должна отвечать следующим требованиям:

  • В системе не должно быть пользователей с одинаковыми логинами и email-адресами.
  • Пароли пользователей не должны храниться в базе данных в открытом виде.

Правило валидации UserUniqueFieldRule

При помощи класса UserUniqueFieldRule мы будем проверять наличие одного уникального пользователя со значением какого-либо поля. Это правило валидации потом будет использоваться в классе User.

Файл shop/src/validation/UserUniqueFieldRule.class.php:

<?php
lmb_require('limb/validation/src/rule/lmbSingleFieldRule.class.php');
lmb_require('limb/dbal/src/criteria/lmbSQLFieldCriteria.class.php');
lmb_require('src/model/User.class.php');
 
class UserUniqueFieldRule extends lmbSingleFieldRule
{
  protected $current_user;
 
  function __construct($field, $current_user)
  {
    $this->current_user = $current_user;
    parent :: __construct($field);
  }
 
  function check($value)
  {
    $user = new User();
    $records = $user->findAllRecords(new lmbSQLFieldCriteria($this->field_name, $value));
 
    if($records->count() && ($this->current_user->isNew() || $this->_sameFieldUserRecordExists($records)))
      $this->error('User with the same value of {Field} already exists');
  }
 
  function _sameFieldUserRecordExists($records)
  {
    foreach($records as $record)
    {
      if($record->get('id') != $this->current_user->getId())
        return true;
    }
    return false;
  }
}
?> 

Класс UserUniqueFieldRule получает в конструкторе название поля, которое должно быть уникальным, и ссылку на текущего пользователя. Текущий пользователь понадобится нам при редактировании.

Класс lmbSingleFieldRule является базовым для правил валидации одного поля. Файл класса можно найти по пути limb/validation/src/rule/lmbSingleFieldRule.class.php Дочерние классы должны перекрывать метод check($value), где $value - значение поля, которое необходимо проверить. Следует отметить, что check($value) не вызывается, если $value не содержит значимого значения. Поэтому совместно с этим правилом (его дочерними) рекомендуется использовать также правило lmbRequiredFieldRule. Об этом ты также упомянем чуть ниже при описании кода класса User.

При помощи find-метода класса lmbActiveRecord делается запрос к базе данных. В качестве второго параметра вместо $params можно передавать объект критерии, которая будет накладываться на выборку. Критерия - это объектная форма условия. В нашем случае использовался класс lmbSQLFieldCriteria, который принимает в конструкторе название поля и значение (по-умолчанию для этой пары формируется условие равенства). Использование критериев позволяет не думать о экранировании данных в SQL-запросах. Вы можете подробнее ознакомиться с классами Criteria, однако это необязательно для понимания данного примера.

Метод lmbSingleFieldRule :: error($error_string, $values = array()) добавляет ошибку в список ошибок валидации. Выражения вида {Field} будут заменены в процессе работы на реальные имена полей формы, с которой производится создание/редактирование пользователя.

Код класса User

Теперь можно написать код класса User.

Файл shop/src/model/User.class.php:

<?php
class User extends lmbActiveRecord
{
  protected $_default_sort_params = array('name' => 'ASC');
 
  protected $password;
 
  protected function _createValidator()
  {
    $validator = new lmbValidator();
    $validator->addRequiredRule('login');
 
    $validator->addRequiredRule('email');
    $validator->addRequiredRule('name');
 
    lmb_require('src/validation/UserUniqueFieldRule.class.php');
    $validator->addRule(new UserUniqueFieldRule('login', $this));
    $validator->addRule(new UserUniqueFieldRule('email', $this));
 
    lmb_require('limb/validation/src/rule/lmbEmailRule.class.php');
    $validator->addRule(new lmbEmailRule('email'));
    return $validator;
  }
 
  protected function _onBeforeSave()
  {
    $this->_generatePassword();
  }
 
  protected function _generatePassword()
  {
    if($this->password)
      $this->setHashedPassword(self :: cryptPassword($this->password));
  }
 
  static function cryptPassword($password)
  {
    return md5($password);
  }
}
?>

Создаваемый в методе _createValidator() валидатор проверяет, что поля email и login должны быть заполнены и иметь уникальные значения. Последнее обеспечивается за счет использования правила UserUniqueFieldRule. Правило lmbEmailRule удостоверяется, что введенное в поле значение является электронным адресом.

Обратите внимание на метод _onBeforeSave(). Это так называемый метод расширения класса lmbActiveRecord, который дочерние классы могут использовать чтобы выполнять необходимые действия, например, перед сохранением новой записи, перед обновлением существующей, после сохранения и т.д. Список методов расширения класса lmbActiveRecord есть в разделе "Расширение поведения базового класса lmbActiveRecord".

В нашем случае мы использовали этот метод, чтобы шифровать поле password и формировать значение поля hashed_password. Таким образом мы будем хранить только хешированный пароль, а обновление хешированного пароля будет происходить только, если в поле password есть новое значение.

Создание шаблонов и контроллера для управления пользователями

Надеемся, что это часть не составит для Вас никакого труда. Новые для вас моменты должны быть в реализации действий по созданию нового пользователя и смене пароля в контроллере AdminUserController. Там мы будем использовать дополнительный валидатор для проверки полей с паролями. Получится, что в итоге данные с одной формы будут валидироваться дважды - один раз в контроллере, другой раз в ActiveRecord-е.

Шаблоны

Файл shop/template/admin_user/create.html:

<core:set title='Create user'/>
<core:wrap file="admin_page.html" in="content">
<form id='user_form' name='user_form' method='post' runat='server'>
 
   <core:include file="form_errors.html" />
 
   <div class="field">
   <label for="password">Password:</label>
   <input name="password" type="password" title="Password" class="input"/>
   </div>
 
   <div class="field">
   <label for="repeat_password">Repeat password:</label>
   <input name="repeat_password" type="password" title="Repeat password" class="input"/>
   </div>
 
    <core:include file='admin_user/form_fields.html'/>
 
   <input name='create' type='submit' value='Create' runat='client'/>
</form>
</core:wrap>

Файл shop/template/admin_user/edit.html:

<core:set title='Edit user'/>
<core:wrap file="admin_page.html" in="content">
<form id='user_form' name='user_form' method='post' runat='server'>
 
   <core:include file="form_errors.html" />
 
   <core:include file='admin_user/form_fields.html'/>
 
   <input name='edit' type='submit' value='Edit' runat='client'/>
</form>
</core:wrap>

Файл shop/template/admin_user/form_fields.html:

<div class="field">
<label for="login">Login:</label>
<input name="login" type="text" title="Login" class="input"/>
</div>
 
<div class="field">
<label for="name">Name:</label>
<input name="name" type="text" title="Name" class="input"/>
</div>
 
<div class="field">
<label for="email">Email:</label>
<input name="email" type="text" title="Email" class="input"/>
</div>
 
<div class="field">
<label for="email">Is admin:</label>
<js_checkbox id='is_admin' name='is_admin' class="input"/>
</div>
 
<div class="hr"></div>

Файл shop/templates/admin_user/display.html:

<core:set title='Users'/>
<core:wrap file="admin_page.html" in="content">
 
<route_url params='action:create'>Create user</route_url>
 
<active_record:fetch using='src/model/User' target="users" navigator='pager' />
 
<core:include file='pager.html'/>
 
<list:list id='users'>
<table  cellspacing=0 cellpadding=0 class='list'>
<thead>
<tr>
  <th>Name</th>
  <th>Login</th>
  <th>E-mail</th>
  <th>Actions</th>
</tr>
</thead>
<tbody>
<list:item>
<tr class='{$Parity}'>
  <td>{$name}</td>
  <td>{$login}</td>
  <td><a href='mailto:{$email}'>{$email}</a></td>
  <td>
    <route_url params='action:change_password,id:{$id}'>Change password</route_url>
    <route_url params='action:edit,id:{$id}'>Edit</route_url>
    <route_url params='action:delete,id:{$id}'>Delete</route_url>
  </td>
</tr>
</list:item>
</tbody>
</table>
</list:list>
</core:wrap>

Контроллер AdminUserController

При реализации контроллера AdminUserController мы также создадим общий метод _performCreateOrEdit(), как и в AdminProductController, который будет отвечать за создание и редактирование учетных записей пользователей.

Дополнительно вы введем проверку при создании пользователя, что значения полей password и repeat_password должны совпадать. Так мы покажем как можно валидировать данные, пришедшие с форм, прямо в контроллере.

Файл shop/src/controller/AdminUserController.class.php:

<?php
lmb_require('src/model/User.class.php');
 
class AdminUserController extends lmbController
{
  function doCreate()
  {
    $this->_performCreateOrEdit();
  }
 
  function doEdit()
  {
    $this->_performCreateOrEdit();
  }
 
  protected function _performCreateOrEdit()
  {
    if(!$id = $this->request->getInteger('id'))
      $id = null;
 
    $item = new User($id);
    $this->useForm('user_form');
    $this->setFormDatasource($item);
 
    if($this->request->hasPost())
    {
      $item->import($this->request);
 
      if($this->request->get('create'))
        $this->_validatePasswordField();
 
      if($item->trySave($this->error_list))
        $this->redirect();
    }
  }
 
  function doDelete()
  {
    $item = new User($this->request->getInteger('id'));
    $item->destroy();
    $this->redirect();
  }
 
  function _validatePasswordField()
  {
    $this->validator->addRequiredRule('password');
    $this->validator->addRequiredRule('repeat_password');
    lmb_require('limb/validation/src/rule/lmbMatchRule.class.php');
    $this->validator->addRule(new lmbMatchRule('password', 'repeat_password'));
    $this->validator->validate($this->request);
  }
}
?>

Рассмотрим метод _validatePasswordField(). Класс lmbController по-умолчанию содержит объект валидатора $validator, которому передали список ошибок $error_list. В методе мы добавили в этот валидатор три правила:

  • lmbRequiredRule для поля repeat_password,
  • lmbRequiredRule для поля password и
  • lmbMatchRule для полей password и repeat_password. lmbMatchRule удостоверяется, что значения двух полей совпадают.

Метод lmbValidator :: validate($datasource) проверяет переданный контейнер данных $datasource при помощи ранее добавленных в него правил. В нашем случае мы отдали на проверку объект Запроса $request.

Теперь можете попробовать создать уникальных пользователей, пользователей с дублирующимися логинами и т.д.

Смена пароля пользователей

Добавим также действие по смене пароля, скажем change_password. Текущий зарегистированный администратор сможет легко менять пароли пользователей. Для этого нам необходимо будет добавить новый метод doChangePassword() в AdminUserController и новый шаблон change_password.html:

Файл shop/template/admin_user/change_password.html:

<core:set title='Change user password'/>
<core:wrap file='admin_page.html' in='content'>
<form method="post" name="user_form" id="user_form" runat="server">
 
  <core:include file='form_errors.html'/>
 
  <div class='field'>
  <label for="password">New password:</label>
  <input name="password" type="password" title="New password" class="input"/>
  </div>
 
  <div class='field'>
  <label for="repeat_password">Repeat password:</label>
  <input name="repeat_password" type="password" title="Repeat password" class="input"/>
  </div>
 
  <input type="submit" name="submitted" value="Change password"/>
 
</form>
</core:wrap>

Изменения в файле shop/src/controller/AdminUserController.html:

class AdminUserController extends lmbController
{
  [...]
  function doChangePassword()
  {
    if(!$this->request->hasPost())
      return;
 
    $this->useForm('user_form');
    $this->setFormDatasource($this->request);
 
    $this->_validatePasswordField();
 
    if(!$this->error_list->isValid())
      return;
 
    $user = new User($this->request->getInteger('id'));
    $user->setPassword($this->request->get('password'));
 
    if($user->trySave($this->error_list))
      $this->redirect();
  }
  [...]
}

Здесь мы также использовали метод _validatePasswordField() для проверки полей для ввода паролей.

Далее

Мы сделали модуль по управлению пользователей. Мы рекомендуем вам при помощи этой новой функциональности добавить себе пользователя с установленных флагом «Is Admin» и приступить к следующему шагу: "Шаг4.2 Аутентификация пользователей".

Обсуждение

Ваш комментарий. Вики-синтаксис разрешён:
   ___   _____   __ __   _  __  __  __
  / _ | / ___/  / //_/  | |/_/ / / / /
 / __ |/ /__   / ,<    _>  <  / /_/ / 
/_/ |_|\___/  /_/|_|  /_/|_|  \____/
 
limb3_2007_3/ru/tutorials/shop/step4-1.txt · Последние изменения: 2010/11/10 10:02 (внешнее изменение)