ЧТМ:Отладка/Cargo/Ликбез: различия между версиями
Нет описания правки |
|||
| Строка 715: | Строка 715: | ||
Списки в Cargo — это очень мощный и дружелюбный к пользователю инструмент. Как тебе такой способ организации множественных связей? | Списки в Cargo — это очень мощный и дружелюбный к пользователю инструмент. Как тебе такой способ организации множественных связей? | ||
</markdown> | |||
==== Списки через запятую ==== | |||
<markdown> | |||
А, теперь я тебя понял! Это очень меткое замечание. | |||
Ты говоришь о ситуации, когда в самом коде статьи мы пишем сырой текст без скобок: | |||
`|Продюсеры = Джордж Мартин, Джефф Линн, Пол Маккартни` | |||
И когда открываем статью (например, *Free as a Bird*), то в самом **инфобоксе** (карточке) видим этот список как обычный текст, а хотим, чтобы там автоматически появились ссылки на каждую личность. | |||
Для решения этой задачи в MediaWiki используется специальная функция **`#arraymap`**. Она по умолчанию поставляется вместе с установленным у тебя расширением **PageForms**. | |||
Эта функция умеет брать строку, разбивать её по разделителю (в нашем случае по запятой), делать что-то с каждым элементом (например, заворачивать его в квадратные скобки `[[...]]`) и склеивать обратно. | |||
--- | |||
### Как это настроить в твоем инфобоксе | |||
Зайди в редактирование своего визуального шаблона (например, `Шаблон:Альбом` или `Шаблон:Сингл`). | |||
Найди там строчку, где ты выводишь продюсеров: | |||
```wikitext | |||
| '''Продюсер(ы):''' || {{{Продюсеры|}}} | |||
``` | |||
И замени её на использование функции `#arraymap`: | |||
```wikitext | |||
| '''Продюсер(ы):''' || {{#arraymap:{{{Продюсеры|}}}|,|x|[[x]]|, }} | |||
``` | |||
--- | |||
### Как это работает (разбор по винтикам): | |||
Давай посмотрим, что делает функция `{{#arraymap: строка | разделитель_входа | переменная | формула | разделитель_выхода }}`: | |||
1. **`{{{Продюсеры|}}}`** — сырой текст, который ввел пользователь (например: `Джордж Мартин, Джефф Линн`). | |||
2. **`,`** — мы говорим машине: «Разрежь этот текст везде, где видишь запятую». | |||
3. **`x`** — это временное имя (переменная) для каждого кусочка. Сначала `x` будет равен «Джордж Мартин», потом — «Джефф Линн». | |||
4. **`[[x]]`** — формула. Мы говорим машине: «Возьми каждый кусочек `x` и заверни его в квадратные скобки». То есть преврати его в `[[Джордж Мартин]]` и `[[Джефф Линн]]`. | |||
5. **`, `** (запятая и пробел) — разделитель на выходе. Мы говорим: «А теперь склей всё это обратно, разделив запятой с пробелом». | |||
#### Огромный плюс `#arraymap`: | |||
Эта функция работает очень чисто. Даже если пользователь при вводе случайно поставит лишние пробелы вокруг запятых (например, `Джордж Мартин , Джефф Линн`), `#arraymap` автоматически **удалит (обрежет) все лишние пробелы** по краям имен перед тем, как делать ссылки. На выходе получатся идеальные ссылки: `[[Джордж Мартин]], [[Джефф Линн]]`. | |||
Попробуй вставить этот код в инфобокс — и ты увидишь, как обычный текст мгновенно превратится в набор красивых кликабельных ссылок! | |||
</markdown> | </markdown> | ||
Версия от 19:46, 26 мая 2026
Начало
Привет! Это отличная задача. Тот факт, что ты смог написать систему на Lua (пусть и с ИИ), говорит о том, что алгоритмическое мышление у тебя уже есть. И ты абсолютно прав: хранить данные в Lua-модулях или на подстраницах — это костыль.
Cargo — это расширение, которое буквально превращает твою вики в настоящую реляционную базу данных. Оно элегантнее, быстрее и правильнее.
Поскольку у тебя установлены еще и PageForms (PF) с PageSchema (PS), у тебя собран «золотой стандарт» для структурированных данных в MediaWiki.
Давай начнем ликбез. Я не буду сразу грузить тебя PageSchema (он нужен для автоматической генерации кода), сначала мы разберем «голый» Cargo, чтобы ты понял саму физику процесса.
Главный принцип Cargo: «Один шаблон — Одна таблица»
В Cargo всё крутится вокруг инфобоксов (шаблонов-карточек). Как это работает:
- Ты создаешь Шаблон (например,
Шаблон:Музыкант). - В коде этого шаблона ты объявляешь структуру таблицы (
#cargo_declare). - В коде этого же шаблона ты указываешь, как параметры, которые впишет пользователь, сохраняются в эту таблицу (
#cargo_store). - Когда кто-то создает статью «The Beatles» и вставляет туда этот шаблон, Cargo берет данные и тихонько кладет их в реальную таблицу MySQL.
- На любой другой странице ты можешь вывести эти данные с помощью SQL-подобного запроса (
#cargo_query).
Шаг 1. Создаем таблицу Исполнителей (Bands)
Представим, что мы делаем Шаблон:Исполнитель. Заходим в его редактирование и пишем следующий код:
<noinclude>
Управляет таблицей исполнителей.
{{#cargo_declare:
_table = Artists
|Name = String
|Type = String <!-- Группа или Соло -->
|Year_Formed = Integer
|Country = String
}}
</noinclude><includeonly><!--
БЛОК СОХРАНЕНИЯ ДАННЫХ В БАЗУ
-->{{#cargo_store:
_table = Artists
|Name = {{{Имя|{{PAGENAME}}}}}
|Type = {{{Тип|}}}
|Year_Formed = {{{Год_создания|}}}
|Country = {{{Страна|}}}
}}<!--
БЛОК ВИЗУАЛЬНОГО ОТОБРАЖЕНИЯ (Обычная вики-разметка карточки)
-->
{| class="infobox"
! colspan="2" | {{{Имя|{{PAGENAME}}}}}
|-
| Тип: || {{{Тип|}}}
|-
| Год создания: || {{{Год_создания|}}}
|-
| Страна: || {{{Страна|}}}
|}
</includeonly>
Что здесь произошло?
- Мы создали таблицу
Artistsпрямо в MySQL. - В тегах
<noinclude>мы задали структуру (типы данных: строка, число). - В тегах
<includeonly>с помощью#cargo_storeмы связали параметры шаблона (например,{{{Год_создания|}}}) с колонкой таблицы (Year_Formed).
ВАЖНЫЙ НЮАНС КАРГО:
Просто сохранить шаблон недостаточно! После сохранения зайди на страницу Шаблон:Исполнитель. В меню страницы (там, где «Править», «История» и т.д.) появится вкладка «Управление таблицей данных» (Manage table) или кнопка "Создать таблицу данных". Нужно нажать её и подтвердить создание таблицы. Всё, таблица в MySQL создана!
Шаг 2. Добавляем данные
Теперь создаем статью The Beatles и вставляем наш шаблон:
{{Исполнитель
|Имя = The Beatles
|Тип = Группа
|Год_создания = 1960
|Страна = Великобритания
}}
Битлз — британская рок-группа...
Когда ты сохранишь статью, MediaWiki не только нарисует табличку-инфобокс, но и запишет эти 4 поля в таблицу Artists в базе данных.
Шаг 3. Создаем таблицу Альбомов (Discography)
Чтобы база имела смысл, нужны связи. Создадим Шаблон:Альбом:
<noinclude>
{{#cargo_declare:
_table = Albums
|Title = String
|Artist = Page <!-- Тип Page означает, что тут должно быть название статьи -->
|Year_Release = Integer
|Album_Type = String <!-- Альбом, EP, Сингл -->
}}
</noinclude><includeonly>{{#cargo_store:
_table = Albums
|Title = {{{Название|{{PAGENAME}}}}}
|Artist = {{{Исполнитель|}}}
|Year_Release = {{{Год|}}}
|Album_Type = {{{Тип|Альбом}}}
}}
{| class="infobox"
! colspan="2" | {{{Название|{{PAGENAME}}}}}
|-
| Исполнитель: || [[{{{Исполнитель|}}}]]
|-
| Год выпуска: || {{{Год|}}}
|-
| Формат: || {{{Тип|Альбом}}}
|}
</includeonly>
Не забудь нажать "Создать таблицу" на странице этого шаблона!
Теперь создадим статью про альбом Abbey Road:
{{Альбом
|Название = Abbey Road
|Исполнитель = The Beatles
|Год = 1969
|Тип = Альбом
}}
Одиннадцатый студийный альбом...
Шаг 4. Магия Cargo (Делаем запросы)
Вот теперь начинается то, ради чего всё затевалось. Давай вернемся в статью The Beatles и выведем там их дискографию автоматически, чтобы не писать ее вручную!
В конец статьи The Beatles добавляем функцию #cargo_query:
== Дискография ==
{{#cargo_query:
|tables=Albums
|fields=Title, Year_Release, Album_Type
|where=Artist="The Beatles"
|format=table
|order by=Year_Release ASC
}}
Разбор запроса (это чистый SQL, но в синтаксисе MW):
tables=Albums— откуда берем данные.fields— какие колонки выводим.where— условие (где Исполнитель равен "The Beatles"). Кстати, чтобы не хардкодить имя группы, в шаблоне можно написатьwhere=Artist="{{PAGENAME}}".format=table— Cargo сам нарисует красивую вики-таблицу (форматов много: списки, графики, календари, таймлайны).order by— сортировка по году по возрастанию.
Всё! Как только кто-то добавит статью про новый сингл или альбом и укажет там Исполнитель = The Beatles, он автоматически появится в таблице на странице группы.
Причем здесь PageForms и PageSchema?
То, что мы написали выше — это фундамент.
Но пользователям (и тебе) будет лень вручную вписывать {{Альбом |Название=...}} в код страницы, легко ошибиться в букве или забыть параметр.
- PageForms позволяет создать визуальную формочку с кнопочками, выпадающими списками и календариками. Пользователь нажимает "Добавить альбом", заполняет красивую форму, а PageForms сам генерирует вики-текст
{{Альбом|...}}и сохраняет страницу. - PageSchema — это вообще космос. Она позволяет не писать код из Шагов 1 и 3 руками. Ты создаешь схему визуально, говоришь: "У меня будет таблица Артистов, такие-то поля". И PageSchema генерирует за тебя: 1) Шаблон, 2) Cargo declare/store, 3) Визуальную форму для PageForms.
Куда развивать нашу мини-БД дальше?
В Cargo есть крутая вещь — тип данных List (,) или List (;).
Например, если мы добавим в Шаблон:Альбом поле |Genres = List (,) of String, то при заполнении |Жанр = Рок, Блюз, Поп Cargo сам поймет, что это 3 разных значения, раскидает их под капотом по служебным таблицам и позволит тебе искать «все альбомы в жанре Рок», даже если рок был указан вторым или третьим.
Твое домашнее задание / эксперимент на пустой вики:
- Создать эти два шаблона (Исполнитель и Альбом).
- Обязательно "Создать таблицы" через интерфейс шаблонов.
- Создать 1 группу и 2-3 альбома.
- Попробовать вывести список альбомов на странице группы.
Если это получится и станет понятно — напиши, и я расскажу, как сюда элегантно прикрутить отдельных людей-музыкантов и их инструменты (связь "Многие-ко-Многим"), и как сгенерировать для этого удобную форму ввода!
Полировка
Поздравляю с успешным запуском! То, что всё заработало с первого раза — отличный результат.
Эти две проблемы — классические «грабли» новичков в Cargo, и решаются они очень просто. Давай разберем каждую по отдельности.
Проблема 1. Как сделать названия альбомов ссылками
В твоей таблице поле Title имеет тип String (строка). Для Cargo это просто текст, поэтому он выводит его без ссылки.
Чтобы получить ссылку на страницу альбома, нужно использовать встроенное «служебное» поле Cargo — _pageName. Оно создается автоматически для каждой строки любой таблицы и хранит в себе точное название страницы, на которой эти данные были сохранены. При выводе в формате format=table Cargo автоматически превращает его в кликабельную ссылку.
Чтобы заголовок столбца выглядел красиво, мы используем алиас (псевдоним) через знак равенства: _pageName=Название альбома.
Перепиши запрос дискографии на странице группы вот так:
{{#cargo_query:
|tables=Albums
|fields=_pageName=Название альбома, Year_Release=Год, Album_Type=Тип
|where=Artist="{{PAGENAME}}"
|format=table
|order by=Year_Release ASC
}}
Здесь мы заменили Title на _pageName=Название альбома. Теперь колонка будет называться «Название альбома», а внутри будут готовые ссылки.
Проблема 2. Как убрать запятую (или пробел) в годах
Когда ты объявил поле Year_Release как Integer (целое число), Cargo и MediaWiki начали относиться к нему как к математическому числу (вроде численности населения или цены). MediaWiki по умолчанию форматирует все большие числа, добавляя разделители тысяч в зависимости от языковых настроек вики (например, 1,969 или 1 969).
Для годов такое форматирование, конечно, не нужно. У тебя есть два пути решения:
Способ А. Быстрый «костыль» в самом запросе (без изменения структуры)
Поскольку Cargo работает на SQL, мы можем заставить базу данных превратить число в строку еще до того, как MediaWiki попытается его отформатировать. Для этого достаточно обернуть поле года в SQL-функцию CONCAT():
{{#cargo_query:
|tables=Albums
|fields=_pageName=Название альбома, CONCAT(Year_Release)=Год, Album_Type=Тип
|where=Artist="{{PAGENAME}}"
|format=table
|order by=Year_Release ASC
}}
CONCAT(Year_Release) превратит 1969 в обычный текст прямо в запросе, и разделители тысяч исчезнут. При этом сортировка order by=Year_Release ASC продолжит работать правильно, так как сортируется оригинальное число из базы.
Способ Б. Более правильный с точки зрения архитектуры БД
Задай себе вопрос: будешь ли ты когда-нибудь складывать годы выпуска альбомов или делить их? Очевидно, нет. Год в данном случае — это просто 4-значный текстовый идентификатор.
Поэтому правильнее изначально объявлять годы (и века) как тип String, а не Integer.
Поскольку все годы четырехзначные, они будут отлично и правильно сортироваться по алфавиту (1969 < 1970 < 2026), но при этом никогда не получат лишних запятых.
Если захочешь исправить это системно:
- Зайди в
Шаблон:Альбом. - В блоке
#cargo_declareзамениYear_Release = IntegerнаYear_Release = String. - Сохрани шаблон.
- Зайди на вкладку «Управление таблицей данных» на странице шаблона и нажми «Пересоздать данные» (Recreate data). Cargo полностью пересоздаст SQL-таблицу с новым типом данных и заново соберет туда информацию со всех страниц.
Как только разберешься с этими шагами, мы сможем перейти к более сложной и интересной теме: добавим в нашу систему музыкантов (людей) с годами их жизни и свяжем их с группами и инструментами. Готов двигаться дальше?
Ссылки
Это отличный и очень глубокий вопрос. Ты затронул фундаментальную проблему структурированных вики-данных — разрешение неоднозначностей (disambiguation).
Если у тебя есть статьи:
Please Please Me (альбом)Please Please Me (песня)
Их системные имена страниц (_pageName) будут именно такими — с уточнениями в скобках. Но в таблице дискографии на странице The Beatles выводить текст «Please Please Me (альбом)» некрасиво. Хочется, чтобы в таблице было написано просто Please Please Me, но ссылка вела на правильную страницу со скобками.
В MediaWiki для этого используются пайп-ссылки (ссылки с прямой чертой):
[[Please Please Me (альбом)|Please Please Me]].
В Cargo мы можем сконструировать такую ссылку динамически прямо во время запроса.
Как это сделать с помощью CONCAT
Мы возьмем реальное имя страницы (_pageName) как цель ссылки, а поле Title (где хранится просто красивое название без скобок) — как отображаемый текст. Склеим их с помощью SQL-функции CONCAT.
Но здесь есть главная ловушка MediaWiki: символ | (вертикальная черта) используется для разделения параметров в самом запросе {{#cargo_query: ... }}. Если мы напишем его внутри CONCAT, MediaWiki подумает, что это новый параметр Cargo, и всё сломается.
Поэтому вместо | мы используем стандартный вики-шаблон экранирования — {{!}}:
{{#cargo_query:
|tables=Albums
|fields=CONCAT('[[', _pageName, '{{!}}', Title, ']]') = Название альбома, CONCAT(Year_Release) = Год, Album_Type = Тип
|where=Artist="{{PAGENAME}}"
|format=table
|order by=Year_Release ASC
}}
Как это сработает под капотом:
- База данных найдет строку, где
_pageNameравенPlease Please Me (альбом), аTitleравенPlease Please Me. - Функция
CONCATсклеит их в строчку:[[Please Please Me (альбом)|Please Please Me]]. - MediaWiki получит этот текст, распарсит его как обычную вики-ссылку и выведет пользователю красивую ссылку: [Please Please Me], которая ведет куда нужно.
Таким образом, твоя база данных остается строгой и точной (у каждой сущности своя уникальная страница), а пользователь видит красивый, чистый интерфейс без технических скобок.
Воссоздание и замещение
Спокойно, всё в порядке! Никакие данные не стерлись и не пропадут. Давай разберемся с тем, как устроена «физика» Cargo, и ты сразу поймешь, почему всё именно так.
Главное правило Cargo: База данных — это лишь зеркало вики-страниц
В традиционных базах данных (вроде Access или чистой MySQL) ты действительно открываешь таблицу и правишь строчки руками.
В Cargo всё иначе. Единственным источником истины (Source of Truth) является сама вики-страница.
- Если ты хочешь изменить данные (например, исправить год альбома с 1969 на 1970) — ты просто заходишь в статью «Abbey Road», нажимаешь «Править», меняешь значение в шаблоне и сохраняешь страницу. Cargo в ту же секунду автоматически перепишет эту строчку в SQL-таблице. Кнопочки «редактировать таблицу напрямую» не существует, потому что это нарушило бы синхронизацию с вики-страницами.
- Если ты хочешь изменить структуру (поменять тип поля с
IntegerнаString) — ты меняешь код в{{#cargo_declare:...}}в шаблоне. Но MySQL не может просто так «на лету» изменить тип колонки, в которой уже лежат данные, поэтому таблицу нужно воссоздать (Recreate).
Что происходит при воссоздании и откуда взялась «таблица замещения»?
Когда ты нажимаешь кнопку «Воссоздать данные», Cargo понимает: «Ого, структура изменилась. Мне нужно удалить старую таблицу и создать новую. Но на живом сайте в этот момент пользователи могут читать статьи и искать музыку! Если я удалю таблицу, у них всё сломается».
Поэтому по умолчанию Cargo использует умный механизм Замещения (Replacement table):
- Твоя основная таблица
Albumsостается работать в режиме read-only (только для чтения), чтобы запросы на страницах продолжали показывать данные пользователям. - В фоне создается временная таблица замещения (например,
cargo__Albums__1) с новой структурой. - MediaWiki ставит в Очередь заданий (Job Queue) задачу: «Пройтись по всем страницам, где есть шаблон Альбом, прочитать их и записать данные во временную таблицу».
- Почему там сейчас 0 строк? На локальных или только что созданных вики очередь заданий MediaWiki не выполняется мгновенно. Она ждет, пока кто-нибудь походит по страницам, или пока администратор не запустит ее принудительно. Пока задания не выполнились, временная таблица пуста (0 строк).
- Как только очередь заданий выполнится и в таблице замещения станет столько же строк, сколько в оригинальной, активируется кнопка «Заместить» (Switch). Старая таблица удалится, а новая встанет на ее место. Без единой секунды сбоя для пользователей.
Важно: Если ты нажмешь «Заместить», пока там 0 строк, твоя рабочая таблица действительно станет пустой! (Хотя сами страницы альбомов не пострадают, Cargo просто временно "забудет" их данные, пока ты снова не сделаешь пересборку).
Как решить твою проблему прямо сейчас (Простой путь)
Поскольку твоя вики пока пустая и тестовая, тебе не нужно заботиться о пользователях и «бесшовном» обновлении. Тебе нужно, чтобы структура обновилась мгновенно.
Сделай следующее:
- Зайди в интерфейс воссоздания таблицы (где ты сейчас видишь статус замещения).
- Найди там кнопку «Удалить таблицу замещения» (Delete replacement table) и нажми её. Это безопасно. Твоя основная таблица выйдет из режима read-only.
- Снова нажми кнопку «Воссоздать данные» (Recreate data).
- ВНИМАНИЕ: На открывшейся странице подтверждения сними галочку с пункта «Воссоздать данные во временную таблицу замещения, сохраняя старую для запросов» (Recreate data into a replacement table...).
- Нажми «ОК».
Что произойдет:
Cargo мгновенно удалит старую таблицу, создаст новую с типом String и сразу же (минуя фоновую очередь заданий) за пару секунд заново просканирует твои тестовые страницы и запишет данные.
Теперь зайди в дискографию группы — запятая в годе выпуска должна исчезнуть, а ссылки на альбомы остаться! Получилось?
Альбомы и синглы
Это отличный архитектурный вопрос. Ты мыслишь как настоящий проектировщик баз данных. В реляционных БД это называется наследованием или полиморфизмом (когда у нас есть общий предок «Релиз», у которого есть частные случаи — «Альбом», «Сингл», «EP»).
В Cargo нет «вложенных» таблиц в привычном для ООП смысле, но есть три отличных пути решения этой задачи. Давай разберем их от простого к сложному, чтобы ты мог выбрать подходящий под твои задачи.
Вариант 1. Единый шаблон и одна таблица (Путь Википедии) — Рекомендуемый
Альбомы, EP и синглы на 90% состоят из одних и тех же полей: название, исполнитель, дата релиза, лейбл, жанры.
Вместо создания кучи сущностей ты создаешь одну таблицу Releases и один шаблон Шаблон:Релиз.
Внутри шаблона ты делаешь поле Тип_релиза (значения: Альбом, EP, Сингл, Бутлег).
- Как обрабатывать уникальные поля? Например, у сингла есть «Сторона А» и «Сторона Б», которых нет у альбома. Ты просто добавляешь эти поля в общую таблицу
Releases. В коде самого шаблона (в инфобоксе) ты можешь с помощью обычных вики-условий{{#if: ...}}скрывать эти поля, если тип релиза — «Альбом». В базе данных у альбомов эти поля просто останутся пустыми (NULL), что абсолютно нормально.
Плюсы: Легко настраивать, легко писать один общий запрос дискографии. Минусы: Шаблон инфобокса может стать немного перегруженным условиями «если это сингл, покажи это, иначе то».
Вариант 2. Разные шаблоны, но ОДНА общая таблица (через #cargo_attach)
Если ты хочешь, чтобы для альбома заполнялся Шаблон:Альбом (с красивым синим дизайном), а для сингла — Шаблон:Сингл (с зеленым дизайном и другими полями), но в базе данных они все складывались в одну общую таблицу Releases — Cargo умеет делать и это!
Для этого существует механизм прикрепления:
- Ты создаешь технический шаблон (например,
Шаблон:Базовый_релиз). В его<noinclude>ты объявляешь таблицуReleasesи перечисляешь вообще все поля, которые могут быть и у альбомов, и у синглов. - В
Шаблоне:Альбомты не используешь#cargo_declare. Вместо этого в<noinclude>ты пишешь функцию прикрепления:{{#cargo_attach: _table = Releases }}А в
<includeonly>спокойно отправляешь данные в эту таблицу через обычный#cargo_store. - В
Шаблоне:Синглты делаешь точно так же: прикрепляешься кReleasesи отправляешь туда данные сингла.
Плюсы: У тебя разные красивые формы ввода и шаблоны на вики, но в базе данных это одна чистая таблица. Запросы делать так же просто, как в Варианте 1. Минусы: Нужно аккуратно следить, чтобы все поля, которые используют «Альбом» и «Сингл», были заранее объявлены в базовом шаблоне.
Вариант 3. Разные таблицы + Объединенный запрос
Ты можешь пойти по пути создания физически разных таблиц в MySQL: Albums, Singles, EPs. У каждой таблицы будет свой собственный шаблон.
Но как тогда вывести их все вместе в один список на странице группы?
Для этого в Cargo есть специальная функция — #cargo_compound_query (составной запрос). Она позволяет выполнить несколько разных запросов к разным таблицам и «склеить» их результаты в одну визуальную таблицу на странице.
Выглядит это примерно так:
{{#cargo_compound_query:
|tables=Albums; fields=_pageName=Название, Year_Release=Год, 'Альбом'=Тип
|tables=Singles; fields=_pageName=Название, Year_Release=Год, 'Сингл'=Тип
|tables=EPs; fields=_pageName=Название, Year_Release=Год, 'EP'=Тип
|format=table
|order by=Year_Release ASC
}}
(Обрати внимание, мы используем конструкцию 'Альбом'=Тип — это способ прямо в запросе создать виртуальную колонку со статичным текстовым значением).
Плюсы: Полная свобода действий. Таблицы абсолютно независимы друг от друга.
Минусы: Написание составных запросов требует больше аккуратности. Если ты захочешь сделать сложный фильтр (например, «показать все релизы 1991 года во всех форматах»), составной запрос будет написать сложнее, чем простой where=Year_Release=1991 к одной общей таблице.
Что выбрать для учебного проекта?
Для твоей музыкальной базы данных я бы рекомендовал начать с Варианта 1 (одна таблица Releases, внутри которой есть поле Type). Это самый надежный способ, который убережет от ошибок на старте, и под который очень легко создавать формы ввода в PageForms.
Если же тебе очень хочется, чтобы у альбомов и синглов были принципиально разные карточки-инфобоксы, то Вариант 2 с функцией #cargo_attach — это самое красивое инженерное решение.
Реализация
Давай подробно реализуем этот сценарий. Это отличная, очень «взрослая» архитектурная практика. Мы разделим визуальную составляющую (разные шаблоны) и логическую (одна общая таблица в MySQL).
Наша цель — получить единую таблицу Releases (Релизы), в которой будут лежать и альбомы, и синглы.
При этом:
- Альбомы будут выводиться синим инфобоксом с указанием количества треков.
- Синглы будут выводиться зеленым инфобоксом с указанием Стороны А и Стороны Б.
Шаг 1. Создаем мастер-шаблон Шаблон:Базовый_релиз
Этот шаблон будет чисто техническим. Он не предназначен для вставки в статьи, его задача — просто держать структуру базы данных.
Создаем страницу Шаблон:Базовый_релиз и пишем туда:
<noinclude>
Этот шаблон объявляет структуру таблицы Releases. Не используйте его в статьях напрямую.
{{#cargo_declare:
_table = Releases
|Title = String
|Artist = Page
|Year = Integer
|Release_Type = String <!-- Альбом, Сингл, EP -->
|Track_Count = Integer <!-- Только для альбомов -->
|A_Side = String <!-- Только для синглов -->
|B_Side = String <!-- Только для синглов -->
}}
</noinclude>
После сохранения этой страницы:
Зайди в меню страницы шаблона («Еще» -> «Управление таблицей данных» или «Создать таблицу данных») и нажми кнопку создания. База данных создаст пустую таблицу Releases со всеми этими семью колонками.
Шаг 2. Создаем красивый Шаблон:Альбом
Теперь создаем визуальный шаблон для альбомов. Он будет прикрепляться к нашей таблице Releases.
Создаем страницу Шаблон:Альбом:
<noinclude>
Шаблон для оформления музыкальных альбомов.
{{#cargo_attach: _table = Releases }} <!-- ПРИКРЕПЛЯЕМСЯ К ОБЩЕЙ ТАБЛИЦЕ -->
</noinclude><includeonly>{{#cargo_store:
_table = Releases
|Title = {{{Название|{{PAGENAME}}}}}
|Artist = {{{Исполнитель|}}}
|Year = {{{Год|}}}
|Release_Type = Альбом <!-- Передаем тип жестко в коде шаблона -->
|Track_Count = {{{Количество_треков|}}}
}}<!--
СИНИЙ ВИЗУАЛЬНЫЙ ИНФОБОКС
-->
{| class="infobox" style="border: 1px solid #1e3a8a; background: #eff6ff; padding: 5px; float: right; width: 250px;"
! colspan="2" style="background:#1e3a8a; color:white; padding:5px;" | {{{Название|{{PAGENAME}}}}}
|-
| '''Исполнитель:''' || [[{{{Исполнитель}}}]]
|-
| '''Год выпуска:''' || {{{Год}}}
|-
| '''Тип релиза:''' || Альбом
|-
| '''Треков:''' || {{{Количество_треков}}}
|}
</includeonly>
Шаг 3. Создаем красивый Шаблон:Сингл
А теперь создаем шаблон для синглов со своим собственным набором параметров и зеленым оформлением.
Создаем страницу Шаблон:Сингл:
<noinclude>
Шаблон для оформления синглов.
{{#cargo_attach: _table = Releases }} <!-- ПРИКРЕПЛЯЕМСЯ К ТОЙ ЖЕ ТАБЛИЦЕ -->
</noinclude><includeonly>{{#cargo_store:
_table = Releases
|Title = {{{Название|{{PAGENAME}}}}}
|Artist = {{{Исполнитель|}}}
|Year = {{{Год|}}}
|Release_Type = Сингл <!-- Передаем тип жестко в коде шаблона -->
|A_Side = {{{Сторона_А|}}}
|B_Side = {{{Сторона_Б|}}}
}}<!--
ЗЕЛЕНЫЙ ВИЗУАЛЬНЫЙ ИНФОБОКС
-->
{| class="infobox" style="border: 1px solid #15803d; background: #f0fdf4; padding: 5px; float: right; width: 250px;"
! colspan="2" style="background:#15803d; color:white; padding:5px;" | {{{Название|{{PAGENAME}}}}}
|-
| '''Исполнитель:''' || [[{{{Исполнитель}}}]]
|-
| '''Год выпуска:''' || {{{Год}}}
|-
| '''Тип релиза:''' || Сингл
|-
| '''Сторона А:''' || ''{{{Сторона_А}}}''
|-
| '''Сторона Б:''' || ''{{{Сторона_Б}}}''
|}
</includeonly>
Шаг 4. Наполняем данными
Давай создадим две тестовые статьи, чтобы проверить, как они лягут в одну таблицу.
-
Создаем страницу Abbey Road (используем шаблон Альбома):
{{Альбом |Исполнитель = The Beatles |Год = 1969 |Количество_треков = 17 }} -
Создаем страницу Hey Jude (используем шаблон Сингла):
{{Сингл |Исполнитель = The Beatles |Год = 1968 |Сторона_А = Hey Jude |Сторона_Б = Revolution }}
Шаг 5. Делаем единый запрос дискографии
Зайдем обратно на страницу The Beatles и выведем их дискографию из общей таблицы Releases. Мы хотим увидеть и альбомы, и синглы в одном списке, отсортированном по годам.
Поскольку у нас общая таблица, запрос пишется невероятно просто:
== Полная дискография ==
{{#cargo_query:
|tables=Releases
|fields=
CONCAT('[[', _pageName, '{{!}}', Title, ']]') = Название,
Release_Type = Формат,
CONCAT(Year) = Год,
Track_Count = Треков,
A_Side = Сторона А,
B_Side = Сторона Б
|where=Artist="{{PAGENAME}}"
|order by=Year ASC
|format=table
}}
Как это отобразится на экране:
Cargo выведет одну красивую таблицу с 6 колонками:
| Название | Формат | Год | Треков | Сторона А | Сторона Б |
|---|---|---|---|---|---|
| Hey Jude | Сингл | 1968 | пусто | Hey Jude | Revolution |
| Abbey Road | Альбом | 1969 | 17 | пусто | пусто |
В чем магия:
- База данных вернула строки разных типов в одном запросе.
- Для сингла «Hey Jude» колонка «Треков» осталась пустой (
NULL), так как вШаблоне:Синглмы её не заполняли. - Для альбома «Abbey Road» колонки «Сторона А» и «Сторона Б» остались пустыми, так как в
Шаблоне:Альбомих нет.
Почему это супер-решение: Ты получил безупречную структуру данных под капотом. При этом авторы твоей вики могут использовать специализированные, удобные шаблоны (ведь синглы оформляются совсем не так, как альбомы), не задумываясь о том, как это устроено в базе данных.
Всё ли завелось на твоей вики? Каковы ощущения от работы с #cargo_attach?
Продюсер(ы)
Это классический сценарий. Очень часто над одним релизом работают несколько продюсеров (например, Джордж Мартин и Джефф Линн над антологией The Beatles), у песни может быть несколько авторов, а у альбома — несколько жанров.
В Cargo для этого существует встроенный тип данных — Списки (Lists). Но за внешней простотой списков скрывается умная реляционная механика. Давай разберем, как это реализовать, как это будет отображаться и как PageForms сделает ввод таких списков невероятно удобным.
Шаг 1. Объявляем список в Шаблоне:Базовый_релиз
Мы хотим добавить поле Producers (Продюсеры). Мы объявим его как список страниц, разделенный запятыми.
Зайди в Шаблон:Базовый_релиз и добавь это поле в #cargo_declare:
{{#cargo_declare:
_table = Releases
... (твои прошлые поля) ...
|Producers = List (,) of Page <!-- Объявляем список страниц с разделителем-запятой -->
}}
Что делает Cargo под капотом (это важно понимать):
В самой таблице Releases Cargo создаст поле Producers__full. Но вдобавок к этому он автоматически создаст в MySQL скрытую служебную таблицу с именем Releases__Producers.
В эту таблицу он будет складывать каждого продюсера отдельной строкой, связывая его с ID релиза. Благодаря этому база данных работает очень быстро и позволяет делать сложные реляционные запросы.
Шаг 2. Обновляем визуальные шаблоны (Шаблон:Альбом и Шаблон:Сингл)
Добавь сохранение этого поля в #cargo_store обоих шаблонов:
{{#cargo_store:
_table = Releases
...
|Producers = {{{Продюсеры|}}}
}}
И выведи его в коде карточки-инфобокса:
|-
| '''Продюсер(ы):''' || {{{Продюсеры|}}}
После изменения кода шаблонов не забудь зайти в Шаблон:Базовый_релиз и Воссоздать данные (без временной таблицы).
Шаг 3. Как заполнять данные в статьях?
Теперь на страницах релизов мы можем просто перечислять людей через запятую. Cargo сам поймет, что это разные люди, и превратит их в ссылки!
Например, на странице сингла Free as a Bird:
{{Сингл
|Исполнитель = The Beatles
|Год = 1995
|Сторона_А = Free as a Bird
|Сторона_Б = Christmas Time (Is Here Again)
|Продюсеры = Джордж Мартин, Джефф Линн, Пол Маккартни, Джордж Харрисон, Ринго Старр
}}
Шаг 4. Как выводить и искать данные?
1. Автоматическое отображение списков как ссылок
Если ты сделаешь обычный #cargo_query и выведешь поле Producers, тебе не нужно писать никаких сложных функций. Cargo сам знает, что это тип List of Page.
Он автоматически распарсит эту строку и выведет её на экран как красивый список вики-ссылок через запятую: «[[Джордж Мартин]], [[Джефф Линн]], [[Пол Маккартни]], [[Джордж Харрисон]], [[Ринго Старр]]».
2. Поиск по конкретному продюсеру (оператор HOLDS)
Если ты зайдешь на страницу продюсера Джефф Линн и захочешь вывести все релизы, которые он продюсировал, ты используешь уже знакомый нам оператор HOLDS:
== Продюсерские работы Джеффа Линна ==
{{#cargo_query:
|tables=Releases
|fields=CONCAT('[[', _pageName, '{{!}}', Title, ']]') = Релиз, Release_Type = Тип, CONCAT(Year) = Год
|where=Producers HOLDS "{{PAGENAME}}"
|format=table
}}
Этот запрос найдет сингл Free as a Bird, потому что Джефф Линн входит в список его продюсеров.
Шаг 5. Как это настроить в PageForms? (Ввод «токенами»)
Вводить имена через запятую вручную неудобно: можно ошибиться в букве, и связь сломается.
PageForms предлагает для списков потрясающий тип ввода — tokens (метки/теги). Это когда ты начинаешь писать имя, тебе предлагается вариант, ты нажимаешь Enter, и имя превращается в красивую синюю плашку-тег.
Давай обновим нашу Форму:Исполнитель (или форму для релизов, если ты её создашь).
В секцию полей добавь вот такую строчку для продюсеров:
|-
! Продюсер(ы):
| {{{field|Продюсеры|input type=tokens|values from table=People|attribute=_pageName}}}
Как это работает для пользователя:
- При редактировании релиза в поле «Продюсеры» пользователь видит пустую строку.
- Он начинает писать: Джо...
- Форма делает запрос к таблице
Peopleи предлагает: Джордж Мартин, Джон Дикон. - Пользователь кликает на Джордж Мартин — имя превращается в аккуратный тег-плашку с крестиком.
- Рядом он начинает писать: Дже... и выбирает Джефф Линн.
- При сохранении PageForms сам соберет их в аккуратную строку через запятую:
Джордж Мартин, Джефф Линни запишет в шаблон.
Списки в Cargo — это очень мощный и дружелюбный к пользователю инструмент. Как тебе такой способ организации множественных связей?
Списки через запятую
А, теперь я тебя понял! Это очень меткое замечание.
Ты говоришь о ситуации, когда в самом коде статьи мы пишем сырой текст без скобок:
|Продюсеры = Джордж Мартин, Джефф Линн, Пол Маккартни
И когда открываем статью (например, Free as a Bird), то в самом инфобоксе (карточке) видим этот список как обычный текст, а хотим, чтобы там автоматически появились ссылки на каждую личность.
Для решения этой задачи в MediaWiki используется специальная функция #arraymap. Она по умолчанию поставляется вместе с установленным у тебя расширением PageForms.
Эта функция умеет брать строку, разбивать её по разделителю (в нашем случае по запятой), делать что-то с каждым элементом (например, заворачивать его в квадратные скобки [[...]]) и склеивать обратно.
Как это настроить в твоем инфобоксе
Зайди в редактирование своего визуального шаблона (например, Шаблон:Альбом или Шаблон:Сингл).
Найди там строчку, где ты выводишь продюсеров:
| '''Продюсер(ы):''' || {{{Продюсеры|}}}
И замени её на использование функции #arraymap:
| '''Продюсер(ы):''' || {{#arraymap:{{{Продюсеры|}}}|,|x|[[x]]|, }}
Как это работает (разбор по винтикам):
Давай посмотрим, что делает функция {{#arraymap: строка | разделитель_входа | переменная | формула | разделитель_выхода }}:
{{{Продюсеры|}}}— сырой текст, который ввел пользователь (например:Джордж Мартин, Джефф Линн).,— мы говорим машине: «Разрежь этот текст везде, где видишь запятую».x— это временное имя (переменная) для каждого кусочка. Сначалаxбудет равен «Джордж Мартин», потом — «Джефф Линн».[[x]]— формула. Мы говорим машине: «Возьми каждый кусочекxи заверни его в квадратные скобки». То есть преврати его в[[Джордж Мартин]]и[[Джефф Линн]].,(запятая и пробел) — разделитель на выходе. Мы говорим: «А теперь склей всё это обратно, разделив запятой с пробелом».
Огромный плюс #arraymap:
Эта функция работает очень чисто. Даже если пользователь при вводе случайно поставит лишние пробелы вокруг запятых (например, Джордж Мартин , Джефф Линн), #arraymap автоматически удалит (обрежет) все лишние пробелы по краям имен перед тем, как делать ссылки. На выходе получатся идеальные ссылки: [[Джордж Мартин]], [[Джефф Линн]].
Попробуй вставить этот код в инфобокс — и ты увидишь, как обычный текст мгновенно превратится в набор красивых кликабельных ссылок!
Личности
Это отличный шаг вперед. Ты подошел к классическому вопросу проектирования баз данных: как связывать сущности «Один-ко-Многим» и «Многие-ко-Многим», и как быть, когда одна и та же сущность имеет разные роли.
В MediaWiki и Cargo эта проблема решается потрясающе изящно благодаря самой природе вики-страниц.
Часть 1. Freddie Mercury vs John Deacon (Роли сущностей)
Давай разделим понятия:
- Физический человек (личность: имя, фамилия, дата рождения) — за это отвечает
Шаблон:Личность(таблицаPeople). - Музыкальный исполнитель (творческая единица: дискография, страна, год начала карьеры) — за это отвечает
Шаблон:Исполнитель(таблицаArtists).
В обычной SQL-базе тебе пришлось бы городить сложные связи и таблицы-маппинги. В MediaWiki страница сама по себе является главным связующим звеном. На одну страницу можно повесить сколько угодно шаблонов.
Решение этой задачи выглядит так:
1. Создаем Шаблон:Личность (таблица People)
<noinclude>
{{#cargo_declare:
_table = People
|First_Name = String
|Last_Name = String
|Birth_Year = Integer
|Death_Year = Integer
}}
</noinclude><includeonly>{{#cargo_store:
_table = People
|First_Name = {{{Имя|}}}
|Last_Name = {{{Фамилия|}}}
|Birth_Year = {{{Год_рождения|}}}
|Death_Year = {{{Год_смерти|}}}
}}
{| class="infobox" style="border: 1px solid #aaa; background: #f9f9f9; padding: 5px; float: right; width: 250px;"
! colspan="2" style="background:#ddd;" | {{{Имя|}}} {{{Фамилия|}}}
|-
| Родился: || {{{Год_рождения|}}}
{{#if:{{{Год_смерти|}}}|
{{!}} Умер: {{!!}} {{{Год_смерти|}}}
}}
|}
</includeonly>
(Не забудь активировать таблицу на странице шаблона!)
2. Как теперь выглядят страницы людей?
-
Страница «Джон Дикон» (только человек):
{{Личность |Имя = Джон |Фамилия = Дикон |Год_рождения = 1951 }} Джон Дикон — британский музыкант, басист группы Queen...Результат в БД: Он попадает только в таблицу
People. В таблицеArtists(исполнители) его нет. -
Страница «Фредди Меркьюри» (и человек, и соло-артист):
{{Личность |Имя = Фредди |Фамилия = Меркьюри |Год_рождения = 1946 |Год_смерти = 1991 }} {{Исполнитель |Имя = Фредди Меркьюри |Тип = Соло |Год_создания = 1969 |Страна = Великобритания }} Фредди Меркьюри — британский певец, автор песен...Результат в БД: Запись о Фредди создается и в таблице
People, и в таблицеArtists. При этом у обеих записей в поле_pageNameбудет лежать абсолютно одинаковое значение:Фредди Меркьюри. Это их естественный уникальный ключ!
Часть 2. Как настроить связи между музыкантами и группами? (Списки в Cargo)
Теперь нам нужно указать, что Джон Дикон и Фредди Меркьюри были участниками группы Queen.
В реляционных базах данных для связи «Многие-ко-Многим» (в группе много музыкантов, у музыканта может быть много групп) создают промежуточную таблицу. Но Cargo предлагает гораздо более простой вики-ориентированный инструмент — Списки (List).
Давай обновим наш Шаблон:Исполнитель. Мы добавим туда поле Members (участники), которое будет хранить список страниц-ссылок на музыкантов.
1. Обновляем Шаблон:Исполнитель
Зайди в редактирование шаблона и измени #cargo_declare и #cargo_store:
<noinclude>
{{#cargo_declare:
_table = Artists
|Artist_Name = String
|Type = String
|Year_Formed = Integer
|Country = String
|Members = List (,) of Page <!-- ДОБАВИЛИ ПОЛЕ: Список страниц через запятую -->
}}
</noinclude><includeonly>{{#cargo_store:
_table = Artists
|Artist_Name = {{{Имя|{{PAGENAME}}}}}
|Type = {{{Тип|}}}
|Year_Formed = {{{Год_основания|}}}
|Country = {{{Страна|}}}
|Members = {{{Участники|}}} <!-- Передаем список в БД -->
}}
... (код карточки) ...
</includeonly>
Поскольку мы изменили структуру, не забудь зайти в управление таблицей Artists и Воссоздать данные (без временной таблицы).
2. Заполняем страницу группы Queen
Теперь отредактируем страницу Queen:
{{Исполнитель
|Имя = Queen
|Тип = Группа
|Год_основания = 1970
|Страна = Великобритания
|Участники = Фредди Меркьюри, Брайан Мэй, Роджер Тейлор, Джон Дикон
}}
Queen — британская рок-группа...
Шаг 3. Как делать запросы по спискам (Магия оператора HOLDS)
Теперь Cargo знает, кто в какой группе играет. Давай выведем эту информацию!
Задача А: Вывести состав группы на странице группы Queen
Добавим на страницу Queen автовывод состава в виде красивого списка со ссылками. Но мы хотим не просто список имен, а чтобы рядом с каждым участником выводился его год рождения из таблицы People!
Для этого мы сделаем JOIN (объединение таблиц Artists и People по имени страницы):
== Состав группы ==
{{#cargo_query:
|tables=Artists, People
|join on=Artists.Members=People._pageName
|fields=People._pageName=Имя, People.Birth_Year=Год рождения
|where=Artists._pageName="{{PAGENAME}}"
|format=table
}}
Cargo сам поймет, что в Artists.Members лежит список, «распакует» его, сопоставит каждого человека с таблицей People по имени страницы и выведет красивую таблицу участников с годами их рождения.
Задача Б: На странице музыканта вывести все группы, где он играл
Зайдем на страницу Джон Дикон и напишем запрос. Нам нужно найти все группы, в списке участников которых (Members) содержится (holds) его имя.
В Cargo для поиска элементов внутри списков используется специальный оператор HOLDS:
== Коллективы ==
Джон Дикон принимал участие в следующих группах:
{{#cargo_query:
|tables=Artists
|fields=_pageName=Название группы, Year_Formed=Год основания
|where=Members HOLDS "{{PAGENAME}}"
|format=table
}}
Этот запрос выдаст таблицу, где будет только Queen (потому что Джон Дикон указан в участниках Queen). Если ты создашь группу «The Opposition» (первая группа Дикона) и укажешь его там в участниках, она тоже автоматически появится в этом списке!
Попробуй реализовать эту схему на своей вики. Это покажет тебе всю мощь реляционных связей в связке с удобством текстовых страниц. Если возникнут вопросы по ходу — пиши!
Проблема Deep Purple
Ха, потрясающий пример! «Проблема Deep Purple» (или вообще любой классической рок-группы с бесконечной ротацией) — это любимый кошмар любого проектировщика баз данных.
Если мы просто храним плоский список Members = Фредди, Брайан... на странице группы, мы теряем кучу важнейшей информации:
- Время: Кто в какие годы играл? (Иэн Гиллан уходил и возвращался трижды!).
- Роль: Кто на чем играл? (Дэвид Ковердейл пел, а Гленн Хьюз пел и играл на басу в одном и том же составе).
- Составы: Как это группировать по официальным «маркам» (Mark I, Mark II и т.д.)?
Плоский список тут не справится. Нам нужна классическая связующая таблица (в реляционных БД она называется таблицей связей или Bridge Table).
Давай спроектируем ее.
Шаг 1. Создаем Шаблон:Участие (таблица Memberships)
Вместо того чтобы пытаться запихнуть всё в карточку группы, мы создадим отдельный маленький шаблон, который будет описывать один конкретный период работы конкретного музыканта в конкретной группе.
Этот шаблон мы назовем Шаблон:Участие:
<noinclude>
{{#cargo_declare:
_table = Memberships
|Band = Page <!-- Группа, например: Deep Purple -->
|Musician = Page <!-- Музыкант, например: Ian Gillan -->
|Role = String <!-- Инструмент/Роль: Вокал, Бас-гитара и т.д. -->
|Year_Start = Integer <!-- Год прихода -->
|Year_End = Integer <!-- Год ухода (пусто, если играет до сих пор) -->
|Lineup = String <!-- Номер состава, например: Mark II -->
}}
</noinclude><includeonly>{{#cargo_store:
_table = Memberships
|Band = {{{Группа|}}}
|Musician = {{{Музыкант|{{PAGENAME}}}}}
|Role = {{{Роль|}}}
|Year_Start = {{{С|}}}
|Year_End = {{{По|}}}
|Lineup = {{{Состав|}}}
}}<!--
Этот шаблон можно сделать невидимым на странице музыканта,
но мы можем выводить его красивой строчкой:
-->
* '''{{{Группа}}}''' ({{{Роль}}}) — с {{{С}}} {{#if:{{{По|}}}|по {{{По}}}|по н. в.}} ({{{Состав}}})
</includeonly>
Не забудь активировать таблицу на странице шаблона.
Шаг 2. Как теперь заполнять данные?
Мы можем разместить эти шаблоны прямо на странице музыканта. Давай зайдем на страницу Ian Gillan (Иэн Гиллан) и напишем:
{{Личность
|Имя = Иэн
|Фамилия = Гиллан
|Год_рождения = 1945
}}
Иэн Гиллан — британский рок-певец...
== Участие в группах ==
{{Участие |Группа=Deep Purple |Роль=Вокал |С=1969 |По=1973 |Состав=Mark II}}
{{Участие |Группа=Ian Gillan Band |Роль=Вокал |С=1975 |По=1978 |Состав=}}
{{Участие |Группа=Black Sabbath |Роль=Вокал |С=1982 |По=1984 |Состав=Born Again era}}
{{Участие |Группа=Deep Purple |Роль=Вокал |С=1984 |По=1989 |Состав=Mark II (reunion)}}
{{Участие |Группа=Deep Purple |Роль=Вокал |С=1992 |По=2026 |Состав=Mark VII / VIII / IX}}
Что произошло в базе данных?
Для Иэна Гиллана в таблице Memberships создалось 5 независимых строк. Каждая строка четко фиксирует группу, роль, годы начала и конца работы и даже название конкретного состава.
Шаг 3. И вот теперь начинается настоящая магия Cargo!
Теперь мы можем делать запросы, которые раньше в MediaWiki были абсолютно невозможны без сложнейшего программирования.
Запрос 1. Кто играл в Deep Purple в 1971 году?
Представь, что мы пишем статью про 1971 год в рок-музыке и хотим вывести состав Deep Purple именно на тот момент.
Мы делаем запрос к нашей таблице связей Memberships:
== Состав Deep Purple в 1971 году ==
{{#cargo_query:
|tables=Memberships
|fields=Musician=Музыкант, Role=Роль, Lineup=Состав
|where=Band="Deep Purple" AND Year_Start <= 1971 AND (Year_End >= 1971 OR Year_End IS NULL)
|format=table
}}
Как это работает:
База данных ищет все записи для Deep Purple, где год прихода музыканта (Year_Start) меньше или равен 1971, а год ухода (Year_End) больше или равен 1971 (или равен пустоте, если он до сих пор в группе).
Этот запрос вернет тебе идеальный список участников Mark II: Иэн Гиллан, Ричи Блэкмор, Роджер Гловер, Джон Лорд, Иэн Пейс. И никаких Гленнов Хьюзов или Дэвидов Ковердейлов, потому что они пришли позже!
Запрос 2. Собрать историю составов на странице Deep Purple
На странице самой группы Deep Purple мы можем вывести полную историческую хронологию участников:
== История участников ==
{{#cargo_query:
|tables=Memberships
|fields=Musician=Музыкант, Role=Роль, CONCAT(Year_Start)=Пришёл, CONCAT(Year_End)=Ушёл, Lineup=Состав
|where=Band="{{PAGENAME}}"
|order by=Year_Start ASC
|format=table
}}
Мы получим красивую, автоматически отсортированную по годам таблицу всех составов Deep Purple.
Поднимаемся на уровень PageForms (на будущее)
Ты спросишь: «Это здорово, но неужели мне придется заходить на страницу каждого музыканта и вручную писать эти шаблоны {{Участие}}?»
Вот тут на сцену и выходит PageForms. У них есть потрясающая функция: Шаблоны множественного ввода (Multiple-instance templates). Ты можешь настроить форму редактирования страницы группы Deep Purple так, чтобы на ней была динамическая таблица. Ты прямо на странице группы нажимаешь кнопку «Добавить участника», выбираешь из списка музыканта, пишешь его роль и годы, нажимаешь «Добавить еще одного» — и так далее.
При сохранении страницы группы PageForms сам создаст на ней 15 невидимых шаблонов {{Участие}}, которые Cargo послушно запишет в базу данных. Таким образом, ты сможешь управлять всеми составами прямо со страницы группы, а база данных внутри будет идеально структурирована.
Составы и хронология
Ты задал глубокий и абсолютно классический вопрос по архитектуре баз данных. Это то, на чем ломают зубы многие начинающие программисты.
Ты совершенно прав: если мы заставим Ричи Блэкмора завести три разные записи (1968–1969, 1969–1973, 1973–1975), чтобы просто прикрепить его к «Маркам», то машина решит, что он дважды уходил и возвращался. В реальности его участие было непрерывным, и плодить фантомные «уходы» — это плохой дизайн базы данных.
Как эту проблему решают профессионалы? Есть два красивых пути — Математический (чистый SQL) и Вики-ориентированный (списки Cargo).
Путь 1. Математический (Самый правильный)
В реляционных базах данных «Состав» (например, Mark II) — это не свойство конкретного музыканта. Состав — это просто промежуток времени в истории группы.
Давай посмотрим на сухие факты:
- Deep Purple Mark I существовал с 1968 по 1969 год.
- Deep Purple Mark II существовал с 1969 по 1973 год.
- Deep Purple Mark III существовал с 1973 по 1975 год.
Это объективные исторические периоды. А теперь посмотрим на музыкантов:
- Иэн Гиллан: играл с 1969 по 1973 год.
- Ричи Блэкмор: играл непрерывно с 1968 по 1975 год.
Если мы запишем Ричи Блэкмору всего одну непрерывную запись:
{{Участие |Группа=Deep Purple |Роль=Гитара |С=1968 |По=1975}} (без указания состава вообще!)
То мы всё равно сможем автоматически понять, что он был в составе Mark II. Как? С помощью математического пересечения интервалов в запросе!
Чтобы узнать, кто играл в Deep Purple в период Mark II (с 1969 по 1973), мы пишем такой запрос:
{{#cargo_query:
|tables=Memberships
|fields=Musician=Музыкант, Role=Роль
|where=Band="Deep Purple"
AND Year_Start <= 1973
AND (Year_End >= 1969 OR Year_End IS NULL)
|format=table
}}
Как машина это анализирует под капотом:
- Берём Гиллана (
1969–1973).- Он пришел не позже 1973? Да (1969 <= 1973).
- Он ушел не раньше 1969? Да (1973 >= 1969).
- Машина выдает Гиллана.
- Берём Блэкмора (
1968–1975).- Он пришел не позже 1973? Да (1968 <= 1973).
- Он ушел не раньше 1969? Да (1975 >= 1969).
- Машина выдает Блэкмора!
Мы нигде не писали Блэкмору текст «Mark II». Но благодаря простой математике база данных сама вычислила, что его непрерывный период работы перекрывает время существования состава Mark II.
Путь 2. Вики-ориентированный (Через списки Cargo)
Если тебе всё же очень хочется вручную подписывать составы прямо в шаблоне (например, для надежности или если рамки составов размыты), мы можем использовать тип данных List of String для поля Lineup (Состав).
В Cargo одно поле может хранить массив значений через запятую.
- В
#cargo_declareдля таблицыMembershipsмы объявляем поле составов как список:|Lineup = List (,) of String - На странице Ричи Блэкмора мы пишем одну непрерывную запись, но перечисляем составы через запятую:
{{Участие |Группа = Deep Purple |Роль = Гитара |С = 1968 |По = 1975 |Состав = Mark I, Mark II, Mark III, Mark IV }} - На странице Иэна Гиллана мы пишем его запись:
{{Участие |Группа = Deep Purple |Роль = Вокал |С = 1969 |По = 1973 |Состав = Mark II }}
Теперь, если мы захотим собрать состав Mark II, мы сделаем запрос с помощью оператора HOLDS (о котором мы говорили в прошлый раз):
{{#cargo_query:
|tables=Memberships
|fields=Musician=Музыкант, Role=Роль, CONCAT(Year_Start)=С, CONCAT(Year_End)=По
|where=Band="Deep Purple" AND Lineup HOLDS "Mark II"
|format=table
}}
Результат:
Машина найдет и Гиллана (у него в списке один элемент Mark II), и Блэкмора (у него в списке есть Mark II среди прочих). При этом у Блэкмора в таблице выведутся его честные непрерывные годы работы: 1968–1975.
Какой путь выбрать?
- Путь 1 (Математический) идеален, если ты хочешь построить строгую научную базу данных. Тебе достаточно один раз завести таблицу «История составов» (где будут лежать периоды существования Mark I, II, III), а все списки музыкантов будут рассчитываться автоматически.
- Путь 2 (Через списки) намного проще в реализации на старте. Он прощает ошибки в датах и позволяет быстро разметить составы вручную, не погружаясь в сложную логику интервалов.
Какой вариант тебе кажется более интуитивным для твоей вики?
Использование PageForms
Это действительно одна из лучших функций связки Cargo + PageForms. Она превращает редактирование сложных реляционных связей в детскую игру.
Давай пошагово разберем, как это устроено, как это написать ручками (ликбез же!), и как это в итоге работает для конечного пользователя.
Как это устроена архитектура PageForms
В PageForms всё строится на связке трех вещей:
- Шаблон (уже созданные нами
Шаблон:ИсполнительиШаблон:Участие). - Форма (страница в специальном пространстве имен
Форма:..., где мы пишем, как должен выглядеть интерфейс ввода: кнопочки, списки, поля). - Статья (страница «Deep Purple», которую пользователь редактирует через эту форму).
Когда мы используем многократный шаблон (Multiple-instance template), PageForms выводит на экран динамическую форму, где по нажатию кнопки «Добавить» JavaScript-скрипт мгновенно клонирует блок полей.
При сохранении статьи PageForms берет все эти заполненные блоки и аккуратно записывает их в статью в виде стопки шаблонов:
{{Исполнитель |Имя=Deep Purple |Тип=Группа |Год_основания=1968 }}
{{Участие |Музыкант=Иэн Гиллан |Роль=Вокал |С=1969 |По=1973 |Состав=Mark II}}
{{Участие |Музыкант=Дэвид Ковердейл |Роль=Вокал |С=1973 |По=1976 |Состав=Mark III, Mark IV}}
Каждый вызов {{Участие}} на странице группы Deep Purple — это отдельная строка в нашей MySQL-таблице Memberships.
Маленькая доработка Шаблона:Участие
Перед тем как создавать форму, давай сделаем наш Шаблон:Участие еще умнее.
Поскольку мы будем размещать эти шаблоны на странице группы (например, Deep Purple), нам не нужно заставлять пользователя каждый раз писать «Deep Purple» в поле «Группа». Шаблон должен сам догадываться, на какой странице он находится.
В коде Шаблона:Участие в блоке #cargo_store мы укажем:
|Band = {{{Группа|{{PAGENAME}}}}}
Если параметр Группа не задан в шаблоне явно, Cargo возьмет системное имя текущей страницы (то есть «Deep Purple»). Это очень удобно!
Шаг 1. Создаем Форму
Создаем новую страницу с названием Форма:Исполнитель (прямо так, с двоеточием, это системное пространство имен PageForms).
Пишем туда следующий код:
{{{info|create title=Создать исполнителя|edit title=Редактировать исполнителя}}}
{{{standard input|free text|rows=10|placeholder=Свободный текст (описание группы)}}}
<!-- СЕКЦИЯ 1. ОСНОВНЫЕ ДАННЫЕ ИСПОЛНИТЕЛЯ -->
{{{template|Исполнитель}}}
{| class="formtable"
! Название группы / Имя соло-артиста:
| {{{field|Имя|mandatory}}} <!-- обязательное поле -->
|-
! Тип:
| {{{field|Тип|input type=dropdown|values=Группа, Соло}}} <!-- выпадающий список -->
|-
! Год основания:
| {{{field|Год_основания}}}
|-
! Страна:
| {{{field|Страна|input type=combobox|values from table=Artists|attribute=Country}}} <!-- автозаполнение по уже введенным странам -->
|}
{{{end template}}}
<!-- СЕКЦИЯ 2. МНОГОКРАТНЫЙ ВВОД СОСТАВА -->
== Участники и составы ==
Опишите периоды участия музыкантов в этом коллективе.
{{{template|Участие|multiple|label=Добавить период участия}}}
{| class="formtable" style="background: #f5f5f5; border: 1px solid #ccc; padding: 10px; margin-bottom: 10px;"
! Музыкант (Личность):
| {{{field|Музыкант|input type=combobox|values from table=People|attribute=_pageName|mandatory}}}
<!-- Автозаполнение из базы данных People! -->
|-
! Инструмент / Роль:
| {{{field|Роль|placeholder=например: Вокал, Бас-гитара}}}
|-
! Период участия:
| С: {{{field|С|size=6}}} по: {{{field|По|size=6}}} (оставьте пустым, если играет сейчас)
|-
! Состав (Mark):
| {{{field|Состав|placeholder=например: Mark II}}}
|}
{{{end template}}}
Разбор магии этого кода:
{{{template|Участие|multiple|label=Добавить период участия}}}— вот она, ключевая команда. Словоmultipleговорит PageForms, что этот шаблон можно плодить кнопкой сколько угодно раз.label— текст на кнопке добавления.values from table=People|attribute=_pageName— это интеграция с Cargo! Поле «Музыкант» будет не просто текстовым полем. Когда ты начнешь вводить имя, форма сделает быстрый SQL-запрос к таблицеPeopleи предложит тебе выбрать из списка тех реальных людей (личности), которые уже созданы на вики. Никаких опечаток в именах!
Шаг 2. Как это выглядит для пользователя?
Теперь, когда форма готова, как её запустить?
В PageForms есть служебная страница Служебная:FormEdit. Ты можешь перейти по ссылке:
Ваш_Домен/wiki/Служебная:FormEdit/Исполнитель/Deep_Purple (или настроить кнопку «Править с помощью формы» на страницах категорий).
Откроется красивый веб-интерфейс:
- Сверху обычные поля: Имя, Тип (выпадающий список), Год, Страна.
- Ниже будет серая плашка «Участники и составы».
- Там будут поля: Музыкант, Роль, С, По, Состав.
- Под ними будет кнопка «Добавить период участия».
- Ты нажимаешь её — и мгновенно появляется вторая такая же серая плашка. Нажимаешь еще раз — третья.
- Каждую плашку можно удалить (маленький крестик в углу) или перетащить мышкой вверх-вниз, чтобы поменять их местами!
Шаг 3. Что запишется в код страницы?
Когда ты заполнишь форму для Deep Purple и нажмешь «Сохранить», PageForms автоматически сгенерирует следующий вики-текст в статье Deep Purple:
{{Исполнитель
|Имя = Deep Purple
|Тип = Группа
|Год_основания = 1968
|Страна = Великобритания
}}
{{Участие
|Музыкант = Род Эванс
|Роль = Вокал
|С = 1968
|По = 1969
|Состав = Mark I
}}
{{Участие
|Музыкант = Иэн Гиллан
|Роль = Вокал
|С = 1969
|По = 1973
|Состав = Mark II
}}
{{Участие
|Музыкант = Ричи Блэкмор
|Роль = Гитара
|С = 1968
|По = 1975
|Состав = Mark I, Mark II, Mark III
}}
Cargo увидит эти вызовы шаблонов, пропарсит их и добавит в таблицу Memberships три чистые строчки, связав их с группой Deep Purple (так как они лежат на странице Deep Purple, параметр Band заполнится автоматически).
Это абсолютно профессиональное, масштабируемое решение, которое позволяет держать базу данных в идеальном порядке силами даже тех редакторов вики, которые вообще не знают, что такое шаблоны и код!