Представьте, что вы являетесь руководителем команды разработчиков нового PHP-проекта. Согласно требованиям к проекту предварительная модель данных состоит приблизительно из 150-ти таблиц. По предварительным расчетам на написание и тестирование каждого класса доступа к базе уходит примерно один рабочий день, то есть на построение 150-ти таблиц у вас уйдет 150 дней. Таким образом, при 20-ти рабочих днях в месяц на написание кода, отвечающего за доступ к базе данных, у вас уйдет почти восемь месяцев.
Для того чтобы сократить время на разработку вам необходимо найти способ уменьшить временные затраты. С этой целью вы можете использовать постоянную структуру, однако даже при таком подходе на выполнение задачи уйдет около четырех месяцев. Можно создать общую библиотеку, однако подобные библиотеки могут быть сложны в использовании, подвержены ошибкам и сложны в отладке.
При другом способе можно создать один или несколько базисных классов, а затем при помощи генератора кода построить остальные. Казалось бы, это - наихудший сценарий, при таком подходе только на написание генератора уйдет около месяца. Однако после этого генерирование всего кода займет всего несколько минут. В дополнении к этому любые ошибки, появляющиеся в случае, если SQL-схема не соответствует коду доступа к базе данных, будут исключаться, так как генератор создает и SQL-схему, и код доступа.
В данной статье рассказывается о применении такой методики и объясняется, почему при таком подходе приложение будет проще в обслуживании, надежнее и легче в написании кода. В первой части документа описывается генерирование основного кода и пошаговое построение той части генератора, которая создает SQL заданной модели. Во второй части руководства находится подробное описание XSL и описывается, как закончить генератор на примере той части кода, которая создает PHP.
Перед началом работы вам понадобится генератор, который на основе абстрактной модели базы данных создает SQL-схему и PHP-классы для доступа к БД. На Рисунке 1 представлена такая модель и показан потенциал создания кода доступа к базе для других технологий, например Java™.
Рис. 1. Основной информационный поток для генератора
На Рисунке 1 пунктиром (SQL модель и модель доступа к БД) показаны промежуточные модели, созданные из абстрактной модели. Область, помеченная точечным пунктиром (Java) указывает на потенциальную возможность генерирования кода.
Следующим вопросом является выбор используемой технологии. Генератор можно написать на Java, Perl, Python или Ruby. При всей простоте данной задачи можно обойтись такими механизмами создания шаблонов как Velocity или XSLT. Наилучшим выбором является язык XSLT 2.0, так как он сам по себе обладает большой мощностью и, кроме того, для расширения возможностей его можно внедрить в Java код.
Язык XSLT версии 2.0 предлагает разработчикам, ищущим механизм создания шаблонов для генерирования кода, расширенные возможности. В данной статье эти возможности раскрываются в контексте создания надежного генератора для SQL и PHP, который создает определение абстрактной таблицы. В XSLT 1.x возможны только одноуровневые преобразования. Использование XSLT версии 2.0 позволяет построить промежуточные модели генерируемого кода. Этот набор функциональных возможностей упрощает понимание и обслуживание генератора, а также позволяет переназначить его для задач другого языка.
Являясь технологией преобразования XML, XSLT может преобразовывать XML как в XML, так и в текст. В данном генераторе кода используется оба таких преобразования. В XSLT версии 2.0 были произведены значительные улучшения по сравнению с XSLT версий 1.x. В данной статье используются три следующие новые возможности, доступные в новом стандарте XSLT 2.0:
- Функции: Появилась возможность определять заказные функции. Это является значительным усовершенствованием по сравнению с версией XSLT 1.x, в которой для этой цели использовался громоздкий синтаксис построения шаблонов.
- Промежуточные деревья: В XSLT версий 1.x можно было управлять только исходным деревом XML. При помощи XSLT версии 2.0 можно создавать в памяти промежуточные деревья, которые можно использовать для управления другими шаблонами.
- Результирующие документы: В новой версии XSLT 2.0 отдельный шаблон может генерировать множество результирующих файлов. Эта возможность используется в нашей задаче для генерирования SQL-файла и отдельных PHP-файлов.
Теперь перейдем к построению генератора.
Начнем пример с ввода в генератор исходных данных. В Листинге 1 представлен пример определения таблицы для простой базы данных библиотечного каталога.
Листинг 1. Определение исходной абстрактной таблицы
<?xml version="1.0" encoding="UTF-8"?> <tables> <table name="Author"> <field name="first" type="text"/> <field name="last" type="text"/> </table> <table name="Publisher"> <field name="name" type="text"/> <field name="last" type="text"/> </table> <table name="Book"> <field name="name" type="text"/> <field name="author" type="id"/> <field name="publisher" type="id"/> </table> </tables> |
Этот простой XML-код определяет три таблицы базы данных с полями: Author, Publisher и Book. Исходные данные для вашего PHP-приложения могут быть более сложными, однако данный код вполне подходит в качестве начального примера.
Вы можете создать генератор, преобразующий данный XML в SQL или PHP за один проход, однако этого делать не рекомендуется, так как шаблоны станут сложными и трудными в поддержке. На сегодняшний день лучшим подходом для генерирования кода будет наличие множества уровней моделей, каждый из которых имеет свои характерные особенности. Такой подход упрощает любое преобразование и напоминает использование множества ярусов на Web-сервере для уменьшения общей сложности и стимуляции повторного использования.
Возвращаясь к Рисунку 1, мы видим, как посредством модели доступа к базе данных абстрактная модель генерирует одну модель для SQL, а другую для PHP. Эти модели, хранящиеся в промежуточных деревьях, используются впоследствии для построения кода. На Рисунке 2 показан процесс преобразования генератором таблицы Author.
Рисунок 2. Развитие отдельной SQL-таблицы
На Рисунке 2 в верхнем блоке показана исходная абстрактная модель таблицы. В среднем блоке показана SQL-модель, построенная из этой абстрактной модели. И наконец, в нижнем блоке показан результирующий вывод SQL. Как видно из данной схемы, SQL-модель определяет по пунктам, что именно будет выводиться в конечном SQL-файле. Задачей кода шаблона является простое преобразование XML в SQL, что дает возможность генерировать код из одной модели для различных баз данных. Теперь рассмотрим подробнее само XSL-преобразование.
В Листинге 2 показана начальная часть основного XSL-преобразования генератора.
Листинг 2. Начальная часть XSL-преобразования
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:gen="http://www.codegeneration.net/" version="2.0"> <!-- Output specifications --> <xsl:output method="text"/> <xsl:output method="xml" name="debug-xml" indent="yes"/> <xsl:output method="text" name="sql" indent="yes"/> <!-- SQL Templates --> <xsl:include href="gen2-sql.xsl"/> <xsl:include href="gen2-sql-model.xsl"/> <xsl:include href="gen2-queries.xsl"/> <!-- Database Access Templates --> <xsl:include href="gen2-dba.xsl"/> <xsl:include href="gen2-php.xsl"/> <!-- The generator main entry point --> <xsl:template match="/"> <!-- Create the SQL model --> <xsl:text>Building SQL model</xsl:text> <xsl:variable name="sql-model"> <xsl:call-template name="gen-sql-model"> <xsl:with-param name="model" select="."/> </xsl:call-template> </xsl:variable> <!-- Dump it out for debugging --> <xsl:text>Dumping SQL model</xsl:text> <xsl:result-document href="db/gen-tables.xml" format="debug-xml"> <xsl:copy-of select="$sql-model"/> </xsl:result-document> <!-- Generate the SQL from the SQL model --> <xsl:text>Generating SQL</xsl:text> <xsl:result-document href="db/gen-tables.sql" format="sql"> <xsl:apply-templates mode="sql" select="$sql-model/sql"/> </xsl:result-document> </xsl:template> </xsl:stylesheet> |
Важной частью данного сценария является создание переменной sql-model, содержащей модель SQL, а также тега result-document, который создает SQL-файл, используя содержимое переменной sql-model. Данные теги демонстрируют два существенных изменения в XSLT версии 2.0: возможность создавать временные деревья при помощи тега xsl:variable и возможность создавать множество исходящих файлов при помощи тега xsl:result-document.
Теперь рассмотрим шаблон, генерирующий SQL-модель из модели абстрактной таблицы. В Листинге 3 представлен шаблон gen-sql-model.
Листинг 3. Генератор SQL-модели
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:gen="http://www.codegeneration.net/" version="2.0">
<!-- Builds the SQL model from the original data model -->
<xsl:template name="gen-sql-model">
<xsl:param name="model"/>
<sql>
<xsl:for-each select="$model/tables/table">
<create name="{lower-case(@name)}"
primary-key="{concat(lower-case(@name),'_id')}">
<field name="{concat(lower-case(@name),'_id')}"
type="{gen:model-type-to-sql('integer')}"/>
<xsl:for-each select="field">
<field name="{lower-case(@name)}"
type="{gen:model-type-to-sql(@type)}"/>
</xsl:for-each>
</create>
</xsl:for-each>
</sql>
</xsl:template>
</xsl:stylesheet>
|
Данный шаблон довольно прост: Он берет исходную модель и создает несколько новых тегов для SQL-модели. Обратите внимание на вызов функции gen:model-type-to-sql – он не является частью стандартного набора функций XSLT. Это пользовательская функция, описанная в Листинге 4.
Листинг 4. Функция преобразования модели в SQL
<xsl:function name="gen:model-type-to-sql"> <xsl:param name="type"/> <xsl:choose> <xsl:when test="$type eq 'text'">TEXT NOT NULL</xsl:when> <xsl:when test="$type eq 'id'">INTEGER NOT NULL</xsl:when> <xsl:when test="$type eq 'integer'">INTEGER NOT NULL</xsl:when> </xsl:choose> </xsl:function> |
В XSLT 2.0 появилась возможность создания новых Xpath-функций. Ранее вам необходимо было использовать синтаксис xsl:call-template, который мог стать громоздким из-за растущего количества параметров. Кроме того, использование шаблонов в роли функций "засоряет" пространство имен шаблона.
Заключительным шагом в создании SQL является приведение SQL-модели к SQL-запросу. Этот процесс описан в Листинге 5, где используется два шаблона.
Листинг 5. Шаблоны генерирования SQL
<!-- Template for SQL create tags --> <xsl:template match="create" mode="sql"> DROP TABLE IF EXISTS <xsl:value-of select="@name" />; CREATE <xsl:value-of select="@name" /> ( <xsl:apply-templates mode="sql" select="field" /> PRIMARY KEY ( <xsl:value-of select="@primary-key" /> ) ); </xsl:template> <!-- Template for SQL field tags --> <xsl:template match="field" mode="sql"> <xsl:value-of select="concat(@name,' ',@type,',')" /><xsl:text> </xsl:text> </xsl:template> |
Как видно из данного кода, у нас есть один шаблон, соответствующий в SQL-модели тегу create. Далее путем вызова тега apply-templates вторым шаблоном обрабатываются дочерние для тега create теги field.
Вернемся к Рисунку 2. Обратите внимание на то, что структура XML похожа на структуру SQL. Тег create включает в себя несколько тегов полей. Подобное наблюдается и в SQL, где команда create включает в себя несколько параметров полей. Структурирование модели в такой же манере, как и структурирование кода, может значительно упростить шаблоны кода.
В данной (первой) части документа были представлены некоторые новые особенности XSLT 2.0, в частности, описаны возможности языка и возможность построения промежуточных моделей генерируемого кода. Здесь также проиллюстрировано использование промежуточных моделей для построения надежного генератора для SQL, обрабатывающего определение абстрактной таблицы.
В следующей Части будут представлены способы генерирования PHP-части кода, обеспечивающего доступ к БД для Web-сервера. В ней также будут представлены новые возможности XSLT 2.0.
- Оригинальная статья "Generate SQL with XSLT 2.0"
- Вторая часть документа "Code generation in XSLT 2.0, Part 2: Generate PHP with XSLT 2.0" (developerWorks, Февраль 2005).
- Статья What kind of language is XSLT?
- Статья XML for Data: XSLT 2.0: An early look
- Ресурс
Code Generation Network
о генерировании кода, содержащий статьи, интервью, обзоры книг и списки рассылок обсуждения данной темы.
- Статья Code Generation in Action (автор - Jack D. Herrington), рассказывающая о возможностях генерирования кода для широкого спектра задач, не ограниченных доступом к БД.
- Ссылка на XSLT-процессор Saxon, используемый в данной статье и поддерживающий XSLT 2.0.
- Основополагающий труд по данной теме -
Generative Programming
. Рассказывает как о генерировании кода, так и о других методиках автоматизированного программирования.
- Подробное описание XSLT 2.0 и XPath 2.0.
XSLT 2.0 Programmer's Reference
(автор - Michael Kay) – библия нового стандарта. Справочник по XPath 2.0 –
XPath 2.0 Programmer's Reference
(автор - Michael Kay).
- Ссылки на ресурсы по XML на странице developerWorks XML zone.
- Как стать Cертифицированным разработчиком IBM в области XML и родственных технологиях.
В качестве инженера с более чем 20-летним опытом работы, Jack Herrington является действительным главным редактором Code Generation Network. Он является автором труда Code Generation in Action. Связаться с ним можно по адресу: jack_d_herrington@codegeneration.net