Создаем динамическое XML меню

 

Почему XML?

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

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

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

 

Когда следует и не следует использовать XML?

Итак, когда вы используете XML? Очевидно, вы не захотите хранить в XML файлах всю информацию о пользователях. Это затруднит доступ к информации (а также понизит производительность приложения) - базы данных и LDAP сервера куда более лучше подходят для этой задачи. А также, вряд ли вы захотите хранить в XML файлах каталоги, продукты и информацию о покупателях, т.к., опять же, база данных является более удобным средством для такой задачи.

Но что, если у вас более простые данные? Например, вам нужно хранить такие настройки вашего приложения как, например, источник данных, время ожидания запроса и т.п. Вам не захочется держать эти данные в базе данных (кроме того, в любом случае названия источника данных нельзя хранить в базе данных). Можно ли эти данные хранить в формате XML? Возможно. Вот пример такого XML файла:
Это простой пример хранения информации о настройках. Вы можете хранить эту информацию в файле (возможно, назвав его config.xml) и считывать, обрабатывать и сохранять его.

Этот способ работает, но стоит ли он того? Существует более простой формат файлов - ini формат (популяризованный компанией Microsoft):

[MyApp]
datasource=MyDSN
timeout=90
Вы можете сохранить эту информацию в ini файле (возможно, назвав его config.ini). Затем, с помощью одной функции GetProfileString() вы можете считывать файл, обрабатывать его, получать из него данные в виде переменных. Таким образом, мы видим два преимущества: использование ini файлов понятнее и проще.

Я не предполагаю, что какой-либо формат файла всегда лучше, чем какой-либо другой формат, просто в приведенном примере видно, что XML (несмотря на ажиотаж вокруг него) не всегда является правильным решением.

Итак, в каких случаях XML действительно полезен? Давайте разберем конкретный пример.

 

Определяем задачу

Любому приложению нужно меню, будь то меню со всего лишь несколькими пунктами или меню с подпунктами и дополнительными опциями. Обычно, мы создаем меню с помощью HTML, DHTML, Macromedia Flash MX или какой-нибудь другой технологии. Меню состоит из двух вещей: информационное содержание и визуальное форматирование.

Что если вам нужно создать гибкую систему отображения меню, которая ... Чтобы осуществить это, сперва, нам нужно отделить содержание (пункты меню) от внешнего исполнения. Затем, следует организовать отдельное хранение содержания. И наконец, вам нужно определить простой способ считывания и обработки данных так, чтобы они были доступны любой технологии на стороне клиента.

 

XML как решение поставленной задачи

Давайте начнем с простого меню с 3 пунктами (Рисунок 1).

простое меню с 3 пунктами
Рисунок 1. Простое меню с 3 пунктами

HTML код этого меню может выглядеть так:
Если в нашем меню 3 пункта, у каждого из которых своя ссылка, то как нам следует организовать хранение этой информации? Ее можно хранить в базе данных, но по мере добавления подпунктов наше меню становится более сложным и именно эта особенность заставляет нас использовать XML. Вот как можно описать это в XML файле:
Приведенный пример XML кода определяет меню (используя теги <menu> и </menu>), которое содержит 3 пункта. Каждый пункт обозначен тегами <item> и </item>. Внутри пунктов также есть теги, обозначающие название пункта и его ссылку.

Почему такое описание данных лучше, чем таблица в базе данных или ini файл? Рассмотрим следующий пример, в котором пункты меню содержать подпункты:

В этом примере меню имеет 4 пункта, причем второй из них содержит еще 2 подпункта. Как видим, использование XML для описания данных дает нам бесконечную гибкость - мы можем располагать сколько угодно пунктов, подпунктов в пунктах, подпунктов в подпунктах и т.д. Другими словами, XML предоставляет необходимую структуру хранения данных, а также требуемую гибкость в отображении данных в любом сочетании.

 

Обрабатываем XML

Теперь, когда у нас есть данные в формате XML, пора научиться как написать код, чтобы получить доступ к данным. Как я уже упоминал, ColdFusion MX имеет мощную поддержку обработки XML и, чтобы прочесть XML нам нужна только одна функция:

Функция XMLParse() считывает XML документ (такой, как мы указывали выше и предварительно считанный в некоторую переменную с использованием тега <cffile>) и возвращает структуру, содержащую все XML данные. Вы можете использовать тег <cfdump>, чтобы просмотреть эту структуру (Рисунок 2).


Рисунок 2. Использование функции XMLParse() и тега <cfdump> для отображения структуры XML данных

Например, если вы сохраните приведенный выше XML код в файле menu.xml, то можете прочесть и просмотреть его содержимое с помощью этого CFML кода:
Прочитать XML файл и просмотреть его CFML структуру оказалось для нас просто. Однако, процесс усложняется при обработке и отображении всех данных. Для получения значений требуется циклически (с помощью тега <cfloop>) "пройтись" по ним столько раз, сколько пунктов и подпунктов содержит меню.

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

В этом примере функция GetItem() воспринимает структуру меню как параметр. Она циклически проходит по меню (считая его массивом) и определяет его пункты. Если обнаруженный пункт является пунктом меню (XMLName is "item"), тогда функция показывает его. Но если обнаруженный пункт является вложенным меню (XMLName is "menu"), то внутри функции GetItem() мы снова вызываем функцию GetItem(), которая показывает подпункты вложенного меню.

К тому времени, когда тег <cfloop> закончит циклический проход по меню, он уже считает и отобразит всю структуру меню.

 

Складываем все вместе

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

Код обработки меню может определять все пункты и подпункты меню. Так должно быть несмотря ни на что и, в конечном счете, стиль его обработки всегда остается одним, а информационное содержание зависит от XML данных. Если код обработки меню создает его в виде ненумерованного списка, то должны быть использованы теги <ul> и <li>, а если идет построение более сложной структуры меню, то тут требуется использовать другой HTML код. Но это не должно влиять на обработку XML данных.

Представьте, что вам нужно запросить функцию обработки XML, затем передать в нее имя другой функции, которая дополнит обработку. Чтобы это сделать, нам нужно написать ColdFusion UDF (пользовательскую функцию), которая отформатирует меню по нашему желанию, а затем передаст результат в пользовательскую функцию построения меню. Каждый раз, когда пользовательская функция построения меню находт пункт или вложеное меню, она делает запрос форматирующей пользовательской функции, которая определяет содержимое найденного объекта. Сложно? Нет. То, что я только что описал, называется "вызов функции".

Это привело нас к самой мощной (и наименее понятной) возможности пользовательских функций ColdFusion - к возможности передавать функцию как параметр в другую функцию.

Следующий код вообще не зависит от какой-либо клиентской технологии. Любой файл, который должен содержать меню включает в себя этот файл (menufunc.cfm):

Давайте начнем рассмотр этого примера с конца. Функция BuildMenu() создает меню. Чтобы построит меню, вызываем функцию BuildMenu() и передаем ей два параметра: XML структуру и имя вызываемой функции. Сначала функця BuildMenu() обрабатывает XML, затем она использует функцию IsCustomFunction(), чтобы убедиться, что вызываемая функция - ни что иное как пользовательская функция (UDF) и использует функцию GetMetaData() для получения информации об этой пользовательской функции - это будет означать, что вызываемая функция есть и возвращает правильные данные (чтобы не было ошибок, она должна получить и обработать 3 параметра, а затем вернуть их все в виде строки). Если одна из проверок приводит к ошибке, то функция BuildMenu() вернет сообщение об этой ошибке. Если ошибок не было обнаружено, то функция BuildMenu() вызывает функцию callback() и сообщает ей, что началась обработка меню, затем вызывает функцию GetItem(), чтобы обработать пункты меню (передавая их в функцию callback) и затем вызывает функцию callback() второй раз и сообщает ей, что обработка меню закончена.

Функция GetItem() циклически проходит по меню. Каждый раз, когда она находит пункт меню, то для его отображения передает его в функцию callback (и функция callback возвращает результат обработки); и каждый раз, когда она обнаруживает пункт меню, то вызывает функцию callback, а затем саму себя (и считывает все подпункты пункта, полученного повторным вызовом функции GetItem()).

К тому времени, когда функция BuildMenu() завершит свою работу, возвращаемый ей код уже полностью вернет строку с данным меню.

Все, что мы упустили, это возвращаемый код. Посмотрим на файл menutest.cfm:

Сначала мы вставляем функции построения меню.

Далее я объясню функцию визуального форматирования - MenuAsList() (которая, как видно из ее названия, создает меню в виде списка). Мой код нигде не запрашивает функцию MenuAsList(), а передает ее в функцию BuildMenu(), которая запрашивает ее по мере необходимости. Функция MenuAsList() принимает три переменные, по которым мы определяем, какой вернуть HTML код для визуального форматирования меню. Большую часть этой функции представляет тег <cfswitch>, который строит результат на основе значения переменной type. Следовательно, если переменная type равна "item", переменная text равна "Пункт 1", а link - "link1.cfm", то функция MenuAsList() вернет следующее:

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

Чтобы построить меню, используйте следующий код:

Функция BuildMenu() возвращает все меню в виде строковой переменной, которую можно отобразить так:
А это файл menu.xml (который содержит 11 пунктов в 3-х уровнях):
И какой HTML код мы получаем?
Выполнив весь наш код, мы получаем полную структуру меню (как показано на Рисунке 3).


Рисунок 3: Динамическое XML меню с пунктами и подпунктами.

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

Все очень просто, не правда ли?

 

Заключение

Как видим, XML хорошо подходит для хранения данных, когда вам нужно объединить структуру и гибкость. Тем не менее, XML не всегда является правильным решением всех проблем. Не поддавайтесь искушению использовать XML там, где он не подходит. Для описанной в этой статье системы отображения меню, XML подошел очень хорошо, а также набор пользовательских функций (UDF) сделал наше решение более простым и гибким.

Архив с примером.

Примечание: Вы должно быть удивлены, почему я не использовал XSL для форматирования данных. Ну, у меня так получилось. Но, этот способ гораздо проще и понятнее, а также более гибкий и интересный, чем XSLT.


Источник: www.macromedia.com/desdev/mx/coldfusion/extreme/xml_menu.html

 


Hosted by uCoz