Zend Framework: аутентификация и регистрация
by Вредный • 20.04.2010 • php, Zend Framework • Комментарии [8]
В сегодняшней заметке речь пойдет о аутентификации и регистрации новых пользователей с помощью базы данных на Zend Framework.
Для начала нам понадобится создать таблицу, пусть называться она будет users, sql- запрос представлен ниже.
1 2 3 4 5 6 7 8 9 | CREATE TABLE users ( id INT(11) NOT NULL AUTO_INCREMENT, firstname VARCHAR(100) DEFAULT NULL, lastname VARCHAR(100) DEFAULT NULL, email VARCHAR(200) NOT NULL, username VARCHAR(100) NOT NULL, password VARCHAR(32) NOT NULL, PRIMARY KEY(id) ); |
Дальше создадим модель в папке application/models/ и назовем ее Users.php. Этот класс будет расширять Zend_Db_Table
1 2 3 4 5 6 | <?php class Users extends Zend_Db_Table { protected $_name = 'users'; // имя таблицы, которую мы будем использовать } ?> |
Для эксперемента нам еще понадобится отдельный контроллер, который называть будет Auth, создать его можно в ZF Tools и просто ручками.
Если вы используете NetBeans в качестве среды разработки, то в бета версии 6.9 есть возможность взаимодействия с ZF Tools прямо из IDE.

Затем создадим 4 метода у этой модели: для входа, регистрации, выхода и страницы, отображающей имя пользователя после успешного входа. Называться которые будет соответсвенно login, signin, logout, home.
Для тех, кто предпочитает создавать все ручками, нужно создать помимо методов в модели, еще и шаблоны для их отображения в папке application/views/scripts/auth/

Создадим форму LoginForm.php, которая будет создавать форму для входа на сайт.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <?php /** * LoginForm */ class LoginForm extends Zend_Form { public function init() { $username = $this->createElement('text', 'username', array( 'label' => 'Username: *', 'required' => TRUE )); $password = $this->createElement('password', 'password', array( 'label' => 'Password', 'required' => TRUE )); $signin = $this->createElement('submit', 'SignIn', array( 'label' => 'Sign In' )); $this->addElements(array( $username, $password, $signin )); } } |
Далее добавим форму для регистрации новых пользователей RegistrationForm.php. Все формы, как вы, наверное, помините, должны располагаться в папке APPLICATION_PATH/forms
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | <?php /** * RegistrationForm */ class RegistrationForm extends Zend_Form { public function init() { $firstname = $this->createElement('text', 'firstname', array( 'label' => 'First Name:', 'required' => FALSE )); $lastname = $this->createElement('text', 'lastname', array( 'label' => 'Last Name', 'required' => FALSE )); $email = $this->createElement('text', 'email', array( 'label' => 'E-mail: *', 'required' => TRUE )); $username = $this->createElement('text', 'username', array( 'label' => 'Username: *', 'required' => TRUE )); $password = $this->createElement('password', 'password', array( 'label' => 'Password: *', 'required' => TRUE )); $confirmPassword = $this->createElement('password', 'confirmPassword', array( 'label' => 'Confirm Password: *', 'required' => TRUE )); $register = $this->createElement('submit', 'Register', array( 'label' => 'Sign Uo' )); $this->addElements(array( $firstname, $lastname, $email, $username, $password, $confirmPassword, $register )); } } |
В этих формах не происходит ровным счетом ничего интересного, кроме создания нескольких элементов и задания им некотрых значений, к примеру, обязательности заполнения и метки (label).
Давайте создадим наш контроллер, методы которого будут отвечать за вход и регистрацию пользователей, а также их выход. Сначала приведу весь текст, дальше попытаюсь подробнее заострить внимание на важных моментах.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | <?php class AuthController extends Zend_Controller_Action { public function init() { require_once 'Users.php'; require_once 'LoginForm.php'; require_once 'RegistrationForm.php'; } public function indexAction() { $this->_redirect('auth/login'); } public function loginAction() { $users = new Users(); $form = new LoginForm(); $this->view->form = $form; if ($this->getRequest()->isPost()) { if ($form->isValid($_POST)) { $data = $form->getValues(); $auth = Zend_Auth::getInstance(); $authAdapter = new Zend_Auth_Adapter_DbTable($users->getAdapter(), 'users'); $authAdapter->setIdentityColumn('username') ->setCredentialColumn('password') ->setIdentity($data['username']) ->setCredential($data['password']); $result = $auth->authenticate($authAdapter); if ($result->isValid()) { $storage = new Zend_Auth_Storage_Session(); $storage->write($authAdapter->getResultRowObject()); $this->_redirect('auth/home'); } else { $this->view->errorMessage = 'Invalid username or password. Please try again'; } } } } public function signupAction() { $users = new Users(); $form = new RegistrationForm(); $this->view->form = $form; if ($this->getRequest()->isPost()) { if ($form->isValid($_POST)) { $data = $form->getValues(); if ($data['password'] != $data['confirmPassword']) { $this->view->errorMessage = 'Password and confirm password dont \' match'; return; } if ($users->isUnique($data['username'])) { $this->view->errorMessage = 'Name already taken. Please choose another one.'; return; } unset($data['confirmPassword']); $users->insert($data); $this->_redirect('auth/login'); } } } public function logoutAction() { $storage = new Zend_Auth_Storage_Session(); $storage->clear(); $this->_redirect('auth/login'); } public function homeAction() { $storage = new Zend_Auth_Storage_Session(); $data = $storage->read(); if (!$data) { $this->_redirect('auth/login'); } $this->view->username = $data->username; } } |
Метод homeAction(): cперва мы создаем экземпляр класса Zend_Auth_Storage_Session, который будет хранить информацию о пользователе, который пытается войти най сайт или уже вошедшего. Объект данного класса возвращает непустую запись, если пользователь вошел на сайт, null в противном случае. Если же null, перенаправляем пользователя на страницу входа на сайт для ввода имени пользователя и пароля. Дальше в файле views/scripts/auth/home.html напишем следующее:
1 2 3 | Welcome <?php echo $this->username;?>, <br /> This is your home page<br /> <a href="<?php echo $this->url(array('controller' => 'auth', 'action' => 'logout'));?>">click here to logout</a> |
Чтобы пользователь, миновавший вход на сайт, увидел свое имя и ссылку, позволяющую выйти (разлогиниться не очень хорошее слово).
Метод loginAction(): в первых 2х строчках мы создаем экземпляры нашей модели Users и формы для входа на сайт. (из-за неправильного сначала выбранного способа именования классов приходится подключать их в инициализационном методе, это мой промах и в последующих заметках я постараюсь исправить это). Третьей строкой мы ассоциируем нашу форму с переменной вида, которую отобразим в шаблоне.
Дальше идет непосредстенно бизнес-логика. Сначала мы проверяем, чтобы отосланные данные были POST запросом, далее проверяем их правильность. Если оба этих условия соблюдены, мы получаем значения формы
1 | $data = $form->getValues(); |
Создаем экземпляр класса Zend_Auth и аутентификационный адаптер $authAdapter.
Я использую адаптер, который непосредственно связан с базой данных, предоставляемый Zend’ом. Дальще мы указываем какие поля являются именем пользователя, а какие паролем.
1 2 3 4 | $authAdapter->setIdentityColumn('username') ->setCredentialColumn('password') ->setIdentity($data['username']) ->setCredential($data['password']); |
И пытаемся призвести аутентификацию по присланным из формы данным, метод ->authenticate()
Если результат аутентификации валиден ->isValid() (мы нашли хотя запись в базе данных), сохраняем результат в сессии и переадресуем пользователя на домашнюю страницу. Если аутентификация не прошла, выводим сообщения об ошибке.
Шаблон для этого метода. application/views/scripts/login.phtml
1 2 3 4 5 6 7 8 9 | <?php if (isset($this->errorMessage)) { echo $this->errorMessage; } ?> <?php echo $this->form; ?>hf <a href="<?php echo $this->url(array('controller' => 'auth', 'action' => 'signup'));?>"> New users click here? </a> |
Метод signinAction() делаем примерно тоже самое, за исключением вывода другой формы, проверки паролей (одинаковы ли они?) и записи в базу данных данных нового пользователя, если не возникло ошибок. В этом методе упоминается isUnique() метод, который сейчас мы допишем в Users.php, возвращающий true, если заданное в параметре имя в базе данных существует и false в противном случае. Что предотвратит создание двух пользоватлей с одинаковыми именами. Файл application/models/Users.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php class Users extends Zend_Db_Table { protected $_name = 'users'; public function isUnique($username) { $select = $this->_db->select()->from($this->_name)->where('username = ?', $username); $result = $this->getAdapter()->fetchOne($select); if ($result) { return TRUE; } else { return FALSE; } } } |
Начало серьезному проекту положено, хоть и остаются еще неправильности в коде, но с опытом я, надеюсь, от них избавлюсь. Удачного вам программирования! =)
Было бы неплохо выложить структуру файлов\каталогов.
И у меня вопрос. Как получить доступ к БД MySQL, и вытащить из уже созданной таблицы поля, средствами зенд фреймворк.(у меня денвер)
спасибо большое за интересную статью. Мне она очень помогла
Дмитрий, ставьте Zend Server, зачем вам денвер?
Вытащить через Zend_Db_Table
Thanks a lot!!
I’ve found this note very useful!
Спасибо, полезная статейка.
Но я бы сделал парочку изменений:
1. БД-адаптер лучше поместить в реестр, чтобы не создавать каждый раз объект Users только для того, чтобы получить этот адаптер ( loginAction ).
2. В signupAction создание объекта Users поместить после валидации поста ( if ($form->isValid($_POST)) ), так как нет необходимости при каждой попытке регистрации/входа создавать этот объект.
Рад, что вам она пригодилась, на счет второго пункта предложенных вами изменений, я полностью согласен. В ответ на первый пункт — меня что-то побудило создать именно модель, может функционал, не рассмотренный в заметке, уж не упомнить — но в целом согласен с вами.
Спасибо за развернутый комментарий.
И если уж вообще быть занудой то,
А как можно добавить пользователю его личную страницу, где он бы отразил информацию о себе, после того как прошел регистрацию…
Извините если вопрос не по теме, очень хочется узнать)
создаете контроллер к примеру User и какой-нибудь экшн, к примеру user/profile и в зависимости от id, сохраненного в сессии, отображаете данные пользователя, это если в двух словах