...

понедельник, 11 ноября 2013 г.

[Из песочницы] Mooha — нодовый интерфейс для PHP


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


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



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


Интерфейс программы Plogue Bidule выглядит следующим образом:



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


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



Plogue Bidule включает в себя много интересных возможностей, которые не ограничены только работой со звуком, но для данной статьи этого описания будет достаточно.


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


Нодовые редакторы широко используются в графических пакетах для создания 3D графики. В таких программах «черные ящики» описывают сложнейшие структуры и функции, а интерфейс позволяет сохранить свободу и гибкость в построении логики выполнения. Самая интересная, на мой взгляд, реализация нодовой системы в программе Houdini.



Так а что же с веб-программированием? Поиск похожих систем не дал результатов. Я подумал, что, возможно, в такой технологии просто отсутствует необходимость. И что идея моя родилась скорее от некомпетентности и наивности. Но желание попробовать создать такой инструмент оказалось сильнее моих сомнений.


Так родился проект Mooha.


PHP, MySQL, HTML




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

  • HTML/XML ноды (для вывода HTML/XML тэгов)

  • PHP ноды (управление логикой)

  • Ноды запросов к базе данных MySQL


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


Рассмотрим по порядку все три категории нод.


HTML/XML ноды





Соединяя такие ноды между собой мы получаем простейшую структуру HTML документа. По-умолчанию HTML/XML нода имеет по одному входу и одному выходу. Вход (IN) — то, что помещается внутрь тэга, выход (OUT) — результирующая строка. Таким образом из последнего выхода ноды «HTML» мы получим строку:



<html>
<head>
<meta />
<script />
</head>
<body>
<div>
<div />
<div />
</div>
</body>
</html>


Служебная нода «Merge» используется для возможности повлиять на сортировку входящих соединений. Сортировка доступна в свойствах самой ноды. В последней ноде «HTML» два входящих соединения конкатенируются в том порядке, в котором произошло соединение. Т.е. если сначала подключили «HEAD», а потом «BODY», то в такой последовательности они и появятся в конечной строке.



Атрибуты тэга мы можем прописать в свойствах ноды, но при желании можем выводить их в виде коннекторов для входящих соединений. Например, добавив ноде «my div» коннекторы для атрибутов id и class мы получим такой вид:




<div id="my-id" class="my-class">
<div>my text</div>
</div>


Переменные со строковыми значениями «my-id» и «my-class» попадают в наши атрибуты. Переменная «my text» становится содержимым первого тэга div. Переменные — это уже PHP ноды, о которых речь пойдет чуть ниже.


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



<h1>Hello, %user%</h1>


И если шаблон %user% выведен как коннектор, то теперь мы можем заменить этот шаблон переменной следующим образом:



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



<h1>Hello, Mooha</h1>



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


Например, у нас есть файл template.tpl, который содержит два шаблона для замены: %title% и %content%. Содержимое файла следующее:



<html>
<head>
</head>
<body>
<h1>%title%</h1>
<div>%content%</div>
</body>
</html>


Чтобы подключить файл и заменить шаблоны переменными, достаточно построить вот такую схему:



В результате получим:



<html>
<head>
</head>
<body>
<h1>My article title</h1>
<div>My article content</div>
</body>
</html>


Так как мы уже вышли за рамки описания только HTML нод, перейдем к следующему типу — к PHP нодам.


PHP ноды



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

Первые три ноды «1», «2» и «5» — это переменные, значения которых соответствуют названиям.

Т.е. в результате мы получим такой скрипт:



<?php
print abs((1+2)-5);
?>


А на выходе просто число 2.



Я постарался реализовать необходимые логические конструкции, обычно используемые в нодовых системах. Например, вот так выглядит сравнение двух переменных (или результатов двух потоков):



Результат будет true (1!=2).


А вот так можно делать выбор потока, исходя из условий:



Здесь каждому красному коннектору в свойствах ноды присвоены значения, с которыми сравнивается значение, поступившее в коннектор оранжевого цвета. Если значения совпадают, то переключатель Switch пропускает соответствующий поток. В этом примере красные коннекторы имеют значения 1, 2 и 3. То есть результат такой конструкции будет «it was 2».


Во всех цепочках в соединениях передаются конечные значения всех предыдущих вычислений потока. Таким образом в следующем примере у нас результат будет тоже «it was 2», но результирующая строка собирается из нескольких действий:



Генерируется примерно следующий код:



$data = 2;
switch ($data)
{
case "1": // заданное в первом коннекторе значение для сравнения
$result = "it was 1";
break;
case "2":// заданное во втором коннекторе значение для сравнения
$result = str_replace("foo", "2", "in was foo");
break;
case "3": // заданное в третьем коннекторе значение для сравнения
$result = "it was 3";
break;
default:
$result=false;
}
var_dump($result);


Отдельного внимания заслуживает нода Copy, которая по значениям из первого соединения размножает шаблон из второго. Это полезно, например, в том случае, когда нам необходимо размножить HTML код и заменить в нем шаблоны значениями из массива. В самом простом случае с одномерным массивом такая схема выглядит следующим образом:



Для наглядности я назвал ноды значениями их переменных. В первый коннектор (для значений) мы запускаем JSON массив, во второй — HTML код с шаблоном для замены. Все ноды (или цепочки нод), подключенные к коннектору шаблона будут скопированы столько раз, сколько имеется элементов в массиве, подключенному к первому коннектору. При этом у каждой ноды слева от такого соединения появится специальная настройка, какие именно данные в ней заменять элементом из массива. В нашем случае в ноде с параграфом настроена замена шаблона %number%. Т.е. результатом этой схемы будет следующий код:



<p>first paragraph</p>
<p>second paragraph</p>
<p>third paragraph</p>



Вот пример сложнее, с массивом объектов и с несколькими нодами:



В переменной «JSON» следующий массив объектов:



[
{"title":"first title","content":"first content"},
{"title":"second title","content":"second content"}
]


В ноде c тэгом <h1> шаблону %title% в качестве источника для копирования указан объект «title» массива. В ноде с тэгом <p> шаблону %content% в качестве источника для копирования указан объект «content» массива. В результате генерируется следующий код:



<div>
<h1>first title</h1>
<p>first content</p>
</div>
<div>
<h1>second title</h1>
<p>second content</p>
</div>



По умолчанию нода Copy имеет тип foreach и работает так, как я описал в двух примерах выше. Но мы можем выбрать тип for, после чего появится возможность настроить стартовое значение счетчика, конечное значение, шаг и тип инкремента (возрастающий или убывающий).


Ноды запросов к таблицам базы данных MySQL



Базовая нода для запросов — это таблица, а точнее — оператор SELECT, который выполняется по-умолчанию при подключении к любому полю. Присоединившись к последнему выходу, обозначенному символом "*", мы выполним запрос:



SELECT * FROM `authors`


Результат получим в виде массива объектов в формате JSON. Это подключение «поймает» следующий набор:



[
{ "id": "1", "Name": "Iain Menzies Banks", "Birthday": "1954-02-16" },
{ "id": "2", "Name": "Charles Michael Palahniuk", "Birthday": "1962-02-21" }
]


Чтобы получить массив только имен авторов, соединение будет выглядеть следующим образом:



На выходе мы получим обычный одномерный массив:



["Iain Menzies Banks", "Charles Michael Palahniuk"]


На данный момент в редакторе есть несколько служебных SQL нод, с помощью которых можно выполнять несложные выборки. Например:



В первом случае (слева) мы выполним запрос:



SELECT * FROM `authors` WHERE id=1


И получим массив с одним объектом:



[
{ "id": "1", "Name": "Iain Menzies Banks", "Birthday": "1954-02-16" }
]


Во втором случае (справа) запрос будет таким:



SELECT * FROM `authors` WHERE Name LIKE '%Banks%'


А вот запрос с несколькими условиями и сортировкой:




SELECT * FROM `authors` WHERE (`Birthday` < '1990-01-01' AND `Birthday` > '1920-01-01') ORDER BY `Name` ASC


Служебная нода AND выступает здесь не только в качестве логического оператора в условии WHERE, но и в качестве соединения команд в одном запросе (в нашем случае, добавление к запросу команды ORDER BY)



Запросы INSERT, UPDATE и DELETE вызываются подключениями к таким же нодам-таблицам, но только с соответствующими настройками.


Простой INSERT выглядит так:




INSERT INTO `authors` (`id`, `Name`, `Birthday`) VALUES ('1', 'Iain Menzies Banks', '1954-02-16')


UPDATE:




UPDATE `authors` SET `Name`='Iain Banks', `Birthday`='1954-02-17' WHERE `id`='1'


DELETE:




DELETE FROM `authors` WHERE `Birthday`<'1954-02-17'


В процессе построения генерируются запросы с использованием PDO.


С помощью нодовых соединений можно построить почти любые типы запросов, в том числе организовать JOIN объединения. Однако не для всех сложных запросов я нашел идеальные графические решения, поэтому оставил возможность вписать текст SQL инструкции в специальную ноду. Немного забегая вперед, скажу, что работу с базой данных можно построить в системе и другими способами, не только с помощью MySQL нод.


«А теперь вместе!»




И, наконец, пример объединения всех трех технологий (MySQL, PHP и HTML) в одной цепочке, может выглядеть следующим образом:


Здесь у нас имеется заготовленный шаблон library.tpl для вывода книг из домашней библиотеки. Содержимое примерно следующее:



<div>
<select>
<option value=''>all authors</option>
%author-list%
</select>
</div>
<br />
<div>%book-list%</div>


У нас есть два шаблона для замены: %author-list% и %book-list%. В первый нам нужно вывести тэги option, соответствующие всем авторам. Во второй — все книги, а именно — размножить блок «book block», в котором содержится HTML код вывода одной книги. Первый список нам нужен для того, чтобы фильтровать книги по автору (для этого действия мы будем использовать функцию jquery в шаблоне library.tpl).


Для первого шаблона %author-list% мы добавляем ноду Copy («Author Copy»), которая по данным из таблицы «authors» клонирует тэг «option», при этом внутрь тэга помещает имя автора, а атрибуту «value» присваивает значение id. На выходе ноды «Author Copy» мы получим следующий код:



<option value='1'>Iain Menzies Banks</option>
<option value='2'>Charles Michael Palahniuk</option>
<option value='3'>Mikhail Bulgakov</option>


Нода «book block» содержит следующий код:



<div class='author-%author-id%'>
<img width='100' src='images/%image%' align='left' />
<b>%title%</b> by <b>%author%</b>
<br />
ISBN: %isbn%<br />
<small>%description%</small>
<br clear='all'/>
</div>


Шаблоны в этом блоке заменяются данными книги из объединенной выборки двух таблиц «books» и «authors». Таблица «authors» здесь нужна для вывода в книге имени ее автора. Сам блок клонируется столько раз, сколько у нас записей в таблице «books».


Вот что у нас получится в результате:



Заключение




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

  • Создание групп — специальных нод, внутрь которых можно прятать целые схемы и выводить только необходимые входящие и выходящие коннекторы. Глубина вложенности групп неограниченна.

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

  • Сохранение логических схем или их частей в качестве сниппетов, для повторного использования в других скриптах.

  • Возможность написания своих функций, которые можно выводить в редакторе в виде нод.

  • Создание своих или подключение готовых классов, возможность вывода методов классов в качестве нод.

  • Подключение скриптов (include), как созданных в самой системе, так и скриптов из других файлов.

  • Возможность редактирования файлов, используя встроенный редактор.

  • Инструменты для создания и редактирования таблиц MySQL.


Результат работы редактора — набор генерированных PHP файлов. Каждая сцена — это отдельный скрипт, сохраненный в специальной папке. Такой скрипт может выполняться без помощи самого редактора. Кроме того, в настройках системы можно выбрать стартовый сценарий, который будет включать в себя главную логику работы всего проекта, исходя из полученных данных (строка запроса или передаваемые параметры).


Цель написания этой статьи — понять, насколько такая система может быть интересна для разработчиков. Я слишком долго и увлеченно занимался этим проектом и у меня изменилось отношение к нему с того момента, как я начал над ним работу. Поэтому я буду крайне признателен за отзывы и комментарии, за вопросы и предложения, а также за участие в тестировании.


На данный момент проект почти закончен и идет процесс отладки. Совсем скоро я буду готов выложить бета-версию. Для того, чтобы быть в курсе процесса разработки и узнать о выходе проекта — нужно просто подписаться на получение новостей любым удобным способом на сайте Mooha.net.






Техническая информация




В проекте использованы следующие инструменты:


  • HTML5 Canvas и Javascript (нодовый редактор)

  • JQuery 2.0 (панели свойств и настроек)

  • Codemirror (встроенный редактор файлов)

  • CKEditor (расширенный редактор полей таблиц базы данных)

  • Spectrum Colorpicker (изменение цвета элементов нод)

  • jQuery File Tree Plugin (браузер файловой системы)




Серверная часть тестировалась в таких условиях:


  • Apache 2.2.22

  • PHP 5.4.3

  • MySQL 5.5.24


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий