Мой сайт — моя визитная карточка

Создание формы регистрации и авторизации на PHP

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

В одной из своих статей я немного уже затронул тему построения формы. Но там речь шла не о самом написании кода html-формы на PHP, а о реализации алгоритма авторизации и регистрации пользователей.

Сейчас же мы поговорим не просто о том, как с помощью PHP упростить для себя «написание» формы, но и создать инструмент для многократного применения, да ещё и не только конкретно в нашем обсуждаемом случае.

Как мы обычно делаем, например, форму обратной связи себе на сайт? Примерно так...


Зарегистрироваться


И вот так выглядел бы html-код этой формы авторизации на сайте.

<div id="form_wrapper">
	<form action="" method="post" name="userAuthForm" onsubmit="return false;" autocomplete="no">
		<div class="row">
			<label for="login">Логин пользователя:</label>
			<span class="table-row"></span>
			<input type="text" name="login" id="login" placeholder="Введите логин пользователя" />
		</div>
		<div class="row">
			<label for="password">Пароль пользователя:</label>
			<span class="table-row"></span>
			<input type="password" name="password" id="password" placeholder="Введите пароль пользователя" />
		</div>
		<div class="row">
			<label for="saveAuth">Не выходить из системы:
				<input type="checkbox" name="saveAuth" id="saveAuth" checked />
			</label>
		</div>
		<div class="row">
			<input type="submit" value="Войти" name="btn-submit" id="btn-submit" />
		</div>
	</form>
	<p><a href="#">Зарегистрироваться</a></p>
</div>

А теперь давайте представим, что пользователь не зарегистрирован, и потому ему перед авторизацией потребуется заполнить данные формы регистрации на нашем сайте. Форма могла бы выглядеть так.


Вернуться к авторизации


У нас добавились два текстовых поля: одно для повторного ввода пароля, второе для адреса электронной почты пользователя. Наш флажок теперь подпишет пользователя на рассылку, а кнопок стало две. HTML-код такой формы регистрации пользователя мог бы выглядеть так.

<div class="form_wrapper">
	<form action="" method="post" name="userRegForm" onsubmit="return false;" autocomplete="no">
		<div class="row">
			<label for="login">Логин пользователя:</label>
			<span class="table-row"></span>
			<input type="text" name="login" id="login" placeholder="Введите логин пользователя" />
		</div>
		<div class="row">
			<label for="password">Пароль пользователя:</label>
			<span class="table-row"></span>
			<input type="password" name="password" id="password" placeholder="Введите пароль пользователя" />
		</div>
		<div class="row">
			<label for="passwordApproove">Пароль пользователя:</label>
			<span class="table-row"></span>
			<input type="password" name="passwordApproove" id="passwordApproove" placeholder="Повторите ввод пароля" />
		</div>
		<div class="row">
			<label for="email">Электропочта:</label>
			<span class="table-row"></span>
			<input type="text" name="email" id="email" placeholder="Адрес вашей электропочты" />
		</div>
		<div class="row">
			<label for="signNews">Подписаться на рассылку:
				<input type="checkbox" name="saveAuth" id="saveAuth" checked />
			</label>
		</div>
		<div class="row">
			<input type="submit" value="Зарегистрироваться" name="btn-submit" id="btn-submit" />
		</div>
		<div class="row">
			<input type="reset" value="Сбросить" name="btn-reset" id="btn-reser" />
		</div>
	</form>
	<p><a href="#auth">Вернуться к авторизации</a></p>
</div>

Ну, добавилось и добавилось... скажете вы... что такого? Ничего. А если после регистрации вам нужно будет попросить пользователя заполнить форму с подробной информацией о себе? Приведенные примеры форм регистрации и авторизации пользователя не имеют полей для сообщений об ошибках, полей с подсказками. Нет обозначений обязательности заполнений полей.

Другими словами, что делать, когда появляется необходимость создать большое количество однотипных или идентичных полей? Писать всё руками? Ну, может быть. А если потом потребуется добавить всем полям атрибут... data-parent_id="", например. Поиск с заменой не поможет, так как значение атрибута, допустим, должно быть у каждого поля своим.

Привычное для многих использование PHP не даст сразу верный ответ на поставленный вопрос. А ответ прост: написать собственный класс для построения формы обратной связи, формы регистрации и авторизации пользователей и множества других веб-форм.

Если такой класс объявлен в системе, написание вышеупомянутой формы авторизации могло бы выглядеть так.

<?php
// Создаем экземпляр объекта класса, отвечающего за построение формы
// Вызываем статический метод instance() класса Core_Form
$oCore_Form = Core_Form::instance()
	
	// Атрибуту name тега <form> устанавливаем значение authUser
	->name( 'authUser' )
	
	// Устанавливаем значение атрибута id формы
	->id( 'authUser' )
	
	// Значение javascript-атриуба onsubmit для тега <form>
	->onsubmit( 'return false;' )
	
	// Значение атрибута для автозаполнения полей формы
	->autocomplete( 'no' )
	
	// Добавляем поле формы, вызвав метод add() класса Core_Form
	->add(
		
		// Вызываем статический метод класса createField() Core_Form 
		// В качестве аргумента передаем этому методу значение типа поля
		// Это значение хранится внутри класса Core_Form в константе
		// Чтобы не ошибиться и не опечататься, передаем это значение таким образом
		Core_Form::createField( Core_Form::FIELD_INPUT )
		
			// Атрибут type тега <input>
			->type( 'text' )
			
			// Атрибут id
			->id( 'login' )
			
			// Атрибут name
			->name( 'login' )
			
			// Атрибут placeholder
			->placeholder( 'Введите логин пользователя' )
			
			// Заголовок поля в теге <label>
			->label( 'Логин пользователя:' )
			
			// Текст для поля подсказки 
			->instr( 'Логин может содержать только буквы и цифры латинского алфавита' )
			
			// Поле будет обязательным для заполнения
			->requireField( TRUE )
			
			// С этим полем пока что всё, переходим к следующему
	)
	
	// Действуем аналогично, теперь у нас будет поле <input type="password" />
	->add(
		Core_Form::createField( Core_Form::FIELD_INPUT )
			->type( 'password' )
			->id( 'password' )
			->name( 'password' )
			->placeholder( 'Введите пароль пользователя' )
			->label( 'Пароль пользователя:' )
			->instr( 'Пароль может содержать только буквы и цифры латинского алфавита' )
			->requireField( TRUE )
	)
	
	// Добавляем поле <input type="checkbox" />
	->add(
		Core_Form::createField( Core_Form::FIELD_INPUT )
			->type( 'checkbox' )
			->id( 'saveAuth' )
			->name( 'saveAuth' )
			->label( 'Не выходить из системы:' )
			->checked( TRUE )
	)
	
	// Добавляем кнопку отправки формы на обработку
	->add(
		Core_Form::createField( Core_Form::FIELD_INPUT )
			->type( 'submit' )
			->id( 'btn-submit' )
			->name( 'btn-submit' )
			->value( 'Войти' )
	);

/**
 * Данные для построения формы собраны, готовы.
 *
 * За отображение формы на экране отвечать будет класс Core_Form_Show
 * Вызываем конструктор этого класса, передав ему в виде аргумента экземпляр 
 * объекта нашей формы
 *
 */
$oCore_Form_Show = new Core_Form_Show( $oCore_Form );

/**
 *  Дальнейшая обработка формы происходит по следующему алгоритму.
 *  
 *  1. Вызывается метод show() класса Core_Form_Show. Этот метод сформирует промежуточный XML-документ, 
 *     в который поместит всю информацию о форме, её атрибутах и полях.
 *  2. XML-документ будет передан XSLT-процессору.
 *  3. XSLT-процессор посредством обработки XSL-шаблона представит нам форму в таком виде, в каком мы захотим.
 *  4. Имя XSL-шаблона указываем вызвав метод byXSLName(), передав имя шаблона в виде аргумента.
 *  
 */
$oCore_Form_Show->show(
	Core_Xsltprocessor::instance()
		->byXSLName( '1' ));
?>

Этот код позволил бы построить веб-форму следующего вида...


*
Логин может содержать только буквы и цифры латинского алфавита
*
Пароль может содержать только буквы и цифры латинского алфавита

Это был представлен уже пользовательский код для сборки и отображения формы. В комментариях было упомянуто, что информация о форме собирается в промежуточный XML-документ, содержимое которого представлено ниже.

<?xml version="1.0" encoding="utf-8"?>
<document>
  <form_wrapper id="form_wrapper">
    <form>
      <fields>
        <field>
          <label for="login">Логин пользователя:</label>
          <input>
            <field_options>
              <id>login</id>
              <name>login</name>
              <placeholder>Введите логин пользователя</placeholder>
              <type>text</type>
              <required>1</required>
            </field_options>
            <instr>Логин может содержать только буквы и цифры латинского алфавита</instr>
          </input>
        </field>
        <field>
          <label for="password">Пароль пользователя:</label>
          <input>
            <field_options>
              <id>password</id>
              <name>password</name>
              <placeholder>Введите пароль пользователя</placeholder>
              <type>password</type>
              <required>1</required>
            </field_options>
            <instr>Пароль может содержать только буквы и цифры латинского алфавита</instr>
          </input>
        </field>
        <field>
          <checkbox_group id="0">
            <entity>
              <label for="saveAuth">Не выходить из системы:</label>
              <field_options>
                <id>saveAuth</id>
                <name>saveAuth</name>
                <type>checkbox</type>
                <checked>1</checked>
              </field_options>
            </entity>
            <input/>
          </checkbox_group>
        </field>
        <field>
          <input>
            <field_options>
              <id>btn-submit</id>
              <name>btn-submit</name>
              <value>Войти</value>
              <type>submit</type>
            </field_options>
          </input>
        </field>
      </fields>
      <form_options>
        <onsubmit>return false;</onsubmit>
        <autocomplete>no</autocomplete>
        <method>post</method>
        <id>authUser</id>
        <name>authUser</name>
      </form_options>
    </form>
  </form_wrapper>
</document>

Этот документ был передан XSLT-процессору, который обработал его так, как было задано в XSL-шаблоне/ Давайте посмотрим на этот шаблон.

<?xml version = '1.0' encoding = 'utf-8' ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl" xsl:extension-element-prefixes="php">
	<xsl:output method="xml" indent="yes" encoding="utf-8" />
	<xsl:namespace-alias stylesheet-prefix="php" result-prefix="xsl" />
	
	<!-- Обработка корневого узла XML-документа -->
	<xsl:template match="/document">
		
		<!-- Если в документе есть объект формы -->
		<xsl:if test="count(//form)">
			
			<!-- Если у формы есть узел-обертка -->
			<xsl:if test="count(./form_wrapper)">
				
				<!-- Вызвать шаблон для узла-обертки -->
				<xsl:apply-templates select="./form_wrapper" />
			
			</xsl:if>
			
		</xsl:if>
		
	</xsl:template><!-- Обработка корневого узла XML-документа -->
	
	<!-- Шаблон обработки узла-обертки для веб-формы -->
	<xsl:template match="form_wrapper">
		
		<!-- Создаем контейнер, помещаем внутрь его нашу форму -->
		<!-- Элементу DIV добавляем атрибут ID узла-обертки -->
		<div id="{@id}">
			
			<!-- Создаем тег формы -->
			<form>
				
				<!-- Если у формы есть свои свойства или атрибуты -->
				<xsl:if test="count(./form/form_options)">
					
					<!-- Для каждого из них -->
					<xsl:for-each select="./form/form_options/child::node()">
						
						<!-- Сохранить в переменной имя атрибута -->
						<xsl:variable name="attrName" select="name()" />
						
						<!-- Создать атрибут -->
						<xsl:attribute name="{$attrName}">
							
							<!-- Присвоить ему значение -->
							<xsl:value-of select="." disable-output-escaping="yes" />
							
						</xsl:attribute>
						
					</xsl:for-each>
					
				</xsl:if><!-- Если у формы есть свои свойства или атрибуты -->
				
				<!-- Если у формы есть поля -->
				<xsl:if test="count(./form//field)">
					
					<!-- Вызвать шаблон обработки полей формы -->
					<xsl:apply-templates select="./form/fields" />
					
				</xsl:if>
				
			</form>
		
		</div><!-- Контейнер узла-обертки для формы -->
		
	</xsl:template><!-- Шаблон обработки узла-обертки для веб-формы -->
	
	<!-- Шаблон обработки полей формы -->
	<xsl:template match="fields">
		
		<!-- Вызываем шаблон обработки поля формы -->
		<xsl:apply-templates select="field"/>
		
	</xsl:template><!-- Шаблон обработки полей формы -->
	
	<!-- Шаблон обработки поля формы -->
	<xsl:template match="field">
		
		<!-- Сохранить в переменной атрибут required, если он есть -->
		<xsl:variable name="required" select=".//child::field_options/required" />
		
		<!-- Контейнер для всего, что так или иначе относится к понятию одного экземпляра поля формы -->
		<div class="row">
			
			<!-- Создать переменную для хранения значения поля подсказки к полю формы -->
			<xsl:variable name="instr">
				
				<xsl:choose>
					
					<!-- Если у поля есть узел подсказки -->
					<xsl:when test="count(./node()/instr)">
						
						<!-- Сохранить в переменной текст этого узла -->
						<xsl:value-of select="./node()/instr" disable-output-escaping="yes" />
					
					</xsl:when>
					
					<!-- Иначе сохранить строку null -->
					<xsl:otherwise>
					
						<xsl:text>null</xsl:text>
					
					</xsl:otherwise>
				
				</xsl:choose>
				
			</xsl:variable><!-- Создать переменную для хранения значения поля подсказки к полю формы -->
			
			<!-- Аналогичным образом действуем с полем сообщения об ошибке -->
			<!-- Создать переменную для хранения значения поля сообщения об ошибке к полю формы -->
			<xsl:variable name="error">
				
				<xsl:choose>
					
					<!-- Если у поля есть узел сообщения об ошибке --> 
					<xsl:when test="count(./node()/error)">
						
						<!-- Сохранить в переменной текст этого узла -->
						<xsl:value-of select="./node()/error" disable-output-escaping="yes" />
					
					</xsl:when>
					
					<!-- Иначе сохранить строку null -->
					<xsl:otherwise>
					
						<xsl:text>null</xsl:text>
					
					</xsl:otherwise>
				
				</xsl:choose>
				
			</xsl:variable><!-- Создать переменную для хранения значения поля сообщения об ошибке к полю формы -->
			
			<!-- Обработка полей формы -->
			<xsl:choose>
				
				<!-- Если в текущем узле нет груп полей <input type="radio" /> или <input type="checkbox" /> -->
				<xsl:when test="count(radio_group) = 0 and count(checkbox_group) = 0">
					
					<!-- Если у узла поля есть заголовок -->
					<xsl:if test="count(child::label)">
						
						<div class="caption">
							
							<!-- Сохранить в переменной значение атрибута for тега <label> -->
							<xsl:variable name="for" select="./label/@for" />
							
							<!-- Формируем код тега <label> -->
							<label for="{$for}"><xsl:value-of select="node()" disable-output-escaping="yes" /></label>
							
							<!-- Если поле обязательное для заполнения -->
							<xsl:if test="$required = 1">
								
								<!-- Добавить соответствующий символ -->
								<span class="required-field">*</span>
							
							</xsl:if>
							
						</div>
						
					</xsl:if><!-- Если у узла поля есть заголовок -->
					
					<!-- В зависимости от типа поля вызывается соответствующий шаблон -->
					<xsl:choose>
						
						<!-- Если это поле input -->
						<xsl:when test="count(./input)">
							
							<xsl:apply-templates select="./input" mode="text" />
							
						</xsl:when>
						
						<!-- Если это поле Select -->
						<xsl:when test="count(./select)">
						
							<xsl:apply-templates select="select" />
						
						</xsl:when>
						
						<!-- Если это поле Textarea -->
						<xsl:when test="count(./textarea)">
						
							<xsl:apply-templates select="textarea" />
						
						</xsl:when>
						
						<!-- Если это поле button -->
						<xsl:when test="count(./button)">
						
							<xsl:apply-templates select="button" />
						
						</xsl:when>
						
					</xsl:choose><!-- В зависимости от типа поля вызывается соответствующий шаблон -->
					
					
				</xsl:when><!-- Если в текущем узле нет груп полей <input type="radio" /> или <input type="checkbox" /> -->
				
				<!-- Если мы обрабатываем группу полей <input type="radio" /> -->
				<xsl:when test="count(radio_group)">
					
					<!-- Вызываем соответствующий шаблон  -->
					<xsl:apply-templates select="//radio_group" />
					
				</xsl:when><!-- Если мы обрабатываем группу полей <input type="radio /> -->
				
				<!-- Если мы обрабатываем группу полей <input type="checkbox" /> -->
				<xsl:when test="count(checkbox_group)">
					<!-- Вызываем соответствующий шаблон  -->
					<xsl:apply-templates select="//checkbox_group" />
					
				</xsl:when><!-- Если мы обрабатываем группу полей <input type="checkbox" /> -->
				
			</xsl:choose><!-- Обработка полей формы -->
			
			<!-- Если у поля есть узел с сообщением с подсказкой -->
			<xsl:if test="$instr != 'null'">
				
				<!-- Создаем поле с текстом сообщения -->
				<div class="instr" id="{./node()/field_options/id}-instr">
				
					<xsl:value-of select="$instr" disable-output-escaping="yes" />
				
				</div>
				
			</xsl:if><!-- Если у поля есть узел с сообщением с подсказкой -->
			
			<!-- Если у поля есть узел с сообщением об ошибке -->
			<xsl:if test="$error != 'null'">
				
				<!-- Создаем поле с текстом сообщения -->
				<div class="error" id="{./node()/field_options/id}-error">
				
					<xsl:value-of select="$error" disable-output-escaping="yes" />
				
				</div>
				
			</xsl:if><!-- Если у поля есть узел с сообщением об ошибке -->
			
		</div><!-- Контейнер для всего, что так или иначе относится к понятию одного экземпляра поля формы -->
		
	</xsl:template><!-- Шаблон обработки поля формы -->
	
	<!-- Шаблон обработки группы полей <input type="radio" /> -->
	<xsl:template match="radio_group">
		
		<!-- Контейнер поля формы -->
		<div class="field">
			
			<!-- Вызываем шаблон для обработк поля формы -->
			<xsl:apply-templates select="entity" />
		
		</div><!-- Контейнер поля формы -->
		
	</xsl:template><!-- Шаблон обработки группы полей <input type="radio" /> -->
	
	<!-- Шаблон обработки группы полей <input type="checkbox" /> -->
	<xsl:template match="checkbox_group">
		
		<!-- Контейнер поля формы -->
		<div class="field">
			<!-- Вызываем шаблон для обработк поля формы -->
			<xsl:apply-templates select="entity" />
		
		</div><!-- Контейнер поля формы -->
	
	</xsl:template><!-- Шаблон обработки группы полей <input type="checkbox" /> -->
	
	<!-- Шаблон для обработки полей формы input -->
	<xsl:template match="input" mode="text">
		
		<!-- Контейнер поля формы -->
		<div class="field">
			
			<!-- Создаем тег поля формы -->
			<input>
				
				<!-- Для каждого из узла его свойст и атрибутов -->
				<xsl:for-each select="./field_options/child::node()">
					
					<!-- Сохраняем в переменной имя атрибута -->
					<xsl:variable name="attrName" select="name()" />
					
					<!-- Добавляем атрибут -->
					<xsl:attribute name="{$attrName}">
						
						<!-- Если это не атрибут обязательного поля, отключенного поля или отмеченного поля -->
						<xsl:choose>
						
							<xsl:when test="name() != 'required' and name() != 'disabled' and name() != 'checked'">
								
								<!-- Устанавливаем значение для атрибута -->
								<xsl:value-of select="./node()" disable-output-escaping="yes" />
								
							</xsl:when>
							
						</xsl:choose>
						
					</xsl:attribute>
					
				</xsl:for-each><!-- Для каждого из узла его свойст и атрибутов -->
				
			</input>
			
		</div><!-- Контейнер поля формы -->
		
	</xsl:template><!-- Шаблон для обработки полей формы input -->
	
	<!-- Шаблон для обработки полей формы button -->
	<xsl:template match="button">
		
		<!-- Контейнер поля формы -->
		<div class="field">
			
			<!-- Создаем тег поля формы -->
			<button>
				
				<!-- Для каждого из узла его свойст и атрибутов -->
				<xsl:for-each select="./field_options/child::node()">
					
					<!-- Сохраняем в переменной имя атрибута -->
					<xsl:variable name="attrName" select="name()" />
					
					<!-- Добавляем атрибут -->
					<xsl:attribute name="{$attrName}">
						
						<!-- Если это не атрибут обязательного поля, или отмеченного поля -->
						<xsl:choose>
						
							<xsl:when test="name() != 'required' and name() != 'checked'">
								
								<!-- Устанавливаем значение для атрибута -->
								<xsl:value-of select="./node()" disable-output-escaping="yes" />
								
							</xsl:when>
							
						</xsl:choose>
						
					</xsl:attribute>
					
				</xsl:for-each><!-- Для каждого из узла его свойст и атрибутов -->
				
				<xsl:choose>
					
					<!-- Если для кнопки задано значение действия -->
					<xsl:when test="./field_options/value != ''">
						
						<!-- Выводим его для этой кнопки -->
						<xsl:value-of select="./field_options/value" disable-output-escaping="yes" />
						
					</xsl:when>
					
					<xsl:otherwise>
						
						<!-- В ином случае пишем на ней абстрактное... -->
						<xsl:text>Кнопка</xsl:text>
					
					</xsl:otherwise>
					
				</xsl:choose>
				
			</button>
			
		</div><!-- Контейнер поля формы -->
		
	</xsl:template><!-- Шаблон для обработки полей формы input -->
	
	<!-- Шаблон для обработки полей формы <input type="radio" /> или <input type="checkbox" /> -->
	<xsl:template match="entity">
		
		<xsl:choose>
		
			<!-- Если у поля есть заголовок -->
			<xsl:when test="count(child::label)">
				
				<!-- Сохраняем в переменной значение атрибута for -->
				<xsl:variable name="for" select="./label/@for" />
				
				<!-- Открываем тег <label> -->
				<label for="{./label/@for}">
					
					<!-- Если это первое поле из всей группы -->
					<xsl:if test="position() = '1'">
					
						<!-- Добавляем к нему атрибут class -->
						
						<xsl:attribute name="class">
						
							<!-- Если ранее этот атрибут уже существовал у поля, к его значению добавим значение first -->
							<xsl:value-of select="concat( ./field_options/class, ' first' ) " disable-output-escaping="yes" />
						
						</xsl:attribute>
						
					</xsl:if><!-- Если это первое поле из всей группы -->
					
					<!-- Создаем тег поля input -->
					<input>
						
						<!-- Для каждого из свойств и атрибутов поля формы -->
						<xsl:for-each select="./field_options/child::node()">
							
							<!-- Сохраняем имя атрибута в переменной -->
							<xsl:variable name="attrName" select="name()" />
							
							<!-- Создаем атрибут -->
							<xsl:attribute name="{$attrName}">
								
								<xsl:choose>
									
									<!-- Если это не атрибут обязательности поля, отключенного поля или выбранного -->
									<xsl:when test="name() != 'required' and name() != 'disabled' and name() != 'checked'">
										
										<!-- Присвоить значение атрибуту -->
										<xsl:value-of select="./node()" disable-output-escaping="yes" />
										
									</xsl:when>
									
								</xsl:choose>
								
							</xsl:attribute><!-- Создаем атрибут -->
							
						</xsl:for-each><!-- Для каждого из свойств и атрибутов поля формы -->
						
					</input>
					
					<!-- Выводим значение текста тега <label> -->
					<xsl:value-of select="child::label" disable-output-escaping="yes" />
					
				<!-- Закрываем тег <label> -->
				</label>
				
			</xsl:when><!-- Если у поля есть заголовок -->
		
		</xsl:choose>
		
	</xsl:template><!-- Шаблон для обработки полей формы <input type="radio" /> или <input type="checkbox" /> -->
	
	<!-- Шаблон для обработки полей списков -->
	<xsl:template match="select">
		
		<!-- Создаем контейнер для поля формы -->
		<div class="field">
			
			<!-- Создаем элемент поля списка -->
			<select>
				
				<!-- Для каждого из свойств и атрибутов поля формы -->
				<xsl:for-each select="./field_options/child::node()">
					
					<!-- Сохраняем имя атрибута в переменной -->
					<xsl:variable name="attrName" select="name()" />
					
					<!-- Создаем атрибут -->
					<xsl:attribute name="{$attrName}">
						
						<xsl:choose>
							
							<!-- Если это не атрибут обязательности поля, отключенного поля -->
							<xsl:when test="name() != 'required' and name() != 'disabled'">
								
								<!-- Присвоить значение атрибуту -->
								<xsl:value-of select="./node()" disable-output-escaping="yes" />
								
							</xsl:when>
							
						</xsl:choose>
						
					</xsl:attribute><!-- Создаем атрибут -->
					
				</xsl:for-each><!-- Для каждого из свойств и атрибутов поля формы -->
				
				<!-- Вызываем шаблон для элементов списка — тегов <option>  -->
				<xsl:apply-templates select="option" />
				
			</select>
			
		</div><!-- Создаем контейнер для поля формы -->
		
	</xsl:template><!-- Шаблон для обработки полей списков -->
	
	<!-- Шаблон для элементов списка — тегов <option>  -->
	<xsl:template match="option">
		
		<!-- Открываем тег -->
		<option value="{@value}">
			
			<!-- Если у тега есть атрибут selected -->
			<xsl:if test="count(@selected)">
				
				<!-- Устанавливаем этот атрибут элементу -->
				<xsl:attribute name="selected" />
				
			</xsl:if>
			
			<!-- Вставляем текстовое значение элемента -->
			<xsl:value-of select="." disable-output-escaping="yes" />
			
		</option>
		
	</xsl:template>
	
	<!-- Шаблон для обработки поля типа textarea -->
	<xsl:template match="textarea">
		
		<!-- Создаем контейнер для поля формы -->
		<div class="field">
			
			<!-- Открываем тег textarea -->
			<textarea>
				
				<!-- Для каждого из свойств и атрибутов поля формы -->
				<xsl:for-each select="./field_options/child::node()">
					
					<!-- Сохраняем имя атрибута в переменной -->
					<xsl:variable name="attrName" select="name()" />
					
					<!-- Создаем атрибут -->
					<xsl:attribute name="{$attrName}">
						
						<xsl:choose>
							
							<!-- Если это не статус отключенного поля или поля обязательного -->
							<xsl:when test="name() != 'required' and name() != 'disabled'">
								
								<!-- Присваиваем значение атрибуту -->
								<xsl:value-of select="./node()" disable-output-escaping="yes" />
								
							</xsl:when>
							
						</xsl:choose>
						
					</xsl:attribute>
					
				</xsl:for-each><!-- Для каждого из свойств и атрибутов поля формы -->
				
				<!-- Помещаем в поле формы текст  -->
				<xsl:value-of select="text" disable-output-escaping="no" />
				
			</textarea>
			
		</div><!-- Создаем контейнер для поля формы -->
		
	</xsl:template><!-- Шаблон для обработки поля типа textarea -->
	
	
</xsl:stylesheet>

А давайте теперь посмотрим, насколько «сложнее» будет сделать форму регистрации подобную этой:


*
Логин может содержать только буквы и цифры латинского алфавита
*
Пароль может содержать только буквы и цифры латинского алфавита
*
Пароль должен в точности совпадать с предыдущим вводом
*
На указанный адрес электронной почты будет отправлено письмо с описаний действия для активации вашей учетной записи

<?php
// Создаем экземпляр объекта класса, отвечающего за построение формы
// Вызываем статический метод instance() класса Core_Form
$oCore_Form = Core_Form::instance()
	
	// Атрибуту name тега <form> устанавливаем значение regUser
	->name( 'regUser' )
	
	// Устанавливаем значение атрибута id формы
	->id( 'regUser' )
	
	// Значение javascript-атриуба onsubmit для тега <form>
	->onsubmit( 'return false;' )
	
	// Значение атрибута для автозаполнения полей формы
	->autocomplete( 'no' )
	
	// Добавляем поле формы, вызвав метод add() класса Core_Form
	->add(
		
		// Вызываем статический метод класса createField() Core_Form 
		// В качестве аргумента передаем этому методу значение типа поля
		// Это значение хранится внутри класса Core_Form в константе
		// Чтобы не ошибиться и не опечататься, передаем это значение таким образом
		Core_Form::createField( Core_Form::FIELD_INPUT )
		
			// Атрибут type тега <input>
			->type( 'text' )
			
			// Атрибут id
			->id( 'login' )
			
			// Атрибут name
			->name( 'login' )
			
			// Атрибут placeholder
			->placeholder( 'Введите логин пользователя' )
			
			// Заголовок поля в теге <label>
			->label( 'Логин пользователя:' )
			
			// Текст для поля подсказки 
			->instr( 'Логин может содержать только буквы и цифры латинского алфавита' )
			
			// Поле будет обязательным для заполнения
			->requireField( TRUE )
			
			// С этим полем пока что всё, переходим к следующему
	)
	
	// Действуем аналогично, теперь у нас будет поле <input type="password" />
	->add(
		Core_Form::createField( Core_Form::FIELD_INPUT )
			->type( 'password' )
			->id( 'password' )
			->name( 'password' )
			->placeholder( 'Введите пароль пользователя' )
			->label( 'Пароль пользователя:' )
			->instr( 'Пароль может содержать только буквы и цифры латинского алфавита' )
			->requireField( TRUE )
	)
	
	// Действуем аналогично, пользователю нужно повторить пароль, чтобы снизить риск его опечатки
	->add(
		Core_Form::createField( Core_Form::FIELD_INPUT )
			->type( 'password' )
			->id( 'passwordApproove' )
			->name( 'passwordApproove' )
			->placeholder( 'Снова введите пароль' )
			->label( 'Повторный ввод пароля:' )
			->instr( 'Пароль должен в точности совпадать с предыдущим вводом' )
			->requireField( TRUE )
	)
	
	// Добавляем поле для ввода email
	->add(
		Core_Form::createField( Core_Form::FIELD_INPUT )
			->type( 'text' )
			->id( 'email' )
			->name( 'email' )
			->placeholder( 'Введите адрес электропочты' )
			->label( 'Электропочта пользователя:' )
			->instr( 'На указанный адрес электронной почты будет отправлено письмо с описаний действия для активации вашей учетной записи' )
			->requireField( TRUE )
	)
	
	// Добавляем поле <input type="checkbox" />
	->add(
		Core_Form::createField( Core_Form::FIELD_INPUT )
			->type( 'checkbox' )
			->id( 'signNews' )
			->name( 'signNews' )
			->label( 'Подписаться на рассылку:' )
			->checked( TRUE )
	)
	
	// Добавляем кнопку отправки формы на обработку
	->add(
		Core_Form::createField( Core_Form::FIELD_INPUT )
			->type( 'submit' )
			->id( 'btn-submit' )
			->name( 'btn-submit' )
			->value( 'Зарегистрироваться' )
	)

	// Добавляем кнопку очистки полей формы
	->add(
		Core_Form::createField( Core_Form::FIELD_INPUT )
			->type( 'reset' )
			->id( 'btn-reset' )
			->name( 'btn-reset' )
			->value( 'Очистить поля' )
	)

	// Добавляем кнопку возврата к авторизации
	->add(
		Core_Form::createField( Core_Form::FIELD_BUTTON )
			->id( 'btn-back' )
			->name( 'btn-back' )
			->value( 'Вернуться к авторизации' )
	);

/**
 * Данные для построения формы собраны, готовы.
 *
 * За отображение формы на экране отвечать будет класс Core_Form_Show
 * Вызываем конструктор этого класса, передав ему в виде аргумента экземпляр 
 * объекта нашей формы
 *
 */
$oCore_Form_Show = new Core_Form_Show( $oCore_Form );

/**
 *  Дальнейшая обработка формы происходит по следующему алгоритму.
 *  
 *  1. Вызывается метод show() класса Core_Form_Show. Этот метод сформирует промежуточный XML-документ, 
 *     в который поместит всю информацию о форме, её атрибутах и полях.
 *  2. XML-документ будет передан XSLT-процессору.
 *  3. XSLT-процессор посредством обработки XSL-шаблона представит нам форму в таком виде, в каком мы захотим.
 *  4. Имя XSL-шаблона указываем вызвав метод byXSLName(), передав имя шаблона в виде аргумента.
 *  
 */
$oCore_Form_Show->show(
	Core_Xsltprocessor::instance()
		->byXSLName( '1' ));
?>

Считаю подобный алгоритм лучшим вариантом реализации построения веб-формы (авторизации пользователей, регистрации пользователей и вообще всего того, что связано с формами). Мы один раз написали класс, в котором реализовали сбор информации о полях формы. Этот класс отдает собранные данные, чтобы сформировать из них XML-документ, который будет обработан XSL-шаблоном и выведен на экран.

Подобный алгоритм позволяет нам при написании класса для построения формы не зацикливаться над тем, как построить html-код формы. И что делать, допустим, если мы захотим добавить в форму различные информационные блоки (всплывающие подсказки и другие).

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

Я не привел код реализации класса. Пока что. Многие из вас поймут, что там даже не один класс, а... несколько. Но, наверное, вы хотели бы лично убедиться в его работоспособности, не так ли? Что будет, если я вам скажу, что даже весь вышеупомянутый код писать нет необходимости? Не верите? Смотрите сами!

Я разместил эту статью: 08.06.2019
Количество просмотров: 281
Яндекс.Метрика