Блог
05 мая 2010
Контроллер и валидация форм
Мы не сильно ограничивали контроллер и не пытались "изобрести велосипед", когда реализовывали этот слой логики системы. Основная задача контроллера - это дать разработчику возможность реализовать все то, что он не может сделать через API. Взять, например, реализацию API форм.
Формы редко существуют без механизма проверки данных, которые через них передаются. У newt:form есть несколько примитивных встроенных проверок: required у полей question, а также проверка на тип данных, которая реализована совместно с объектным хранилищем. Разработчику должна быть предоставлена возможность накладывать произвольные проверки на все данные под все требования бизнес-логики приложения. Рассмотрим на примере, как это реализовать. Попутно обратите внимание, что подобным методом можно не только проверять, но и модифицировать данные.
XML
XML-файл содержит структуру формы. В нем же мы дополняем эту структуру на этапе постинга (после сабмита формы) данных ошибками, если таковые имеются, чтобы в дальнейшем в XSLT-файле нам проще было их отобразить. Многие проверки можно осуществить уже на этом уровне средствами XPath.
Форма отправки резюме:
Copy Source | Copy HTML
<?xml version="1.0" encoding="utf-8"?>
<root xmlns:newt="http://adv.ru/newt" xmlns:html="http://adv.ru/html">
<!-- Controller for validation and getting region email. -->
<!-- We have to pass handler if we want to run SQL inside the controller. -->
<newt:include match="query[_forward and regionhr_id[not(@value = '')]]">
<newt:action function="getEMail.regionhr" var="regionemail">
<arg value=""/>
<arg value="" />
</newt:action>
</newt:include>
<newt:form lang="ru" id="send-resume"
add-query="regionemail=&filesize=">
<form id="send-resume" action="" method="post" last="yes" enctype="application/x-www-form-urlencoded">
<!-- Process actions only in case of correct query data. -->
<newt:include match="query[_forward and filesize/@value <= 102400 and regionemail[not(@value = '')]]">
<actions hide="yes">
<action type="mail-to" on-error="Cannot send letter">
<param name="from" value="email@example.com"/>
<param name="to" value="" />
<param name="subject" value="Резюме на вакансию"/>
<param name="xslt" value="send-resume"/>
<newt:http-env query="yes" />
</action>
</actions>
</newt:include>
<!-- Inserting some error marks in case of mistakes. -->
<newt:include match="query[_forward and filesize/@value > 102400]">
<error>Приложеный файл слишком велик.</error>
</newt:include>
<newt:include match="query[_forward and regionemail[not(@value = '')]]">
<error>Невозможно отправить e-mail.</error>
</newt:include>
<newt:include match="query[_forward and filesize/@value = '']">
<error>Нет файла, приложите файл резюме.</error>
</newt:include>
<!-- Don't pay attention to this. -->
<question name="regionhr" id="regionhr" type="select">
<label>Регион:</label>
<newt:transform template="transform-base-to-answer">
<newt:base id="regionhr" add-query="get-regionhr"
query-filter="regionhr_id" request="request-career" />
<newt:http-env query="yes" />
</newt:transform>
</question>
<question name="vacancy" id="vacancy" type="text" required="yes"
on-error="Это поле должно быть заполнено">
<label>Название вакансии:</label>
<answer />
</question>
<question name="fio" id="fio" type="text" required="yes"
on-error="Это поле должно быть заполнено">
<label>ФИО:</label>
<answer />
</question>
<question name="phone" id="phone" type="text">
<label>Контактный телефон:</label>
<answer />
</question>
<question name="email" id="email" type="text">
<label>E-mail:</label>
<answer />
</question>
<question name="resumefile" required="yes" id="resumefile" type="file">
<label><span>Приложить</span> резюме (файл не более 100kB):</label>
<answer />
</question>
<!-- CAPTCHA -->
<question name="mcaptcha" id="mcaptcha" type="text" required="yes"
on-error="Введены не верные символы">
<label>Введите символы, изображенные на картинке:</label>
<answer value="" />
</question>
</form>
</newt:form>
</root>
Задачей контроллера является получение e-mail адреса из базы по ID региона, который пришел из формы (попутно можно дополнительные проверки встроить в сам контроллер). Результат кладется в request scope (var="regionemail"), которые через метод add-query затем передается внутрь newt:form:
Copy Source | Copy HTML
add-query="regionemail=&filesize="
Далее, эти данные в форме доступны как обычные данные из query (query/filesize и query/regionemail). Если вам нужно внутри формы подставить часть этих данных в поля, можно воспользоваться возможностями EL, а именно (отправим письмо на адрес, полученный в контроллере):
Copy Source | Copy HTML
<param name="to" value="" />
Задача же проверки данных формы облегчается тем, что нам в примере надо сделать лишь корректную проверку прилагаемого файла-резюме, а для этого у нас уже есть все данные, доступные в специальном объекте query. Поэтому специальный контроллер писать нет необходимости. При помощи и проверок параметров query методами XPath на их корректность мы управляем структурой формы.
Проверка размера файла, добавляем ошибку для отображения:
Copy Source | Copy HTML
<newt:include match="query[_forward and filesize/@value > 102400]">
<error>Приложеный файл слишком велик.</error>
</newt:include>
Обобщаем все проверки, чтобы избежать сохранения формы в случае наличия ошибки:
Copy Source | Copy HTML
<newt:include match="query[_forward and filesize/@value <= 102400 and regionemail[not(@value = '')]]">
<actions>
...
</actions>
</newt:include>
Contoller
Сам вызываемый функционал контроллера может содержать как проверку данных (пришедших через специальный объект query), так и просто выборку. В нашем примере это небольшая функция, написанная на языке Groovy.
Пример передачи данных в контроллер:
Copy Source | Copy HTML
<arg value="" />
getEmail.groovy - выбираем данные для формы:
Copy Source | Copy HTML
// We have to find correct e-mail for resume sending.
def regionhr(dbHandler,id) {
id = new Long(id)
def row = dbHandler.firstRow("select email from regionhr where id=$id");
return (row != null) ? row[0] : '';
}
XSLT
В файле темплейта осуществим простую проверку данных. Мы просто должны помимо прочего обработать все элементы error в нашей форме, выводя ошибку на экран пользователя:
Copy Source | Copy HTML
<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="default.xsl"/>
....
<xsl:template match="form">
<xsl:choose>
<xsl:when test="$query/_forward and (//error or //question[@error])">
<xsl:call-template name="form"></xsl:call-template>
</xsl:when>
<xsl:when test="$query/_forward and not(//error)">
<p>Ваше резюме отправлено в кадровую службу в регионе.</p>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="form"></xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="error">
<div class="red">
<xsl:value-of select="text()" /></div>
</xsl:template>
<xsl:template name="form">
<form method="{@method}" action="{@action}" enctype="{@enctype}" class="{@class}" id="{@id}">
<div class="bgBot">
<div class="bgTop">
<h4>Отправьте свое резюме</h4>
<xsl:apply-templates />
<p><br />
<xsl:call-template name="required-text" /></p>
<xsl:call-template name="submit-button-forward"/>
</div>
</div>
</form>
</xsl:template>
...

Комментарии