После того как моя статья о фреймворке LOTIS вызвала интерес, я решил подробнее раскрыть его архитектуру и привести примеры кода.
Напоминаю ссылку на репозиторий фреймворка: https://github.com/O-Planet/LOTIS
LOTIS решает одну фундаментальную проблему: разделение логики на клиент и сервер мешает разработке бизнес-приложений. Вместо того чтобы думать о бизнес-логике, разработчики тратят время на:
Синхронизацию данных между клиентом и сервером
Управление состоянием
Написание идентичной логики на двух языках
Обработку AJAX-запросов
LOTIS устраняет эту раздвоенность, позволяя разрабатывать приложения как единое целое. Разберу его работу на примерах. Но сперва – про историю создания, и чем была вызвана эта необходимость.
? История появления: для чего
Я являюсь фрилансером, не совместителем по вечерам, а именно фрилансером. У меня нет «основного» места работы, нет директора. Я сам ищу заказчиков, веду переговоры, заключаю договора, сам выполняю проекты, внедряю, обучаю. Занимаюсь этим уже более 20-и лет. Отсюда и возникла потребность в фреймворке «все – в одном» для быстрой разработки WEB-приложений, ведь большинству заказчиков результат нужен «еще вчера». Поэтому, скорость разработки – первый критерий для моей успешной работы.
Кроме того, бюджета для привлечения на проект отдельных фронта, бэка, дизайнера, тестировщика, внедренца и руководителя проекта у большинства заказчиков конечно же нет: они ищут кого-то одного, сочетающего все эти функции. И мне просто необходим инструмент, позволяющий вести разработку в едином пространстве без многослойных зависимостей.
Но если кто-то думает, что фрилансеру-одиночке отдают только проекты вроде лендингов, то спешу разочаровать: к услугам фрилансеров обращаются сети Duty Free, производственные комбинаты, логистические компании, сети ресторанов, супермаркета, частные медицинские клиники, предприятия оборонной промышленности. И задачи у них объемные. Поэтому, третье требование к моему подручному инструменту – возможность оперировать объектами и сущностями предметной области бизнес процессов, такими, как справочник, документ с несколькими табличными частями, отчеты, стоки и коллекторы данных, устойчивый многопользовательский режим, подкачка из базы данных, реактивность…
Ну и в заключении, поскольку я являюсь адептом объектно-ориентированного подхода в программировании, то отказ от тегов и разработка на классах прозвучала, как интересный вызов и возможность взглянуть на WEB-разработку по-новому.
Так и появилась идея создания фреймворка LOTIS (Low Time Script), его первые, неудачные релизы и текущая, стабильная версия. История длиною в десять лет.
⭐ Основные отличительные особенности LOTIS
Фреймворк для быстрой разработки сложных бизнес-приложений одним разработчиком или небольшой командой
Единое функциональное пространство: линейная логика, без разделения на клиент/сервер
Работа с объектами бизнес-логики, а не с DOM-элементами
Отсутствие необходимости писать HTML вручную: приложение строится на классах
LOTIS реализует модель CMA (Construct – Metadata - Assembly)
┌───────────┐ ┌───────────┐ ┌───────────┐ │ │ │ │ │ │ │ CONSTRUCT│────>│ METADATA │────>│ ASSEMBLY │ │ │ │ │ │ │ └───────────┘ └───────────┘ └───────────┘ │ │ │ │ Создание │ Генерация │ Преобразование │ объектной модели │ структурирован- │ метаданных │ │ ных данных │ в DOM ▼ ▼ ▼ Объекты Массивы данных Готовый интерфейс PHP/JS (тип, id, свойства) в браузере
Суть CMA: Вы пишете объектно-ориентированный код как в десктопном приложении, без разделения на клиент и сервер, а фреймворк автоматически преобразует его в метаданные, по которым интерпретатор строит веб-интерфейс.
Традиционный подход |
LOTIS (CMA) |
Разделение на клиент/сервер |
Единое функциональное пространство |
Работа с DOM-элементами |
Работа с объектами бизнес-логики |
Написание HTML вручную |
Автоматическая генерация через метаданные |
Сложные AJAX-запросы |
Последовательная логика без разделения |
? Объектная модель LOTIS
┌─────────┐ ┌───────────┐ ┌──────────┐ ┌───────────┐ ┌───────────┐ │ Ether │ ←─ │ Construct │ ←─ │ Quark │ ←─ │ Element │ ←─ │ Space │ └─────────┘ └───────────┘ └──────────┘ └───────────┘ └───────────┘ Интерфейс Базовый цикл Управление HTML-специфика Сборка DOM контракта и идентификаторы потомками и метаданные по метаданным
Ether определяет методы жизненного цикла объекта
Construct управляет жизненным циклом объекта
Quark добавляет систему хранения данных и работу с потомками
Element специализируется на визуальных компонентах
Space выполняет сбор метаданных и построкение DOM
Методы жизненного цикла
compile – предварительная обработка до генерации метаданных
shine – генерация метаданных
addinspace – добавление метаданных во вселенную
childs – обход потомков объекта и запуск для них create
create – полный жизненный цикл.
Жизненный цикл объекта
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ check │ → │ before │ → │ shine │ → │ addinspace│ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │ └───▶ [прекращение построения] ┌──────────────┐ ┌─────────────┐ ┌───────────┐ ┌───────────┐ │ checkchilds │ → │ beforechilds│ → │ childs │ → │ onchilds │ └──────────────┘ └─────────────┘ └───────────┘ └───────────┘ │ └───▶ [прекращение построения потомков] ┌───────────┐ │ on │ └───────────┘
Метаданные – это структурированное описание объекта в виде массива:
Пример:
[ 'type' => 'html', 'id' => 'O5', 'tagname' => 'div', 'class' => 'my-class', 'attr’ => ['data-role' => 'button', 'disabled' => true], 'caption' => 'Кнопка', 'parent' => 'O4' ]
⚡ Уникальные возможности архитектуры
Единая точка входа
Мне не хотелось использовать composer и подобные инструменты. Я задался целью минимизировать порог вхождения в фреймворк и сделать его максимально легким. Решением стала реализация фабричного абстрактного класса LTS, ставшего единой точкой входа.
Ключевые особенности:
Автоматически подключает все классы по мере их использования в приложении при помощи spl_autoload_register
Предоставляет фабричные методы для создания объектов: LTS::Form, LTS::Grid, LTS::Events и т.д. всего 41 класс.
Для подключения фреймворка необходимо подключить к проекту только один единственный файл: lotis.php:
include_once 'newlotis/lotis.php';
Единое пространство переменных
В LOTIS вы можете не только помещать последовательно клиентский и серверный код в один файл, но также использовать одни те же переменные в PHP и JavaScript. Это серьезно облегчает разработку:
$mygrid = LTS::Grid(); $mybutton = LTS::Button() ->capt('Переключить') ->click(<<<JS LTS(mygrid).mode('content'); // Вызов клиентского метода mode класса Grid $(mybutton).hide(); // Вызов jQuery-метода hide JS);
События без Ajax
Класс Events позволяет организовать клиент-серверное взаимодействие как единый поток:
// Определение события $myevents = LTS::Events() ->client('hello(name)', <<<JS alert(result); JS) ->server('hello', function ($args) { $name = $args['name']; return "Hello, {$name}!"; }); //Вызов события на клиенте: $nameinput = LTS::Input()->text('myname', 'Ваше имя'); LTS::Button() ->capt('Выполнить') ->click(<<<JS LTS(myevents).hello($(nameinput).val()); JS);
Сигналы
Для организации управления клиентскими объектами предусмотрены сигналы оповещения и подписки на них:
// Подписка: $myelement->signal('hideAll', <<<JS $(myelement).hide(); JS); // Оповещение: $mybutton->click(<<<JS LTS.signal('hideAll'); JS);
Клиентские методы
Функциональность любого объекта может быть расширена клиентскими методами.
$mydiv = LTS::Div()->method('hello', <<<JS alert('Hello, world!'); JS);
Глобальные переменные
Класс Vars предназначен для синхронизации данных при клиент-серверном взаимодействии без участия программиста.
// На сервере $vars = LTS::Vars(); $value = $vars->get('counter'); // На клиенте let counter = LTS.vars().get('counter'); LTS.vars().set('counter', ++counter).store();
Поставка без исходного кода
Архитектура CMA позволяет:
Сохранять только метаданные (не исходный код)
Загружать приложение из сериализованного массива
Увеличить скорость загрузки
Защитить авторские права
? Примеры использования классов фреймворка
Построение иерархии
Вы создаете объектную модель интерфейса, а не HTML-разметку. Нет необходимости думать о том, как это будет выглядеть в DOM — фреймворк позаботится об этом.
$content = LTS::Div()->columnbox(); $header = LTS::Div()->rowbox()->height('50px'); $header->addmany(LTS::Button()->capt('Button 1'), LTS::Button()->capt('Button 2')); $body = LTS::Div()->flex(); $body->css('background', '#000000')->css('color', '#ffffff')->capt('Body content …'); $footer = LTS::Div()->height('50px')->capt('Footer'); $content->addmany($header, $body, $footer); LTS::Space()->build($content);
Работа с формами
Логика формы и обработчики для кнопок находятся в одном месте.
// Создание формы $myform = LTS::Form(); // Добавление полей $myform->text('username', 'Логин') ->password('password', 'Пароль') ->email('email', 'Email'); // Добавление кнопок $myform->button('close', 'Закрыть')->click(<<<JS $(myform).hide() JS); $clickbutton = $myform->button('clickme', 'Нажми меня'); // Привязка события к кнопке $myform->event($clickbutton, <<<JS alert(result); JS) ->event($clickbutton, function($args) { return 'Привет, ' . $args['username']; });
Таблицы
Вы работаете с таблицами, как с объектами, определяя состав колонок, содержимое, сортировку строк. Вы можете динамически подкачивать, обновлять данные таблицы без перерисовки DOM.
// Создание базовой таблицы $table = LTS::DataTable() ->head(['name' => 'Наименование', 'date' => 'Дата']) ->data([ ['id' => 1, 'name' => 'Элемент 1', 'date' => '2023-01-01'], ['id' => 2, 'name' => 'Элемент 2', 'date' => '2023-02-01'] ]) ->hidden(['id']) // Скрываем поле ID ->sort('name'); // Сортировка по наименованию // Настройка обработки клика по строке $table->rowclick($form); // Добавление новых строк $table->append([ ['id' => 3, 'name' => 'Элемент 3', 'date' => '2023-03-01'] ]);
Все те же методы работы с таблицами доступны и на клиенте. На пример, заполнение новой строки в таблицу данными из формы ввода:
LTS(table).append(LTS(form).values());
Интеграция готовой верстки в проект
Что если клиент предоставляет готовую верстку с HTML, CSS и JavaScript? LOTIS ускоряет разработку и в этом случае, поскольку LOTIS обеспечивает позднее связывание событий и методов с объектами по их ID:
// Интегрируем HTML-код в проект $mysite = LTS::Html('mysite.html') // Подключаем внешний CSS $mysite->CSS()->add('styles.css'); // Подключаем внешние скрипты $mysite->JS()->add('scripts.js'); // Определяем события (функционал back-office) $events = LTS::Events() ->client('clickbutton1', <<<JS alert(result); JS) ->server('clickbutton1', function ($args) { return 'Hello, mysite!'; }); // Привязывает событие к клику по кнопке с id=button1 из HTML-шаблона $mysite->JS()->add(<<JS LTS(button1).listen('click', () => { LTS(events).clickbutton1(); }); JS); // Собираем проект $mysite->add($events); LTS::Space()->build($mysite);
Адаптивная CSS Grid-вёрстка
Вы описываете структуру интерфейса на уровне бизнес-логики, а не на уровне CSS-правил. Переключение режимов происходит через вызов методов, а не через манипуляции с классами.
// Создание сетки $grid = LTS::Grid(); // Определяем устройство $grid->deviceQuery('watch', '() => window.innerWidth <= 600 && window.innerHeight <= 600'); // Опрелеление режима dashboard $grid->setMode('dashboard') ->device('desktop') ->areas(["header header", "menu content", "bar bar"]) ->rows("60px 1fr auto") ->columns("200px 1fr") ->device('mobile') ->areas(["header", "content", "bar"]) ->rows("60px 1fr auto") ->columns("1fr") ->device('watch') ->areas(["header", "content"]) ->rows("50px 1fr") ->columns("1fr"); Переключаем сетку в клиентском приложении по кнопке: $btnDashboard = LTS::Button() ->capt('? Панель') ->click(<<<JS LTS(grid).mode('dashboard') JS);
Встроенная ORM
LOTIS позволяет использовать парадигму ООП при работе с MySql таблицами. Это повышает читаемость кода и обеспечивает гибкость в работе с базой данных.
// Подключение к базе: $db = LTS::MySql('mydb', 'localhost', 'user', 'pass'); // Определение таблицы: $users = $db->table('users'); $users->string('name', 100); $users->string('email', 255); $users->enum('status', ['active' => 'Активен', 'blocked' => 'Заблокирован']); $users->table('role_id', $roles); // внешний ключ $users->create(); // создать в БД // Вставка: $users->value('name', 'Иван') ->value('email', 'ivan@test.com') ->insert(); // Обновление: $users->value('status', 'blocked')->set(5); // по ID=5 // Массовое обновление: $users->value('status', 'active')->setall(['role_id' => 1]); // Выборка: $list = $users->all(['status' => 'active', 'ORDER' => '-created_at', 'LIMIT' => 10]); // Сложная выборка через QueryBuilder: $result = $users->query() ->where('status', 'active') ->where('name', '$иван') // LIKE '%иван%' ->leftJoin($roles, 'users.role_id = roles.id', 'roles') ->orderBy('-created_at') ->limit(10) ->all();
DataView – документ с несколькими табличными частями
Пример использования этого класса был разобран в моей предыдущей статье. Не скрою, логику работы с документами я позаимствовал из 1С: у документа может быть шапка, табличные части, форма редактирования, форма списка, форма для поиска и отбора, регистры, отвечающие за проведение и сохранение данных для построения отчетов.
Разговоры о том, чтобы в WEB появилась логика работы с документами, похожая на 1С, велись еще с начала 2000-х. В версии 1С:Предприятие 8.2, был реализован механизм управляемого приложения, отвечающий на этот запрос. Но до сих пор все, что связано с 1С, является далеко не классическим WEB, требующим больших вычислительных ресурсов. Поэтому, создать сущность, позволяющую работать с документами в логике 1С, было вполне логичным и нужным решением.
Другие классы
В распоряжении программиста, использующего LOTIS, имеется около сорока классов. Это позволяет строить WEB-приложения различной сложности.
DataView.php — Главная форма: единый интерфейс для работы с документами DataTable.php — Таблицы с сортировкой и отбором FilterForm.php — Форма фильтрации таблиц Stock.php — Централизованный учёт Space.php — Сборка UI Events.php — События через AJAX Form.php — Форма ввода: базовый контейнер для полей Input.php — Поле ввода текста LookupField.php — Поле поиска с выпадающим списком JS.php — Генерация JavaScript-логики на стороне PHP CSS.php — Вставка и управление CSS-правилами на лету Div.php — Универсальный контейнер Vars.php — Система глобальных переменных Grid.php — Сеточная вёрстка Dialog.php — Модальное окно MySql.php — Подключение к MySQL MySqlField.php — Представление поля таблицы MySqlTable.php — Работа с таблицей QueryBuilder.php — Построитель SQL-запросов MultiTable.php — Привязка подчиненных таблиц к главной Button.php — Кнопка с действиями Cells.php — Сетка элементов Columns.php — Гибкая колоночная вёрстка Accordion.php — Раскрывающиеся блоки Construct.php — Первый базовый класс DataSync.php — Синхронизация данных Debug.php — Инструменты отладки Element.php — Базовый класс всех UI-элементов Ether.php — Базовый интерфейс Html.php — Работа с HTML-тегами Lang.php — Поддержка мультиязычности LayerSlider.php — Переключение слоёв интерфейса Logger.php — Логирование действий и ошибок ProgressBar.php — Визуальный прогресс Quark.php — Мини-реализация объектной модели SimpleChart.php — Простые графики Span.php — Inline-контейнер Tabs.php — Вкладки Video.php — Встраивание видео
? Расширение фреймворка
Архитектура LOTIS позволяет расширять его функциональность пользовательскими классами. Это дает возможность разрабатывать собственные компоненты и использовать их в своих проектах.
Пример:
class Option extends LTS\Element { public function __construct($id = '') { parent::__construct($id); $this->tag('option'); } public function inic($val, $caption) { $this->attr('value', $val) ->capt($caption); return $this; } } class Select extends LTS\Element { public function __construct($id = '') { parent::__construct($id); $this->tag('select'); } public function option($val, $caption = null) { $option = new Option(); $option->inic($val, $caption ?? $val); $this->add($val, $option); return $this; } }
Кстати о Tailwind...
Поскольку любой объект LOTIS имеет встроенные методы addclass и removeclass, позволяющие устанавливать список CSS-классов непосредственно для конкретного объекта, то создание специализированного конструктора Tailwind так и просится к реализации. А пока используем методы "в лоб":
$card = LTS::Div() ->addclass('max-w-sm rounded overflow-hidden shadow-lg bg-white') ->addclass('transition duration-300 hover:shadow-xl');
?️ Что дальше?
LOTIS активно развивается, и в настоящее время Питерское издательство "Наука" готовит к публикации книгу по его применению в разработке WEB-приложений для бизнеса.
Следующие шаги:
Порт на Node.js для создания PWA
Интеграция с локальными базами данных
Поддержка TypeScript для статической проверки
Разработка системы тестов
Создание конфигуратора для визуальной разработки
Проектируя LOTIS, я хотел не просто упростить разработку на PHP, я искал новый уровень абстракции: не строить HTML, а создавать объектную модель бизнес-процессов. Чтобы эта модель стала живым интерфейсом - об этом заботится не программист, а фреймворк.
Буду рад получить обратную связь от коллег, кому фреймворк LOTIS помог в работе и кто хотел бы поделиться практическими соображениями по его улучшению или поучаствовать в его развитии.
include_once 'newlotis/lotis.php'; $div = LTS::Div()->capt("Hello from LOTIS!"); LTS::Space()->build($div);
Комментарии (3)

samako
20.04.2026 02:54Очень интересно. Лет 20-ть назад такое уже было, но сообщество отказалось от этого пути. Я и сам таким баловался по-молодости. Так что это, увы, не новая парадигма..
Как проекта для своего фриланса - это нормально, но для большой компании вряд ли подойдёт - по целому ряду причин.

init0
20.04.2026 02:54Фреймворк для быстрой разработки сложных бизнес-приложений
Я полагаю, что ваше представление о сложной бизнес-логике кардинально отличается от того, что принято таковой считать в отрасли.
x89377
Есть ссылка на пример ?