Zend_Form | Кирилл Павлюков
Авг 27

Собственно, сабж. :-)

Думается, что через недельку-другую уже будет полноценный релиз. По крайней мере, я на это надеюсь. Нет, даже не так! Я на это УПОВАЮ!!! )))

Что нового в RC3? Да не понятно. По идее, ничего нового тут быть не должно. Но я обнаружил в документации новый элемент Zend_Captcha и соответственно Zend_Form_Element_Captcha. Не уверен, но мне кажется что даже в RC2 этого не было. Хотя, в SVN пишут, что эти фишки были скопированы в ветку релиза 1.6 еще 6 августа. Соответственно, тогда они должны были присутствовать во втором релиз-кандидате. В любом случае, наличие этих элементов – вещь мега-полезная и рульная!

Для тех, кто в танке. Zend Framework – это библиотека PHP-классов, созданная с целью облегчить и ускорить процесс разработки сложных сайтов. Основывается на философии MVC. Больше можно прочитать на официальном сайте. 

ЗЫ. Так задолбали спамеры со своими комментами. Кто знает хороший антиспам для WordPress? Akismet не предлагать )

Share

Автор: Кирилл Павлюков \\ Метки: , , ,

Июл 30

Вы, вероятно уже поняли, что мой любимый визуальный редактор (Rich Text Editor, по-буржуйски) – это SPAW Editor. Я уже неоднократно писал заметки о SPAW. В частности, о том, как подружить SPAW и WordPress, и как сделать плагин Spaw для Smarty. Сегодня пришло время подружить SPAW с не менее замечательной опенсорсной разработкой – Zend Framework.

Принципы интеграции

В общем-то, можно инстанционировать экземпляр SPAW просто в нужном контроллере действий, и передать его экземпляр скрипту вида, который выведет в нужном месте HTML-код. Но! Во-первых, это очень просто, а мы легких путей не ищем. А – главное – во-вторых, тогда потеряется возможность фильтрации и валидации введенных пользователями данных с помощью механизмов Zend_Form. Можно, конечно, самостоятельно произвести над данными необходимые действия. Но не кошерно это, что ли.

Итак, в этой статье я опишу, как сделать редактор SPAW полноценным элементом Zend_Form.

Для начала, разберем, что собой представляет HTML-вывод SPAW. Элементы ввода (для каждой страницы редактора) – это обычные textarea, которые обрамлены килобайтами трудночитаемого HTML и JS. Это довольно примитивная характеристика, но для понимания сути ее достаточно. Следовательно, нам необходимо всего лишь «завернуть» Zend_Form_Element_Textarea в соответствующую оболочку кода. Примерно так мы и поступим – мы напишем свой Zend_Form_Decorator.

Что необходимо

Так как SPAW может оперировать двумя типами объектов (объект редактора, собственно, и объект страницы), нам понадобится 2 соответствующих декоратора: My_Decorator_Spaw и My_Decorator_SpawPage.

При этом, My_Decorator_SpawPage – это вовсе не декоратор в философии Zend_Form, а лишь механизм, с помощью которого мы будем информировать My_Decorator_Spaw о наличие дополнительных страниц. Конечно это не правильно, но другого выхода я не придумал :-( . В свою очередь, такая реализация накладывает одно ограничение на использование: все дополнительные страницы редактора должны быть объявлены ДО объявления основной страницы.

Приступим

Для начала приведу код декоратора My_Decorator_SpawPage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

require_once $_SERVER['DOCUMENT_ROOT'] . '/spaw2/spaw.inc.php';

class My_Decorator_SpawPage extends Zend_Form_Decorator_Abstract
{
    public function render($content)
    {
        $element = $this->getElement();
        $element->setDisableLoadDefaultDecorators(true);
        $page = new SpawEditorPage($element->getName(),
                                   $element->getLabel(),
                                   $element->getValue(),
                                   $element->getAttrib('direction'));
        My_Decorator_Spaw::addPage($page);
        return NULL;
    }
}

Как видите, он создает объект страницы и передает его статическому методу My_Decorator_Spaw::addPage(). Этот метод сохраняет объект в хранилище My_Decorator_Spaw::$pages. Позже, при рендеринге основной части SPAW, все объекты страниц SpawEditorPage будут изъяты из этого хранилища. Конструктору SpawEditorPage передаюся 4 параметра, которые устанавливаются с помощью соответствующих методов объекта Zend_Form_Element_Textarea. Для понимания этого механизма либо обратитесь к мануалу, либо посмотрите раздел «Пример использования» в этой статье.

Ниже приведен код второго декоратора – My_Decorator_Spaw. Комментарии приведены в самом коде.

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<?php

require_once $_SERVER['DOCUMENT_ROOT'] . '/spaw2/spaw.inc.php';

class My_Decorator_Spaw extends Zend_Form_Decorator_Abstract
{
    // объявим статическую переменную $pages - наше хранилище
    static $pages = array();

    // а это - статический метод для добавление объектов
    // страниц редактора в хранилище
    static function addPage($page) {
        array_push(self::$pages,$page);
    }
   
    // метод render() вызывается автоматически в момент генерации
    // кода элемента формы. Здесь и происходит перевоплощение обычной
    // textarea в полноценный визуальный редактор
    public function render($content)
    {
        // получим объект элемента формы для удобства доступа
        // к его свойствам
        $element = $this->getElement();
        // на всякий случай отключим дефолтные декораторы
        // для этого элемента
        $element->setDisableLoadDefaultDecorators(true);
        // создаем объект SpawEditor. В качестве параметров
        // передаются свойства элемента.
        $spaw = new SpawEditor($element->getName(),
                               $element->getValue(),
                               $element->getAttrib('language'),
                               $element->getAttrib('toolbarset'),
                               $element->getAttrib('theme'),
                               $element->getAttrib('width'),
                               $element->getAttrib('height'),
                               $element->getAttrib('stylesheet'),
                               $element->getLabel());
        // А это - небольшая хитрость. Дело в том, что конструктору
        // SpawEditor нельзя передать один параметр - направление письма.
        // Поэтому мы создадим страницу редактора с тем же именем.
        $page = new SpawEditorPage($element->getName(),
                                   $element->getLabel(),
                                   $element->getValue(),
                                   $element->getAttrib('direction'));
        $spaw->addPage($page);
       
        // Дальше следует проверка наличия дополнительных свойств,
        // и их установка для текущего объекта редактора
        if ($element->getAttrib('hideModeStrip')) {
            $spaw->hideModeStrip();
        }
        if ($element->getAttrib('hideStatusBar')) {
            $spaw->hideStatusBar();
        }
        if ($element->getAttrib('setFloatingMode')) {
            $spaw->setFloatingMode();
        }
        if (count($element->getAttrib('setStaticConfigValue')) > 0) {
            foreach ($element->getAttrib('setStaticConfigValue') as $name => $value) {
                SpawConfig::setStaticConfigValue($name,$value);
            }
        }
        if (count($element->getAttrib('setConfigValue')) > 0) {
            foreach ($element->getAttrib('setConfigValue') as $name => $value) {
                $spaw->setConfigValue($name,$value);
            }
        }        
       
        if (is_array($element->getAttrib('toolbar'))) {
            $toolbars = $element->getAttrib('toolbar');
            $element->getAttrib('toolbar');
            foreach ($toolbars as $toolbar) {
                    $spaw->addToolbar($toolbar);
                }
        }
       
        // Настало время извлечь из хранилища объекты дополнительных
        // страниц, и добавить объекту редактора
        foreach (self::$pages as $page) {
            $spaw->addPage($page);
        }
       
        // А дальше следует вывод. Комментарии, надеюсь, излишни.
        $separator = $this->getSeparator();
        $placement = $this->getPlacement();
        $output = $spaw->getHtml();

        switch ($placement) {
            case (self::PREPEND):
                return $output . $separator . $content;
            case (self::APPEND):
            default:
                return $content . $separator . $output;
        }
    }
}

Пример использования

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

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
// Создадим собственно объект формы.
$form = new Zend_Form();
// Теперь нужно создать элемент для дополнительной страницы редактора.
// В этом элементе будет содержаться перевод статьи на иврит.
// Создадим новый экземпляр Zend_Form_Element_Textarea,
// в качестве параметра конструктора передадим имя элемента
// формы - 'article_hebrew'
$page = new Zend_Form_Element_Textarea('article_hebrew');
// Установим для этой страницы Label
$page->setLabel('Hebrew translation');
// Следующие 2 строки применяют к элементу созданный нами
// декоратор My_Decorator_SpawPage
$page->addPrefixPath('My_Decorator', 'My/Decorator/', 'decorator');
$page->setDecorators(array('SpawPage'));
// При необходимости - указываем текущее значение элемента
// $page->setValue('Hebrew text');
// А теперь укажем направление текста - справа на лево.
$page->setOptions(array('direction' => 'rtl'));

// Теперь создадим основную страницу редактора с именем 'article'
$element = new Zend_Form_Element_Textarea('article');
// Установим Label
$element->setLabel('English article');
// Следующие 2 строки применяют к элементу декоратор My_Decorator_Spaw
$element->addPrefixPath('My_Decorator', 'My/Decorator/', 'decorator');
$element->setDecorators(array('Spaw'));
// При необходимости - указываем текущее значение элемента
// $element->setValue('111');
// Метод setOptions() устанавливает массив параметров для элемента.
// Эти параметры будут использованы для настройки объекта редактора.
// Больше информации об этих параметрах можно найти в документации по SPAW.
$element->setOptions(array('direction' => 'ltr',
                           'language' => 'ru',
                           'width' => '600px',
                           'height' => '400px',
                           'toolbar' => array('edit','font'),
                           'theme' => 'spaw2',
                           'stylesheet' => 'cust.css',
                           'hideModeStrip' => TRUE,
                           'setFloatingMode' => FALSE,
                           'hideStatusBar' => TRUE,
                           'setStaticConfigValue' => array('default_height' => '200px',
                                                           'default_width' => '200px'),
                           'setConfigValue' => array('default_height' => '500px',
                                                     'default_width' => '500px')));
// Вначале добавим в форму все дополнительные страницы редактора
$form->addElement($page);
// И лишь затем - основную страницу. Это связано с тем, что
// рендеринг элементов происходит в порядке их добавления.
$form->addElement($element);
// Теперь можно вывести форму или передать ее в скрипт вида либо на обработку.
echo $form->render();

Послесловие

Вероятно, я выбрал не очень «изящный» способ интеграции. Но он наиболее легко реализуем. Если у вас будут другие соображения, как подружить SPAW и Zend Framework, пишите каменты – обсудим!

Share

Автор: Кирилл Павлюков \\ Метки: , , , ,

Апр 18

Анализируя статистику поисковых запросов, по которым вы находите мой блог, я заметил, что много людей ищут примеры работы с Zend_Form. Для меня это показалось несколько странным, так как компонент Zend_Form хорошо документирован в руководстве. Но чего не сделаешь по просьбам читателей :-)

Итак, привожу небольшой реальный пример из приложения, которое я сейчас разрабатываю. Как я уже писал, это будет каталог сайтов, но с несколько расширенными функциями. И как, наверное, в любом сайте, без регистрации пользователей никак не обойтись. Вот из контроллера авторизации и взят этот пример.

Для начала разберемся, что позволяет делать Zend_Form:

  • генерировать формы;
  • валидировать их (проверять введенные пользователем данные);
  • выводить ошибки.

Логично, что одна и та же форма может быть востребована несколько раз. Из этого следует, что код генерации формы можно вынести в отдельный метод для многократного использования. Так мы и поступим, создав действия getRegistrationForm и getLoginForm.

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
private function getLoginForm()
{
    $translate = include(APP_DIR . DIRECTORY_SEPARATOR . 'translation.php');
    $translator = new Zend_Translate_Adapter_Array($translate);

    $form = new Zend_Form();
    $form->setAction('/auth/login')
         ->setMethod('post')
         ->setTranslator($translator);
    $form->addElements(array(
        new Zend_Form_Element_Text('email', array(
            'required' => true,
            'label' => 'Адрес e-mail:',
            'description' => 'Е-меил используется в качестве логина для входа на сайт',
            'validators' => array(
                array('NotEmpty', true),
                'EmailAddress'
                ),
            'decorators' => array(
                array('ViewHelper'),
                array('Errors'),
                array('HtmlTag', array('tag' => 'dd')),
                array('Label', array('tag' => 'dt','requiredSuffix' => ' (*)')),
                array('Description'),
                )
            )),
        new Zend_Form_Element_Password('password',array(
            'required' => true,
            'label' => 'Пароль:',
            'description' => 'Выберите себе пароль',
            'validators' => array(
                array('NotEmpty', true),
                array('StringLength', true, array('min' => 6)),
                'Alnum'
                ),
            'decorators' => array(
                array('ViewHelper'),
                array('Errors'),
                array('HtmlTag', array('tag' => 'dd')),
                array('Label', array('tag' => 'dt','requiredSuffix' => ' (*)')),
                )
            )),
        new Zend_Form_Element_Submit('Войти',array(
            'decorators' => array(
                array('ViewHelper'),
                array('HtmlTag', array('tag' => 'center')),
                )
            ))
        ));
       
    return $form;
}
   
private function getRegistrationForm()
{
    $translate = include(APP_DIR . DIRECTORY_SEPARATOR . 'translation.php');
    $translator = new Zend_Translate_Adapter_Array($translate);
    $form = new Zend_Form();
    $form->setAction('/auth/registration')
         ->setMethod('post')
         ->setTranslator($translator);
    $form->addElements(array(
        new Zend_Form_Element_Text('email', array(
        'required' => true,
            'label' => 'Адрес e-mail:',
            'description' => 'Е-меил используется в качестве логина для входа на сайт',
            'validators' => array(
                array('NotEmpty', true),
                array(new My_Validate_Unique('users','email'), true),
                'EmailAddress'
                ),
            'decorators' => array(
                array('ViewHelper'),
                array('Errors'),
                array('HtmlTag', array('tag' => 'dd')),
                array('Label', array('tag' => 'dt','requiredSuffix' => ' (*)')),
                array('Description'),
                )
            )),
        new Zend_Form_Element_Password('password',array(
            'required' => true,
            'label' => 'Пароль:',
            'description' => 'Выберите себе пароль',
            'validators' => array(
                array('NotEmpty', true),
                array('StringLength', true, array('min' => 6)),
                'Alnum'
                ),
            'decorators' => array(
                array('ViewHelper'),
                array('Errors'),
                array('HtmlTag', array('tag' => 'dd')),
                array('Label', array('tag' => 'dt','requiredSuffix' => ' (*)')),
                )
            )),
        new Zend_Form_Element_Password('password_confirm',array(
            'required' => true,
            'label' => 'Повтор пароля',
            'description' => 'Повторите ввод пароля',
            'validators' => array(
                array(new My_Validate_Password(), true)
                ),
            'decorators' => array(
                array('ViewHelper'),
                array('Errors'),
                array('HtmlTag', array('tag' => 'dd')),
                array('Label', array('tag' => 'dt','requiredSuffix' => ' (*)')),
                )
            )),
        new Zend_Form_Element_Text('name', array(
            'required' => false,
            'label' => 'Ваше имя',
            'validators' => array(
                'Alnum'
                ),
            'decorators' => array(
                array('ViewHelper'),
                array('Errors'),
                array('HtmlTag', array('tag' => 'dd')),
                array('Label', array('tag' => 'dt','requiredSuffix' => ' (*)')),
                )
            )),
        new Zend_Form_Element_Submit('Зарегистрировать',array(
            'decorators' => array(
                array('ViewHelper'),
                array('HtmlTag', array('tag' => 'center')),
                )
            ))
        ));
    return $form;
}

Я оставлю пока этот листинг без комментариев, так как если описывать все в одном посте, то получится целый толмуд. Детально я буду рассматривать процесс создания форм в следующих постах (ориентировочно – на следующей неделе). Пока просто поверьте мне на слово, что этот код работает! (Внизу страницы вы сможете найти ссылку на архив, в котором есть файл контроллера и самописные валидаторы, используемые для проверки приведенных форм).

Итак, формы созданы, теперь нужно их использовать :-)
Посмотрите, как выглядит действие registration:

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
public function registrationAction()
{
    // проверим, переданы скрипту данные по методу POST
    if (!$this->getRequest()->isPost()) {
        // если данные не переданы, просто передадим в скрипт вида форму
        return $this->view->form = $this->getRegistrationForm();
    } else {
        $form = $this->getRegistrationForm();
        // если данные переданы, проверим их на корректность
        if (!$form->isValid($_POST)) {
            // если данные не корректны, возвращаем форму в скрипт вида
            // Zend_Form сам отобразит сообщения об ошибках
            return $this->view->form = $form;
        } else {
            // если данные верны, сохраним их в БД
            /** @var Zend_Db_Adapter_Abstract */        
            $db = Zend_Registry::get('db');
       
            if ($db->insert('users', array(
                'name' => $_POST['name'],
                'email' => $_POST['email'],
                'passwd' => new Zend_Db_Expr("MD5('{$_POST['password']}')"),
                ))) {
                $this->view->success = true;
            }
        }
    }
}

В скрипте вида можно формы можно использовать двумя способами. Во-первых, можно вывести форму целиком (что должно вас устроить в 99% случаев). Для этого нужно лишь добавить в скрипт вида инструкцию

1
<?php echo $this->form; ?>

Во-вторых, можно вывести поэлементно. Для этого в нужном месте вызывается код

1
<?php echo $this->form->element; ?>

, в котором element – это имя соответствующего элемента формы.

Share

Автор: Кирилл Павлюков \\ Метки: , , ,