Николай Ланец
15 апр. 2019 г., 5:33

Разворачиваем сайт с нуля на prisma-cms

Всем привет!

Недавно я писал заметку, что мастрячу новый конструктор сайтов на базе prisma-cms. Тогда же я обещался написать продолжение статьи, но так и не написал, потому что там предполагалось написать про новый текстовый редактор, заготовка которого у меня уже появилась. Но далее эксперимент перерос в разработку полноценного конструктора сайтов, так что задача по разработке отдельного текстового редактора (который будет входить в состав этого конструктора) пока отошла на потом. Но если кому интересно понимать масштабы начинаний, очень советую к прочтению: https://habr.com/ru/post/417637/

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

Установка сайта с нуля и использование нескольких корневых шаблонов.
Вот это довольно нестандартный и интересный момент. Мы привыкли в разработке сайтов, что роутинг (адресация), происходит на стороне сайта. Как это работает обычно? На сервер приходит запрос с каким-либо УРЛом, например http://domain/users. На сервере мы определяем, что был запрошен раздел пользователей и надо вывести пользователей. Далее, в зависимости от реализации, выполняется несколько итераций на получение данных из базы данных и набивки их в конечный HTML с помощью системы шаблонизации и отдача конечного кода браузеру. На примере того же MODX это происходит так: мы создаем раздел Пользователи, указываем ему, что это папка, задаем УРЛ users/, указываем шаблон (в котором прописана выборка пользователей), или сниппет, или чанк... В общем, весь этот процесс вам известен и реализаций может быть много (включая использование плагинов, подмены УРЛов и т.п.). Так или иначе, вы наверняка поняли о чем я. Так вот, несмотря на то, что эта задача довольно тривиальная, решить ее можно множеством способов, в связи с чем не редко, заходя в админку сайта, разработанного другим программистом, можно увидеть совсем не то, что ожидаешь (казалось бы банальные вещи реализуются таким усложненным способом, что просто диву даешься). А на самом деле в большинстве случаев все задачи по адресации сводятся к нескольким типовым задачам:
- Получение списка сущностей и вывод этих сущностей.
- Получение конечной сущности и вывод этой сущности.
Вот серьезно. Давайте возьмем практически любой интернет-магазин. Что там есть? Товар, Производитель, Категория, Новость/Публикация, Корзина. Ну и еще какие-нибудь мелочи. Все. Каждой этой сущности можно получить список (страница со списком) и карточку (конечная страница карточки). На страницах списков могут еще добавиться фильтры и постраничность. Практически везде так. Тем не менее, практически на каждом сайте реализации этого могут сильно отличаться. И далеко ходить не надо, и у меня использовались различные реализации, как на MODX, так и на призме. И вот это я и решил исправить, а именно, в конструктор сразу заложить механизмы роутинга и вывода списков и конечных карточек. Как это работает? Давайте рассмотрим этот механизм, установив сайт с нуля. (Напомню, что на windows это не тестировалось пока, работает только на linux, но если кто есть толковый на windows и есть желание запустить там, запускайте и шлите PR, скорее всего там все решится за счет прописать в нужных местах cross-env).

1. Клонируем себе заготовку сайта и устанавливаем зависимости (работаем в терминале).
git clone https://github.com/prisma-cms/boilerplate cd boilerplate yarn --ignore-engines
2. Деплоим схему.
Для этого можно использовать специально созданный api-сервер https://test-api.prisma-cms.com (чтобы не заморачиваться с установкой призмы локально).
APP_SECRET=MyStrongestSecret endpoint=https://test-api.prisma-cms.com/username/boilerplate-dev yarn deploy
Вместо username соответственно подставляете свой никнейм или какое-то слово, которое с малой вероятностью сконфликтует с чужим.
Если ОК, вы увидите типа:
Applying changes... 14.2s Your Prisma GraphQL database endpoint is live: HTTP: https://test-api.prisma-cms.com/username/boilerplate-dev WS: wss://test-api.prisma-cms.com/username/boilerplate-dev ⠋ Get schemaHandlerObject.flags { 'env-file': undefined, project: undefined } Schema file was created: src/schema/generated/prisma.graphql ⠋ Generating fragments for project app...src/schema/generated/api.graphql ✔ Fragments for project app written to src/schema/generated/api.fragments.js Done in 22.53s.
К слову, это будет ваш собственный API-сервер, куда вы можете сохранять данные и получать по API. Но не забывайте, что это тестовый сервер, он только для тестов и в любой момент может быть обнулен.

3. Запускаем локальный API-сервер.
https://test-api.prisma-cms.com - это внешний главный сервер, к которому у других пользователей не должно быть доступа (в случае с тест-сервером это не страшно ввиду заведомо бесполезной хранящейся на нем информации). Конечный же сайт работает с внешним API, которое обслуживается промежуточным API-сервером. Условно, есть backend-API и frontend-API, которое более ограниченное и отфильтрованное. Вот этот промежуточный сервер и запускается вами локально. Сам он данные не будет хранить, а только ретранслировать запросы на back-сервер, примешивая свою логику.
APP_SECRET=MyStrongestSecret endpoint=https://test-api.prisma-cms.com/username/boilerplate-dev yarn start-server
Если ОК, вы увидите
$ Sendmail=true node --experimental-modules src/server/ (node:12281) ExperimentalWarning: The ESM module loader is experimental. Server is running on http://localhost:4000
Соответственно, вы можете открыть у себя в браузере адрес http://localhost:4000 и увидеть интерфейс типа https://api.prisma-cms.com/, и можете там сразу запросы писать (если знаете как писать graphql-запросы), но данных там пока нет ни строчки, так что лучше все-таки запустить фронт.

4. Запускаем фронт.
Открываем эту же папку и выполняем
yarn start
Если ОК, у вас откроется браузер по адресу http://localhost:3000. Надо будет подождать некоторое время, пока сбилдятся скрипты, после чего вы увидите пустую страницу с кнопкой Signin. Это происходит, потому что сайт еще совсем пустой, в нем нет ни пользователей, ни шаблонов оформления, ничего (речь про базу данных). Наша задача здесь зарегистрировать пользователя и создать первый шаблон. Этот шаблон будет загружаться в дальнейшем по умолчанию.
Вот как это выглядит: https://youtu.be/YxoMhYPv96U

5. Прописываем роутинг.
В настоящий момент у нас еще только единый шаблон для всех страниц, нет отдельного вывода различных разделов. Если вы еще не знакомы с компонентами типа react-router, и вообще еще не знаете как строится УРЛизация средствами react на стороне браузера, советую к прочтению: https://habr.com/ru/post/329996/.
Если коротко, то мы создаем вложенную структуру специальных компонентов Switch и Route, которые будет срабатывать в зависимости от того, какая маска УРЛа совпала. Здесь используется декларативный подход, так что даже при наличии нескольких совпадений, кто первый попадет под маску, тот и будет выведен. В нашем случае мы зададим такую структуру:
{ "name": "EditorSwitch", "props": {}, "components": [ { "name": "EditorRoute", "props": { "path": "/", "exact": true }, "components": [] }, { "name": "EditorRoute", "props": { "exact": true, "path": "/users" }, "components": [] }, { "name": "EditorRoute", "props": { "exact": true, "path": "/users/:id" }, "components": [] } ] }
Здесь видно, что внутри компонента EditorSwitch у нас есть три компонента EditorRoute с параметрами path "/", "/users" и "/users/:id".

Обратите внимание на параметр exact. Он определяет точное вхождение (пусть даже и по меске). К примеру, если мы для адреса /users не укажем exact=true, то под него попадут любые вложенные УРЛы, в том числе /users/path1/path2/...

Так же обратите внимание на :id в свойстве "/users/:id". В данном случае УРЛ будет совпадать по маске, где вторая часть УРЛа будет передана в качестве параметра id. Это нам нужно, чтобы на конечной странице пользователя был запрошен пользователь по id из адреса. Таким же образом мы могли бы указать :username, если бы хотели, чтобы УРЛ строился не из id пользователя, а его username.

Вот видео: https://youtu.be/5xE-aRTtCko

6. Прописываем вывод пользователей.
Теперь, когда у нас прописана простейшая УРЛизация, мы можем формировать нужный нам вывод данных на отдельных страницах. К примеру, сейчас мы сделаем вывод списка пользователей на странице пользователей. https://youtu.be/DcLEK7r8y6U

Вот здесь давайте поподробней разберемся что же произошло.

- В редакторе УРЛ страницы в роутинге тоже учитывается, и когда мы перешли на страницу пользователей, Route с маской /users сработал и появилась область для ввода (куда можно разместить дочерние компоненты). Эти дочерние компоненты в дальнейшем будут выводиться при посещении страницы /users.

- В роутер мы закинули компонент Connector. Это универсальный компонент для формирования запросов на сервер. Мы из списка выбрали тип запроса usersConnection. Из названия понятно, что это запрос для получения списка пользователей. Но плюс к этому Connection - это специальный тип запросов, который еще и возвращает данные для формирования постраничности. Таким образом мы не только получим список пользователей, но и получим их с разбивкой по страницам, если их будет больше заданного лимита и они не поместятся на одной странице. Плюс к этому, когда мы закинули компонент Connector, он автоматически добавил еще несколько вложенных компонентов, а именно Filters (фильтры), ListView (область вывода) и Pagination (постраничность). Вот исходный код, где эти компоненты прописаны. Так же обратите внимание, что в редакторе у компонента Connector еще несколько редактируемых параметров, включая лимиты, сортировку и т.п.

- В область вывода мы закинули компонент UserLink, в результате чего мы сразу видим аватарку пользователя (просто пользователи еще не указали аватарки, поэтому серые круглешки только), имя пользователя и вообще это ссылка на страницу пользователя :) Здесь сразу всего так много, потому что компонент UserLink тоже комплексный, а не просто тег div или типа того. Но можно сформировать собственную ссылку на странице элемента, используя компоненты Link и Object field, вот так: https://youtu.be/SUctiCqJNis
В данном случае мы в Link прописали маску УРЛа /users/:id, то есть все ссылки выводимых пользователей будут сформированы с учетом их id. А в компоненте Object field мы указали имя выводимого поля из объекта User, в данном случае username. Вообще таким образом можно вывести любое скалярное поле, типа id, fullname, email и т.п.

К слову, откуда этот компонент берет данные конкретного объекта? Это отдельная магия, пришедшая к нам с новыми контекстами, появившимися в react@16.3. Вот хорошая подробная статья: https://blog.csssr.ru/2018/04/06/new-react-context.
В нашем случае мы видим совместную работу двух компонентов: ListView и Object field. Первый, получив список объектов, в цикле набивает их в вывод, формируя для каждого из них отдельный контекст и выводя в него дочерние компоненты. А Object field при отрисовке получает данные этого объекта из контекста и имеет возможность вывести. Вся прелесть этого заключается в том, что не важен уровень вложенности компонентов, данные объекта могут быть получены на любом дочернем уровне, что позволяет использовать вывод внутри других компонентов, используемых для оформления (например, сетки или ссылки).

7. Сделаем вывод карточки пользователя.
Здесь мы пойдем по простому пути, не выполняя отдельного оформления страницы, а просто закинув предустановленный компонент UserPage https://youtu.be/EByTeb7eFhs
Вжух! И все, карточка пользователя готова :) Можно редактировать профиль, загружать аватарку, писать пользователю в чат, отправлять эфир и т.п.

Собственно, вот эту концепцию и планирую сейчас развивать. Ее можно определить следующим образом:
1. Все на странице можно редактировать, сайт делается на фронте.
2. Постепенно будет формироваться набор полезных заготовок, которые можно использовать в разработке сайта, экономя время.
3. Отдельные части шаблонов можно сохранять в самостоятельные сниппеты для последующего использования в других шаблонах.
4. Каждый предустановленный элемент можно переопределить своим, при этом вывод поменяется без необходимости менять сохраненную схему (к примеру, переопределить UserLink или UserPage и прописать программно свой вывод).
5. Если нам чего-то не хватает в редакторе, без особых сложностей дописываем свои компоненты и передаем их CustomComponents.

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

Жду комментариев (вопросов и пожеланий). В дальнейшем я напишу как писать свои компоненты. Хотя сейчас это дизайнерски выглядит ооочень плохо, да и юзабилити надо прокачивать, но все таки у этого всего есть весьма серьезный потенциал.
развернул, сижу играю
Николай, всё круто! Правда есть моменты, где мне показалось как-то сумбурно описано. Разобраться конечно можно, но порог входа довольно высокий. Инструмент ты пишешь очень классный, т.к. есть большая потребность в этом. Однако, с наскоку тяжело разобраться в этом всём. Очень не хватает какой-то простой и понятной документации. Вообще, проекту не хватает доки (en, ru) и дизайна (как уже говорили не раз).

P.S. И было бы совсем круто, если бы как-то блоки с кодом были внятно отделены от текста. =)
Павел, привет!
Сорри за долгий ответ. До последнего времени не было понятно каков он будет. Но частичный ответ появился.
1. Теперь призму в действии можно опробовать прям здесь. https://prisma-cms.com/topics/prisma-cms@3.0-svoy-sobstvennyy-sayt-na-poddomene-dlya-kazhdogo..html
2. Вот здесь я дописал более компактный скрипт для быстрой установки: /topics/razvorachivaem-graphcool-prisma-na-golom-zheleze-2750.html
По сути, если не нужна полная версия движка, включающая базу данных в докере, а только тестовый сайт нужен с данными по API с тестового сервера или prisma-cms.com, то достаточно наличия node-js и yarn.

Добавить комментарий