вторник, 2 октября 2012 г.

Fluid или изучаем новое шаблонирование в Typo3


Так что же такое Fluid? 100%, что многие, наверняка, слышали это словечко, но до конца не понимали, о чем идет речь. Не знаю почему, но чаще всего люди думают, что fluid это нечто необходимое для написания современных экстеншенов (плагинов) для Typo3. Но это не так. Это нечто вовсе не флюид, а - extbase. А fluid это, всего-навсего, новый механизм шаблонирования или шаблонизатор, как я часто выражаюсь, такой же как automaketemplate и templavoila. Расширения написанные с помощью extbase используют fluid для шаблонизации вывода.

Чем же хорош флюид и почему перейдя на него я не хочу ничего другого? Отвечаю, флюид лишен всех тех недостатков, которыми обладают automaketemplate и templavoila. Я бы сказал, что fluid это симбиоз этих двух шаблонизаторов, который, к тому же, наделён и множеством дополнительных возможностей о которых я как-нибудь расскажу.

Чтобы лучше понять, что я имею ввиду, рассмотрим пример, который будет полезен не только тем кому интересен fluid, но и тем кто установил Typo3 и не знает что дальше с ней делать.

Итак, превратим сверстанные html файлы в шаблоны Typo3 при помощи fluid.

1. Создаем необходимую структуру директорий. 

Считаем, что мы только что установили Typo3 и папка fileadmin - пуста. Переходим в fileadmin и создаем в ней следующие директории:
templates, а внутри templates поддиректории - css, js, images - это стандартное хранилище тех файлов, которые создал верстальщик. Если есть желание или необходимость то сами html файлы можно скопировать в корень fileadmin\templates\. Но я этого не делаю, чтобы они не мозолили глаза. 
Теперь создаем более интересные директории, которые напрямую связаны с работой fluid:
layouts, pages и partials. 

Объясню назначение каждой из них. 

Папка layouts.

Каким бы ни был дизайн наших страниц (двух-, трех-колоночный или еще что-то) у всех страниц есть общие блоки - header, content, footer и т.д. После того как общие блоки выявлены, на базе наших сверстанных файлов в папке layouts создаем 1 файл - default.html, который может выглядеть примерно так:
<header>
<div id="header" class="header-bg">
<div class="wrap">
<!-- Здесь будет лого --><f:cObject typoscriptObjectPath="lib.logo" />
<!-- Место для меню сайта --><f:cObject typoscriptObjectPath="lib.sitemenu" />
</div>
</div>
</header>
<!--end header-->
    <div id="main">
<div class="wrap">
<!-- Бредкрамб будет тут --><f:cObject typoscriptObjectPath="lib.breadcrumb" />
<div class="clear"></div>
<!-- Область контента --><f:render section="Body" />
</div>
</div>
<!-- end content -->
<footer>
<!-- Копирайт --><f:render partial="site_footer" />
</footer>  
<!--end footer-->
Как видим, содержимое данного файла представляет собой кастрированную версию исходного html-файла. Идея такой кастрации такова, что вместо динамического контента мы подставляем либо имена  объектов Typo3 (например, lib.sitemenu или lib.copyright), либо имена html-секций (например, section="Body"), которые описываются файлами из директории pages, либо имена html-партиалсов (например, partial="site_footer"),  которые описываются файлами из директории partials. Я думаю, что истинные любители Typo3 глядя на этот маленький кусочек html-шаблона уже поняли всю прелесть fluid.

Папка pages

Т.к. наш layout содержит секцию Body - <f:render section="Body" />, то мы должны описать её.
Однако тут не все так просто. По-сути, данная секция отвечает за отображение основного контента сайта, а контент как известно бывает разный. Рассмотрим случай когда контент размещается на странице в одной или двух колонках. Для этого в данной директории создаем 2 файла: 1Columns.html и 2Columns.html.

Содержимое 1Columns.html:
<f:layout name="default" />
<f:section name="Body">
<div class="col">
{content -> f:format.raw()}
</div>
<!--end col-->
</f:section> 
Содержимое 2Columns.html:
<f:layout name="default" />
<f:section name="Body">
<div class="left-col">
{content -> f:format.raw()}
</div>
<!--end left-col-->
<div class="right-col">
{content_right -> f:format.raw()}
</div>
<!--end right-col-->
</f:section>
Что означает {content -> f:format.raw()} я опишу ниже. А сейчас хочу обратить внимание на некоторые очень важные вещи. Все настройки fluid чувствительны к регистру! Если в файле layouts\default.html написано section="Body", то в файлах 1Columns.html и 2Columns.html имя этой секции тоже должно начинаться с большой буквы. Следующий важный момент. Обратите внимание на первую строку для 1Columns.html и 2Columns.html. Она одинаковая для обоих файлов и может даже показаться магической - почему именно layout name="default"? Да потому, что слово "default" не что иное как своеобразная ссылка на файл layouts\default.html.

Папка partials

Напомню, что наш файл шаблона - layouts\default.html вместо футера содержит ссылку на html-фрагмент (partials) при помощи тега - <f:render partial="site_footer" />. Как вы уже догадались - партиалы описываются здесь, в этой директории. Но происходит это несколько иначе чем с секциями (см. выше). Достаточно создать файл partials\site_footer.html и его содержимое сразу же попадет в основной шаблон. Вот пример партиала:
<div class="copyright">
        <div class="f-left">
          <address>
          <f:cObject typoscriptObjectPath="lib.copyright" />
          </address>
        </div>
        <div class="f-right">
          <address>
          <f:cObject typoscriptObjectPath="lib.company" />
          </address>
        </div>
</div>
Как видим, партиалы это тоже не статический фрагмент html-кода, и он также может содержать ссылки на объекты Typo3, которые создаются с помощью typoscript.

Ну вот и все, наша страница разбита на элементарные составляющие или иными словами она - размэплена. Первая часть марлизонского балета закончена - html-шаблон готов.

2. Настройка backend_layout  

Для того, чтобы администратору или редактору в админке (BE) было проще и удобнее редактировать контент, в Typo3 придумали backend_layout-ы. Т.к. мы имеем дело с двумя вариантами дизайна (в 1 или в 2 колонки), то подготовим соответственно две записи backend_layout в системной папке - SF Global layout, которую я создал ранее.


Режим редактирования backend_layout имеет 2 режима работы - графический и текстовый:


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

Так или иначе, но в итоге, для одно-колоночного дизайна поле Config должно содержать:
backend_layout {
colCount = 1
rowCount = 1
rows {
1 {
columns {
1 {
name = Main content
colPos = 0
}
}
}
}
}
а для двух-колоночного дизайна:
backend_layout {
colCount = 3
rowCount = 1
rows {
1 {
columns {
1 {
name = Main content
colspan = 2
colPos = 0
}
2 {
name = Right content
colPos = 1
}
}
}
}
Если вы внимательно посмотрите на последнее определение бэкенд-лайаута, то можете удивиться и задать себе вопрос - зачем же нужно ставить colCount = 3, если колонок то всего две? Но посмотрите ниже и вы увидите, что для первой (левой) колонки имеется параметр - colspan = 2 (прям как в html-таблицах). Таким образом, мы получаем 2 колонки как и планировали, но ширина левой будет в 2 раза шире, чем ширина правой колонки. Я это сделал для того, чтобы бэкенд мне сильнее напоминал внешний вид FE-страниц.


После создания каждого backend_layout записывайте сразу id каждой новой записи, т.к. это нам скоро пригодится.

Чтобы присвоить странице любой из этих лайаутов необходимо перейти в свойства страницы и на закладке Appearence найти параметр Backend Layout (this page only) и выбрать из выпадающего списка нужную строку.


В результате, двух-колоночная страница  в BE примет следующий вид:


Я думаю, что вы согласитесь с тем, что данный вид более приятен и реалистичен чем те 4 колонки, которые нам даются по-умолчанию:



3. Создаем Typo3-шаблон

Теперь, когда практически все готово, остался один-единственный вопрос - как контент из левой и правой колонки редактора попадает соответственно в левую и правую колонку html-шаблона?

Для этого нужно создать еще один шаблон, так называемый - Typo3-шаблон сайта. Ни один сайт на Typo3 не может без него обойтись! Если такого шаблона нет, то ваш сайт будет выглядеть примерно так:


Чтобы создать Typo3-шаблон для нашего сайта мы должны перейти в режим Template, выбрать корневую страницу дерева и нажать кнопку Create template...


После того, как Typo3-шаблон создан мы должны увидеть следующий экран:


Теперь в разделах Constants и Setup мы можем вводить наш typoscript. У меня эти разделы, как правило содержат по одной строке. 
Для Constants:
<INCLUDE_TYPOSCRIPT: source="FILE: fileadmin/templates/ts/constants.ts">
Для Setup:
<INCLUDE_TYPOSCRIPT: source="FILE: fileadmin/templates/ts/setup.ts"> 

Существует много причин почему лучше делать именно так, но мне с текстовыми файлами constants.ts и setup.ts работать в обычном текстовом редакторе, например notepad++, намного приятнее. Естественно, что constants.ts и setup.ts нужно создать в папке fileadmin/templates/ts/ самостоятельно, а не ждать чуда от typo3. Но это уже на усмотрение каждого.

Открываем файл constants.ts и создаем две константы с помощью таких строк:
domain = http://www.domain.com/
PathToFiles = fileadmin/templates/

И наконец, добавляем typoscript в setup.ts - самый главный конфигурационный файл сайта.
Я приведу минимум строк необходимых для начала работы.
page = PAGE
page {
typeNum = 0
config {
baseURL = {$domain}
        }
includeCSS {
file = {$PathToFiles}css/style.css
}
includeJSFooter {
file10 = {$PathToFiles}js/jquery-1.7.1.min.js
file20 = {$PathToFiles}js/script.js
}
        # Настройки Fluid
10 = FLUIDTEMPLATE
10 {
  # Указываем пути к layout-ам и partial-ам
partialRootPath = {$PathToFiles}partials/
layoutRootPath = {$PathToFiles}layouts/
  # Делаем привязку backend_layout к html-шаблонам
file.stdWrap.cObject = CASE
file.stdWrap.cObject {
key.data = levelfield:-1, backend_layout_next_level, slide
key.override.field = backend_layout
# Двух-колоночный html-шаблон привязываем к backend_layout с id=1
1 = TEXT
1.value = {$PathToFiles}pages/2Columns.html # Одно-колоночный html-шаблон привязываем к backend_layout с id=2
2 = TEXT
2.value = {$PathToFiles}pages/1Columns.html # Установка дефолтного html-шаблона (двух-колоночный)
default = TEXT
default.value = {$PathToFiles}pages/2Columns.html
}
  # Загоняем контент из колонок в переменные
variables {  
  # Содержимой главной колонки (её номер = 0) соответствует переменная - content
content < styles.content.get
content {
select.where = colPos = 0
wrap = <!--TYPO3SEARCH_begin-->|<!--TYPO3SEARCH_end-->
}
   # Содержимой правой колонки (её номер = 1) соответствует переменная - content_right
content_right < styles.content.get
content_right {
select.where = colPos = 1
wrap = <!--TYPO3SEARCH_begin-->|<!--TYPO3SEARCH_end-->
}
   # Содержимой левой колонки (её номер = 2) соответствует переменная - content_left (добавил ее на всякий случай)
content_left < styles.content.get
  content_gray {
  select.where = colPos = 2
  wrap = <!--TYPO3SEARCH_begin-->|<!--TYPO3SEARCH_end-->
}
}
}
}

Помните содержимое файлов 1Columns.html и 2Columns.html? Оба файла содержат такое вхождение: {content -> f:format.raw()}, а файл 2Columns.html еще и {content_right -> f:format.raw()}. Только что мы указали откуда будет браться контент для данных переменных.

И таких переменных может быть сколько угодно. По умолчанию Typo3 позволяет иметь 4 столбца для ввода контента, но на самом деле их может быть сколько угодно. Мой личный рекорд - 16. И только благодаря backend_layout-ам эти 16 колонок выглядят как мозайка, а не как полосатый матрац.

Теперь по поводу непонятной конструкции f:format.raw() , которая могла некоторых удивить. Это так называемый флюидовский ViewHelper (помощник, что ли). Привыкайте к данному термину, т.к. fluid имеет большое количество таких помощников. Именно этот - format.raw, выводит контент без всяких преобразований, как есть. Подробнее о всех ViewHelper-сах можно почитать тут.

Ну вот и все. Если что-то забыл - допишу, если где-то ошибся - поправьте меня. И переходите на fluid, не пожалеете!!!

пятница, 28 сентября 2012 г.

Совет по отладке плагинов (extension) для Typo3


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

Одним из способов отладки, как известно со времен египетских пирамид, является отладочная печать.

Для тех, кто не очень хорошо знаком с PHP, приведу несколько строк кода, которые помогают при "разборе полетов", а точнее - выводят в текстовый файл все переменные PHP и их значения.

Вот эти строки:

ob_start();
var_dump(get_defined_vars());
$all_vars = ob_get_clean();
// Тут дан абсолютный путь к файлу vars.txt
$f = fopen("/var/www/client1/web/fileadmin/debug/vars.txt", "w");
fwrite($f, $all_vars);
fclose($f); 
Смело вставляйте эти строки  в любое место php-файлов Typo3-плагина и вы увидите полную картину всего происходящего в данной точке.

среда, 25 июля 2012 г.

Управление списком записей в режиме New record

Что-то давненько я ничего не писал...

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



Так вот, все очень просто. За каждой такой записью стоит какая-то таблица из БД. Строке News соответствует таблица tt_news, News category - tt_news_cat и т.д. Узнав имя такой таблицы направляемся в свойства нужной нам страницы (на которой нужно заблокировать ввод) и открываем закладку Ресурсы (Resources). В разделе Page TSConfig вводим примерно такие строки:
mod.web_list {
  deniedNewTables = tx_fitness_workout_equipment,tx_fitness_workout_bodypart,tx_fitness_fav_activity,tx_fitness_activitylog 
}



После чего из списка пропадают строки, которые позволяли вводить контент в наши таблицы с помощью админки.

Теперь несколько другая техника.

Допустим, в системную папку можно вводить только Typo3 шаблоны и Backend layouts, а все остальное нужно запретить. Тогда в Page TSConfig папки нужно вписать следующее:
mod {
web_list {
allowedNewTables := addToList(sys_template,backend_layout)
         }
}

sys_template и backend_layout это таблицы в которых хранятся соответствующие записи.

Теперь список доступных записей такой:




суббота, 18 февраля 2012 г.

Включаем и выключаем deprecation log


Надеюсь многие знают или по крайней мере видели, что в директории typo3conf, в которой находятся главные настройки Typo3 (localconf.php), лежат файлы типа deprecation_8935860f39.log

Для тех кто еще не в курсе, скажу, что в данном файле содержится информация об устаревших конструкциях typoscript и методах расширений (плагинов). Таким образом deprecation.log предупреждает нас о том, что и когда перестанет работать в последующих версиях Typo3 и дает возможность заранее подстелить соломки, чтобы потом нам было не так больно. Кроме того, здесь мы можем получить полезную информацию и о том, чем можно заменить устаревшие методы.

Загляните в такой файл и вы увидите, что-то вроде:

11-03-11 09:11: Using gpvar in TypoScript getText is deprecated since TYPO3 4.3 - Use gp instead of gpvar.
03-01-12 12:56: Usage of $ICON_TYPES is deprecated since 4.4. The extTables.php entry $ICON_TYPES['quick_shop'] = '../typo3conf/ext/quick_shop/ext_icon.gif'; should be replaced with t3lib_SpriteManager::addTcaTypeIcon('pages', 'contains-quick_shop', '../typo3conf/ext/quick_shop/ext_icon.gif'); instead.
20-01-12 23:03: TCA contains a deprecated definition using "newRecordLinkPosition"



Как видно, это обычный текстовый файл. Удаление этого файла к фатальным последствиям не приводит. Поэтому, как только deprecation.log стал довольно объемным его можно смело удалять. Но как сделать так, чтобы отключить логирование устаревших методов в Typo3?

Все очень просто! Идем в файл typo3conf\localconf.php и добавляем такую строку:

$TYPO3_CONF_VARS['SYS']['enableDeprecationLog'] = '0';

Если логирование нужно будет включить вновь - просто замените '0' на '1'.

P.S. Не забывайте почистить кеш системы, путем удаления файлов temp_CACHED_*.php в директории typo3conf.

пятница, 17 февраля 2012 г.

Новая иконка для системной папки


Пока опять сам не забыл, решил записать на блог...

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

1. Добавляется в файл typo3conf\extTables.php всего 2 строки:


$TCA['pages']['columns']['module']['config']['items'][] = array('Description', 'description', '../fileadmin/templates/images/ico/officefitness.gif');
t3lib_SpriteManager::addTcaTypeIcon('pages', 'contains-'description', '../fileadmin/templates/images/ico/officefitness.gif');

2. Создайте системную папку и "привяжите" к ней новую иконку:

P.S. Забыл написать, что иконка должна быть размером 16 на 16 пикселей.

суббота, 28 января 2012 г.

tt_news - добавление новых полей к плагину


Чтобы лучше понять задачу ради которой я решил добавить к tt_news ряд полей, читайте первую часть.

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

Создать новое расширение можно с помощью инструмента - kickstarter. Но в данной ситуации я поступил иначе. Я скачал и установил уже готовый плагин - ttnewsfield, который добавляет к tt_news одно единственное поле -  tx_ttnewsfield_linktitle. Этот плагин послужил мне замечательной стартовой площадкой для добавления множества других полей.

Рассмотрим добавление 5 различных полей: обычное текстовое поле, поле в виде выпадающего списка, поле ссылающееся на запись  из другой таблицы (отношение 1:1), поле ссылающееся на записи  из другой таблицы (отношение 1:n), IRRE поле.



Добавление обычного текстового поля


Все что нужно для работы - это текстовый редактор и 3 файла ранее установленного ttnewsfield: ext_tables.sql, locallang_db.xml и ext_tables.php. Хотя, по-большому счету ext_tables.sql можно и не трогать, если  менять структуру таблиц с помощью phpmyadmin или других программ. Думаю, что с этим файлом всё абсолютно понятно и я не буду о нем писать, как и о locallang_db.xml, который содержит набор текстовых констант, которые служат для отображения надписей в админке и её локализации.

Самое интересное творится в ext_tables.php. На старте содержимое файла такое:

$tempColumns = Array (
    "tx_ttnewsfield_linktitle" => Array (
        "exclude" => 0,
        "label" => "LLL:EXT:ttnewsfield/locallang_db.xml:tt_news.tx_ttnewsfield_linktitle",
        "config" => Array (
            "type" => "input",
            "size" => "15",    
            "max" => "64",
            "eval" => "trim",
        )
    ),
);
t3lib_div::loadTCA("tt_news");
t3lib_extMgm::addTCAcolumns("tt_news",$tempColumns,1);
$TCA['tt_news']['palettes']['1']['showitem'] .= ",tx_ttnewsfield_linktitle";



Поле tx_ttnewsfield_linktitle, которое добавляется в эксте мне не интересно, поэтому я его переименовал в tx_fitness_vimeo. Его задача - хранить обычный кусок html-кода.
Вот описание этого поля для админки в файле ext_tables.php:

'tx_fitness_vimeo' => Array (
'label' => 'LLL:EXT:ttnews_fitness/locallang_db.xml:tx_ttnewsirre_vimeo',
'l10n_mode' => $l10n_mode,
'config' => Array (
'type' => 'text',
'cols' => '48',
'rows' => '5',
'softref' => 'typolink_tag,images,email[subst],url',
'wizards' => Array(
'_PADDING' => 4,
'RTE' => Array(
'notNewRecords' => 1,
'RTEonly' => 1,
'type' => 'script',
'title' => 'LLL:EXT:cms/locallang_ttc.php:bodytext.W.RTE',
'icon' => 'wizard_rte2.gif',
'script' => 'wizard_rte.php',
),
)
)
),
Хочу заметить, что данные строки и все последующие являются элементами массива $tempColumns, который я переименовал в $fitnessColumns. Чтобы лучше понять значение каждого из вышеуказанных параметров элемента массива - почитайте мануал по TCA array reference. Там нет ничего сложного.

А вот так поле будет выглядеть в админке:
Чтобы отобразить содержимое данного поля на FE советую заглянуть во внутрь экста - Raw HTML in tt_news ( ttnews_html ) или Youtube for tt_news ( esg_youtubenews ). Абсолютно ничего военного они не делают. Добавляется одна строка в файл ext_localconf.php и создается дополнительный файл с 10-15 строками, в котором идет сопоставление значения нового поля с определенным маркером в html-шаблоне. Но правда, есть еще один способ вывода. Это универсальный экст - ttnewsgenericmarkers, который выводит в html-шаблон все, что угодно.

Добавление поля с фиксированным списком

Сделать дополнительное поле в виде статического списка значение тоже не сложно.
Вот пример, для поля tx_fitness_shared_office_suitability, которое может принимать всего 5 значений: 1, 2, 3, 4 и 5 (файл ext_tables.php):

"tx_fitness_shared_office_suitability" => Array (
"exclude" => 0,
"label" => "LLL:EXT:ttnews_fitness/locallang_db.xml:tx_ttnewsirre_shared_office_suitability",
'config' => array(
'type' => 'select',
'items' => array(
array('LLL:EXT:ttnews_fitness/locallang_db.xml:suitability.1', 1),
array('LLL:EXT:ttnews_fitness/locallang_db.xml:suitability.2', 2),
array('LLL:EXT:ttnews_fitness/locallang_db.xml:suitability.3', 3),
array('LLL:EXT:ttnews_fitness/locallang_db.xml:suitability.4', 4),
array('LLL:EXT:ttnews_fitness/locallang_db.xml:suitability.5', 5),
),
),
),

Эти 5 строк нужно добавить в файл locallang_db.xml:


<label index="suitability.1">1</label>
<label index="suitability.2">2</label>
<label index="suitability.3">3</label>
<label index="suitability.4">4</label>
<label index="suitability.5">5</label>
Если числа внутри тега label заменить на слова, то список станет более информативным.
Внешний вид такого списка в админке typo3:


Добавление поля-ссылки (отношение 1 к 1)


Ради таких полей я и решил написать данную статью. Меня давно интересовал данный вопрос. И вот, наконец-то, я его разрулил.

В предыдущем случае поле (tx_fitness_shared_office_suitability) являлось фиксированным списком из пяти значений, но что делать если нужен динамический список элементов? Тут все немного интереснее.

1. Создаем новую таблицу в БД, которая, собственно и будет содержать все значения списка.
В моем случае эта таблица имела такую структуру:
CREATE TABLE `tx_fitness_workarea` (
  `uid` int(11) unsigned NOT NULL auto_increment,
  `pid` int(11) NOT NULL default '0',
  `tstamp` int(11) NOT NULL default '0',
  `crdate` int(11) NOT NULL default '0',
  `cruser_id` int(11) NOT NULL default '0',
  `sys_language_uid` int(11) NOT NULL default '0',
  `l18n_parent` int(11) NOT NULL default '0',
  `l18n_diffsource` mediumblob NOT NULL,
  `deleted` tinyint(4) NOT NULL default '0',
  `is_dummy_record` tinyint(1) unsigned NOT NULL default '0',
  `title` tinytext NOT NULL,
  PRIMARY KEY  (`uid`),
  KEY `parent` (`pid`),
  KEY `dummy` (`is_dummy_record`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8

Самое ценное из всех полей данной таблицы, конечно же поле title. Остальные поля - служебные и интересуют больше не меня, а typo3.

В таблицу tt_news я добавил числовое поле tx_fitness_workarea, которое будет содержать uid только одной записи из таблицы tx_fitness_workarea. Вот sql-определение данного поля:
CREATE TABLE tt_news (
...
tx_fitness_workarea int(11) NOT NULL default '0',
...
);

Эти настройки ТСА сделают новое поле tx_fitness_workarea в таблице tt_news выпадающим списком, который будет наполнен строками из таблицы tx_fitness_workarea:

'tx_fitness_workarea' => array(
'exclude' => 1,
'label' => 'LLL:EXT:ttnews_fitness/locallang_db.xml:tx_ttnewsirre_workarea',
'config' => array(
'type' => 'select',
'items' => array(
array('', 0),
),
'foreign_table' => 'tx_fitness_workarea',
'foreign_table_where' => 'AND tx_fitness_workarea.pid=###STORAGE_PID### ORDER BY tx_fitness_workarea.title',
'size' => 1,
'minitems' => 0,
'maxitems' => 1,
'wizards' => array(
'_PADDING' => 2,
'_VERTICAL' => 1,
'add' => array(
'type' => 'script',
'title' => 'Create new record',
'icon' => 'add.gif',
'params' => array(
'table'=>'tx_fitness_workarea',
'pid' => '###STORAGE_PID###',
'setValue' => 'prepend',
),
'script' => 'wizard_add.php',
),
'edit' => array(
'type' => 'popup',
'title' => 'Edit',
'script' => 'wizard_edit.php',
'popup_onlyOpenIfSelected' => 1,
'icon' => 'edit2.gif',
'JSopenParams' => 'height=350,width=580,status=0,menubar=0,scrollbars=1',
),
),
),
)
Вот так поле будет выглядеть в админке:


Самое интересное, как вы уже заметили, что возле списка есть кнопки, которые позволяют редактировать значения списка и добавлять в него новые элементы. Чтобы эти кнопки работали необходимо в файл ext_tables.php вставить следующие строки:

t3lib_extMgm::allowTableOnStandardPages("tx_fitness_workarea");
$TCA['tx_fitness_workarea'] = array(
'ctrl' => array(
'title' => 'LLL:EXT:ttnews_fitness/locallang_db.xml:tx_ttnewsirre_workarea',
'label' => 'title',
//'rootLevel' => 0,
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'languageField' => 'sys_language_uid',
'transOrigPointerField' => 'l18n_parent',
'transOrigDiffSourceField' => 'l18n_diffsource',
'default_sortby' => 'ORDER BY title',
'delete' => 'deleted',
"iconfile" => t3lib_extMgm::extRelPath($_EXTKEY)."res/workarea.gif"
),
'interface' => array(
'showRecordFieldList' => 'sys_language_uid,l18n_parent,l18n_diffsource,title'
),
"feInterface" => $TCA["tx_fitness_workarea"]["feInterface"],
'columns' => array(
'sys_language_uid' => array(
'exclude' => 1,
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.language',
'config' => array(
'type' => 'select',
'foreign_table' => 'sys_language',
'foreign_table_where' => 'ORDER BY sys_language.title',
'items' => array(
array('LLL:EXT:lang/locallang_general.xml:LGL.allLanguages', -1),
array('LLL:EXT:lang/locallang_general.xml:LGL.default_value', 0),
),
),
),
'l18n_parent' => array(
'displayCond' => 'FIELD:sys_language_uid:>:0',
'exclude' => 1,
'label' => 'LLL:EXT:lang/locallang_general.xml:LGL.l18n_parent',
'config' => array(
'type' => 'select',
'items' => array(
array('', 0),
),
'foreign_table' => 'tx_fitness_workarea',
'foreign_table_where' => 'AND tx_fitness_workarea.pid=###CURRENT_PID### AND tx_fitness_workarea.sys_language_uid IN (-1, 0)',
),
),
'l18n_diffsource' => array(
'config' => array(
'type' => 'passthrough',
)
),
'title' => array(
'exclude' => 0,
'label' => 'LLL:EXT:ttnews_fitness/locallang_db.xml:tx_ttnewsirre_workarea',
'config' => array(
'type' => 'input',
'size' => '30',
'eval' => 'required',
),
),
),
'types' => array(
'0' => array('showitem' => 'sys_language_uid;;;;1-1-1, l18n_parent, l18n_diffsource, title;;;;2-2-2'),
),
'palettes' => array(
'1' => array('showitem' => ''),
)
);


Хочу заметить, что эти строки уже не являются элементами массива $tempColumns ($fitnessColumns).

Теперь немного о ###CURRENT_PID### и ###STORAGE_PID###, которые поначалу вызывают недоумение. Что это такое? В принципе, имеются ввиду id фолдера или страницы содержащих (отображающие) записи таблицы tx_fitness_workarea.
Вначале у меня были какие-то ошибки при добавлении новых записей, но всё вылечилось установкой General Record Storage Page для фолдера хранящего все мои данные:

Добавление поля-ссылки (отношение один ко многим 1:n)


Ну а теперь самый сложный и самый интересный случай, когда на одну запись из родительской таблицы (tt_news) ссылается несколько записей из дочерней таблицы.

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

Итак, дополнительное поле в tt_news будет называться -  tx_fitness_equipment, дочерняя таблица - tx_fitness_equipment (список тренажеров), а промежуточная таблица -  tt_news_mainequipment_mm.
SQL для данной модели данных у меня такой:

CREATE TABLE tt_news (
...
  tx_fitness_equipment text,
...
);
CREATE TABLE `tx_fitness_equipment` (
  `uid` int(11) NOT NULL auto_increment,
  `pid` int(11) NOT NULL default '0',
  `tstamp` int(11) NOT NULL default '0',
  `crdate` int(11) NOT NULL default '0',
  `cruser_id` int(11) NOT NULL default '0',
  `sys_language_uid` int(11) NOT NULL default '0',
  `l18n_parent` int(11) NOT NULL default '0',
  `l18n_diffsource` mediumblob NOT NULL,
  `sorting` int(10) NOT NULL default '0',
  `deleted` tinyint(4) NOT NULL default '0',
  `hidden` tinyint(4) NOT NULL default '0',
  `t3ver_oid` int(11) NOT NULL default '0',
  `t3ver_id` int(11) NOT NULL default '0',
  `t3ver_wsid` int(11) NOT NULL default '0',
  `t3ver_label` varchar(30) NOT NULL default '',
  `t3ver_state` tinyint(4) NOT NULL default '0',
  `t3ver_stage` tinyint(4) NOT NULL default '0',
  `t3ver_count` int(11) NOT NULL default '0',
  `t3ver_tstamp` int(11) NOT NULL default '0',
  `t3ver_move_id` int(11) NOT NULL default '0',
  `t3_origuid` int(11) NOT NULL default '0',
  `title` tinytext NOT NULL,
  `exercises` int(11) NOT NULL default '0',
  `image` varchar(255) default NULL,

  PRIMARY KEY  (`uid`),
  KEY `parent` (`pid`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8

CREATE TABLE `tt_news_mainequipment_mm` (
  `uid_local` int(11) NOT NULL default '0',
  `uid_foreign` int(11) NOT NULL default '0',
  `tablenames` varchar(30) NOT NULL default '',
  `sorting` int(11) NOT NULL default '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

Последняя таблица в своем наименовании имеет суффикс MM. Это не просто так. У Typo3 такой вид отношений между таблицами называется mm-отношения. Это довольно распространенный вид взаимосвязи,  наиболее ярким примером которой могут послужить новости и их категории. Ведь одна новость может принадлежать к нескольким категориям, а одна категория может принадлежать нескольким новостям.

Итак, в ext_tables.php добавляем:


'tx_fitness_equipment' => Array (
'exclude' => 1,
"label" => "LLL:EXT:ttnews_fitness/locallang_db.xml:tx_ttnewsirre_equipment",
'config' => Array (
'type' => 'select',
'size' => 10,
'foreign_table' => 'tx_fitness_equipment',
'foreign_table_where' => 'ORDER BY title',
'autoSizeMax' => 50,
'minitems' => 0,
'maxitems' => 10,
'MM' => 'tt_news_mainequipment_mm',
)
),


t3lib_extMgm::allowTableOnStandardPages("tx_fitness_equipment");
$TCA["tx_fitness_equipment"] = Array (
"ctrl" => Array (
'title' => 'LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.tx_ttnewsfield_equipment',
'label' => 'title',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'languageField'            => 'sys_language_uid',
'transOrigPointerField'    => 'l18n_parent',
'transOrigDiffSourceField' => 'l18n_diffsource',
"sortby" => "sorting",
"delete" => "deleted",
"enablecolumns" => Array (
"disabled" => "hidden",
),
"iconfile" => t3lib_extMgm::extRelPath($_EXTKEY)."res/equipment.gif",
'versioningWS' => TRUE,
'origUid' => 't3_origuid',
),
"interface" => Array (
"showRecordFieldList" => "sys_language_uid,l18n_parent,l18n_diffsource,hidden,title,image,exercises"
),
"feInterface" => $TCA["tx_fitness_equipment"]["feInterface"],
"columns" => Array (
'sys_language_uid' => array (
'exclude' => 1,
'label'  => 'LLL:EXT:lang/locallang_general.xml:LGL.language',
'config' => array (
'type'                => 'select',
'foreign_table'       => 'sys_language',
'foreign_table_where' => 'ORDER BY sys_language.title',
'items' => array(
array('LLL:EXT:lang/locallang_general.xml:LGL.allLanguages', -1),
array('LLL:EXT:lang/locallang_general.xml:LGL.default_value', 0)
)
)
),
'l18n_parent' => array (
'displayCond' => 'FIELD:sys_language_uid:>:0',
'exclude'     => 1,
'label'       => 'LLL:EXT:lang/locallang_general.xml:LGL.l18n_parent',
'config'      => array (
'type'  => 'select',
'items' => array (
array('', 0),
),
'foreign_table'       => 'tx_fitness_equipment',
'foreign_table_where' => 'AND tx_fitness_equipment.pid=###CURRENT_PID### AND tx_fitness_equipment.sys_language_uid IN (-1,0)',
)
),
'l18n_diffsource' => array (
'config' => array (
'type' => 'passthrough'
)
),
"hidden" => Array (
"exclude" => 1,
"label" => "LLL:EXT:lang/locallang_general.xml:LGL.hidden",
"config" => Array (
"type" => "check",
"default" => "0"
)
),
"title" => Array (
"exclude" => 1,
'l10n_mode' => 'prefixLangTitle',
"label" => "LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.tx_ttnewsfield_equipment",
"config" => Array (
"type" => "input",
"size" => "30",
"eval" => "required",
)
),
'image' => array (
'exclude' => 0,
'l10n_mode' => 'exclude',
'label' => 'LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.tx_ttnewsfield_image',
'config' => array (
'type' => 'group',
'internal_type' => 'file',
'allowed' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'],
'max_size' => $GLOBALS['TYPO3_CONF_VARS']['BE']['maxFileSize'],
'uploadfolder' => 'uploads/tx_ttnews',
'show_thumbs' => '1',
'size' => 1,
'minitems' => 0,
'maxitems' => 1,
)
),
"exercises" => Array (
"exclude" => 1,
"label" => "LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.tx_ttnewsfield_exercises",
"config" => Array (
"type" => "inline",
"foreign_table" => "tx_fitness_exercise2equipment",
"foreign_field" => "equipmentid",
"foreign_sortby" => "equipmentsort",
"foreign_label" => "exerciseid",
"maxitems" => 10,
'appearance' => array(
'showSynchronizationLink' => 1,
'showAllLocalizationLink' => 1,
'showPossibleLocalizationRecords' => 1,
'showRemovedLocalizationRecords' => 1,
),
'behaviour' => array(
'localizationMode' => 'select',
),
)
),
),
"types" => Array (
"0" => Array("showitem" => "sys_language_uid;;;;1-1-1, l18n_parent, l18n_diffsource, hidden;;1, title;;;;2-2-2, image")
),
"palettes" => Array (
"1" => Array("showitem" => "")
)
);

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

а при редактировании упражнений интерфейс будет таким:
Все супер, но не супер. Есть одно неудобство. Чтобы к упражнению можно было привязать несколько тренажеров, для этого, в первую очередь, необходимо предварительно создать записи этих тренажеров, т.е. для этого нужно каждый раз выходить из формы ввода упражнений (tt_news). Поэтому, я пришел к выводу, что нужно научиться вводить дочерние записи прямо из родительской таблицы. Технология Inline Relational Record Editing (IRRE) позволяет разработчику  сделать это.

Дополнительное IRRE поле


К упражнениям можно привязать еще одну характеристику - группы мышц, которые оно разрабатывает. Как и в предыдущем случае, создаем дополнительно поле в tt_news - musclegroups, дочернюю таблицу - tx_fitness_musclegroup и связующую таблицу - tx_fitness_exercise2musclegroup.


CREATE TABLE tt_news (
...
  musclegroups int(11) NOT NULL default '0',
...
);


CREATE TABLE tx_fitness_musclegroup (
  uid int(11) NOT NULL auto_increment,
  pid int(11) NOT NULL default '0',
  tstamp int(11) NOT NULL default '0',
  crdate int(11) NOT NULL default '0',
  cruser_id int(11) NOT NULL default '0',
  sys_language_uid int(11) NOT NULL default '0',
  l18n_parent int(11) NOT NULL default '0',
  l18n_diffsource mediumblob NOT NULL,
  sorting int(10) NOT NULL default '0',
  deleted tinyint(4) NOT NULL default '0',
  hidden tinyint(4) NOT NULL default '0',
  t3ver_oid int(11) NOT NULL default '0',
  t3ver_id int(11) NOT NULL default '0',
  t3ver_wsid int(11) NOT NULL default '0',
  t3ver_label varchar(30) NOT NULL default '',
  t3ver_state tinyint(4) NOT NULL default '0',
  t3ver_stage tinyint(4) NOT NULL default '0',
  t3ver_count int(11) NOT NULL default '0',
  t3ver_tstamp int(11) NOT NULL default '0',
  t3ver_move_id int(11) NOT NULL default '0',
  t3_origuid int(11) NOT NULL default '0',
  title tinytext NOT NULL,
  exercises int(11) NOT NULL default '0',
  PRIMARY KEY  (uid),
  KEY parent (pid)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8

CREATE TABLE `tx_fitness_exercise2musclegroup` (
  `uid` int(11) NOT NULL auto_increment,
  `pid` int(11) NOT NULL default '0',
  `tstamp` int(11) NOT NULL default '0',
  `crdate` int(11) NOT NULL default '0',
  `cruser_id` int(11) NOT NULL default '0',
  `sys_language_uid` int(11) NOT NULL default '0',
  `l18n_parent` int(11) NOT NULL default '0',
  `l18n_diffsource` mediumblob NOT NULL,
  `deleted` tinyint(4) NOT NULL default '0',
  `hidden` tinyint(4) NOT NULL default '0',
  `t3ver_oid` int(11) NOT NULL default '0',
  `t3ver_id` int(11) NOT NULL default '0',
  `t3ver_wsid` int(11) NOT NULL default '0',
  `t3ver_label` varchar(30) NOT NULL default '',
  `t3ver_state` tinyint(4) NOT NULL default '0',
  `t3ver_stage` tinyint(4) NOT NULL default '0',
  `t3ver_count` int(11) NOT NULL default '0',
  `t3ver_tstamp` int(11) NOT NULL default '0',
  `t3ver_move_id` int(11) NOT NULL default '0',
  `t3_origuid` int(11) NOT NULL default '0',
  `exerciseid` int(11) NOT NULL default '0',
  `musclegroupid` int(11) NOT NULL default '0',
  `exercisesort` int(10) NOT NULL default '0',
  `musclegroupsort` int(10) NOT NULL default '0',

  PRIMARY KEY  (`uid`),
  KEY `parent` (`pid`)
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8

Строки для ext_tables.php:


'musclegroups' => Array (
"exclude" => 1,
"label" => "LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.tx_ttnewsfield_secondarymusclegroups",
"config" => Array (
"type" => "inline",
"foreign_table" => "tx_fitness_exercise2musclegroup",
"foreign_field" => "exerciseid",
"foreign_sortby" => "exercisesort",
"foreign_label" => "musclegroupid",
/*"symmetric_field" => "exerciseid",
"symmetric_label" => "type",*/
"maxitems" => 10,
'appearance' => array(
'showSynchronizationLink' => 1,
'showAllLocalizationLink' => 1,
'showPossibleLocalizationRecords' => 1,
'showRemovedLocalizationRecords' => 1,
'useSortable' => 1,
'collapseAll' => 1,
'expandSingle' => 1,
),
'behaviour' => array(
'localizationMode' => 'select',
),
)
),


t3lib_extMgm::allowTableOnStandardPages("tx_fitness_musclegroup");
$TCA["tx_fitness_musclegroup"] = Array (
"ctrl" => Array (
'title' => 'LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.tx_ttnewsfield_musclegroup',
'label' => 'title',
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'languageField'            => 'sys_language_uid',
'transOrigPointerField'    => 'l18n_parent',
'transOrigDiffSourceField' => 'l18n_diffsource',
"sortby" => "sorting",
"delete" => "deleted",
"enablecolumns" => Array (
"disabled" => "hidden",
),
//"dynamicConfigFile" => t3lib_extMgm::extPath($_EXTKEY)."tca.mnasym.php",
"iconfile" => t3lib_extMgm::extRelPath($_EXTKEY)."res/hand.gif",
'versioningWS' => TRUE,
'origUid' => 't3_origuid',
),
"interface" => Array (
"showRecordFieldList" => "sys_language_uid,l18n_parent,l18n_diffsource,hidden,title,exercises"
),
//"feInterface" => Array (
// "fe_admin_fieldList" => "sys_language_uid, l18n_parent, l18n_diffsource, hidden, title, exercises",
//)
"feInterface" => $TCA["tx_fitness_musclegroup"]["feInterface"],
"columns" => Array (
'sys_language_uid' => array (
'exclude' => 1,
'label'  => 'LLL:EXT:lang/locallang_general.xml:LGL.language',
'config' => array (
'type'                => 'select',
'foreign_table'       => 'sys_language',
'foreign_table_where' => 'ORDER BY sys_language.title',
'items' => array(
array('LLL:EXT:lang/locallang_general.xml:LGL.allLanguages', -1),
array('LLL:EXT:lang/locallang_general.xml:LGL.default_value', 0)
)
)
),
'l18n_parent' => array (
'displayCond' => 'FIELD:sys_language_uid:>:0',
'exclude'     => 1,
'label'       => 'LLL:EXT:lang/locallang_general.xml:LGL.l18n_parent',
'config'      => array (
'type'  => 'select',
'items' => array (
array('', 0),
),
'foreign_table'       => 'tx_fitness_bodypart',
'foreign_table_where' => 'AND tx_fitness_musclegroup.pid=###CURRENT_PID### AND tx_fitness_musclegroup.sys_language_uid IN (-1,0)',
)
),
'l18n_diffsource' => array (
'config' => array (
'type' => 'passthrough'
)
),
"hidden" => Array (
"exclude" => 1,
"label" => "LLL:EXT:lang/locallang_general.xml:LGL.hidden",
"config" => Array (
"type" => "check",
"default" => "0"
)
),
"title" => Array (
"exclude" => 1,
'l10n_mode' => 'prefixLangTitle',
"label" => "LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.tx_ttnewsfield_musclegroup",
"config" => Array (
"type" => "input",
"size" => "30",
"eval" => "required",
)
),
"exercises" => Array (
"exclude" => 1,
"label" => "LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.tx_ttnewsfield_exercises",
"config" => Array (
"type" => "inline",
"foreign_table" => "tx_fitness_exercise2musclegroup",
"foreign_field" => "musclegroupid",
"foreign_sortby" => "musclegroupsort",
"foreign_label" => "exerciseid",
"maxitems" => 10,
'appearance' => array(
'showSynchronizationLink' => 1,
'showAllLocalizationLink' => 1,
'showPossibleLocalizationRecords' => 1,
'showRemovedLocalizationRecords' => 1,
),
'behaviour' => array(
'localizationMode' => 'select',
),
)
),
),
"types" => Array (
"0" => Array("showitem" => "sys_language_uid;;;;1-1-1, l18n_parent, l18n_diffsource, hidden;;1, title;;;;2-2-2, exercises")
),
"palettes" => Array (
"1" => Array("showitem" => "")
)
);


t3lib_extMgm::allowTableOnStandardPages("tx_fitness_exercise2musclegroup");
$TCA["tx_fitness_exercise2musclegroup"] = Array (
"ctrl" => Array (
'title' => 'LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.tx_ttnewsfield_exercise2musclegroup',
'label' => 'exerciseid',
'label_alt' => 'musclegroupid',
'label_alt_force' => TRUE,
'tstamp' => 'tstamp',
'crdate' => 'crdate',
'cruser_id' => 'cruser_id',
'languageField'            => 'sys_language_uid',
'transOrigPointerField'    => 'l18n_parent',
'transOrigDiffSourceField' => 'l18n_diffsource',
"delete" => "deleted",
"enablecolumns" => Array (
"disabled" => "hidden",
),
"iconfile" => t3lib_extMgm::extRelPath($_EXTKEY)."res/finger-bw.gif",
'versioningWS' => TRUE,
'origUid' => 't3_origuid',
'shadowColumnsForNewPlaceholders' => 'exerciseid, musclegroupid',
),
);
$TCA["tx_fitness_exercise2musclegroup"] = Array (
"ctrl" => $TCA["tx_fitness_exercise2musclegroup"]["ctrl"],
"interface" => Array (
"showRecordFieldList" => "sys_language_uid,l18n_parent,l18n_diffsource,hidden,exerciseid,musclegroupid,exercisesort,musclegroupsort,type"
),
"feInterface" => $TCA["tx_fitness_exercise2musclegroup"]["feInterface"],
"columns" => Array (
'sys_language_uid' => array (
'exclude' => 1,
'label'  => 'LLL:EXT:lang/locallang_general.xml:LGL.language',
'config' => array (
'type'                => 'select',
'foreign_table'       => 'sys_language',
'foreign_table_where' => 'ORDER BY sys_language.title',
'items' => array(
array('LLL:EXT:lang/locallang_general.xml:LGL.allLanguages', -1),
array('LLL:EXT:lang/locallang_general.xml:LGL.default_value', 0)
)
)
),
'l18n_parent' => array (
'displayCond' => 'FIELD:sys_language_uid:>:0',
'exclude'     => 1,
'label'       => 'LLL:EXT:lang/locallang_general.xml:LGL.l18n_parent',
'config'      => array (
'type'  => 'select',
'items' => array (
array('', 0),
),
'foreign_table'       => 'tx_fitness_exercise2musclegroup',
'foreign_table_where' => 'AND tx_fitness_exercise2musclegroup.pid=###CURRENT_PID### AND tx_fitness_exercise2musclegroup.sys_language_uid IN (-1,0)',
)
),
'l18n_diffsource' => array (
'config' => array (
'type' => 'passthrough'
)
),
"hidden" => Array (
"exclude" => 1,
"label" => "LLL:EXT:lang/locallang_general.xml:LGL.hidden",
"config" => Array (
"type" => "check",
"default" => "0"
)
),
"exerciseid" => Array (
"label" => "LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.tx_ttnewsfield_exercise",
"config" => Array (
"type" => "select",
"foreign_table" => "tt_news",
'foreign_table_where' => 'AND type=3',
"maxitems" => 1,
'localizeReferences' => 1,
)
),
"musclegroupid" => Array (
"label" => "LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.tx_ttnewsfield_musclegroup",
"config" => Array (
"type" => "select",
"foreign_table" => "tx_fitness_musclegroup",
"maxitems" => 1,
'localizeReferences' => 1,
)
),
"exercisesort" => Array (
"config" => Array (
"type" => "passthrough",
)
),
"musclegroupsort" => Array (
"config" => Array (
"type" => "passthrough",
)
),
/*"type" => Array (
"exclude" => 0,
"label" => "LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.tx_ttnewsfield_musclegroup_type",
'config' => array(
'type' => 'select',
'items' => array(
array('LLL:EXT:ttnews_fitness/locallang_db.xml:musclegroup_type.1', 'Primary'),
array('LLL:EXT:ttnews_fitness/locallang_db.xml:musclegroup_type.2', 'Secondary'),
),
),
),*/
),
"types" => Array (
"0" => Array("showitem" => "sys_language_uid;;;;1-1-1, l18n_parent, l18n_diffsource, hidden;;1, title;;;;2-2-2, exerciseid;;;;3-3-3, musclegroupid, exercisesort, musclegroupsort")
),
"palettes" => Array (
"1" => Array("showitem" => "")
)
);

Получаем следующий результат в BE:
Как видим, такая форма более удобна, т.к. позволяет создавать дочерние записи прямо внутри родительской. Больше информации о IRRE можно найти здесь.

Нужно сказать, что после добавления всех вышеуказанных изменений в файле ext_tables.php, форма ввода tt_news содержать новых полей не будет. Для этого нужно добавить еще 2 строки:


t3lib_div::loadTCA("tt_news");
t3lib_extMgm::addTCAcolumns("tt_news",$fitnessColumns,1);

Возможно у кого-то возникнет вопрос: "А нафига мне Новости с кучей всяких дополнительных полей, которые нужны в одном случае, а в другом уже не нужны?". Все можно настроить. Для того, чтобы стандартные новости не содержали лишних полей создается дополнительный индивидуально настроенный тип записи. Изначально новости имеют 3 типа записей: Новости, Статьи, Внешняя ссылка. Создать еще один тип - проще простого. В файл ext_tables.php своего расширения добавляем:



$GLOBALS['TCA']['tt_news']['columns']['type']['config']['items'][] = Array('LLL:EXT:ttnews_fitness/locallang_db.xml:tt_news.type.I.3', 3);
$GLOBALS['TCA']['tt_news']['ctrl']['typeicons']['3'] = t3lib_extMgm::extRelPath($_EXTKEY)."res/ball-ico.gif";


Что дали последние 2 строки? Первое, добавили новый тип записи к новостям с кодом 3. Текстовое название этого типа сидит в файле locallang_db.xml в строке:

<label index="tt_news.type.I.3">Exercise</label>

Второе, новому типу записей сопоставлена иконка, которая сидит в каталоге res нашего расширения с названием - ball-ico.gif (16 на 16 пикселей).


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


$GLOBALS['TCA']['tt_news']['types']['3']['showitem'] = 'hidden, type;;;;1-1-1,title;;;;2-2-2,tx_fitness_shared_office_suitability,tx_fitness_workarea,bodytext;;2;richtext:rte_transform[flag=rte_enabled|mode=ts];4-4-4,tx_fitness_how2;;2;richtext:rte_transform[flag=rte_enabled|mode=ts];4-4-4,
--div--;LLL:EXT:ttnews_fitness/locallang_db.xml:exercise_info.name,tx_fitness_easier;;2;richtext:rte_transform[flag=rte_enabled|mode=ts];4-4-4,tx_fitness_harder;;2;richtext:rte_transform[flag=rte_enabled|mode=ts];4-4-4,tx_fitness_hints;;2;richtext:rte_transform[flag=rte_enabled|mode=ts];4-4-4,
--div--;LLL:EXT:ttnews_fitness/locallang_db.xml:exercise_relations, category;;;;3-3-3,tx_fitness_purpose,tx_fitness_equipment,tx_fitness_equivalent_gym_equipment,mainmusclegroups,musclegroups,
--div--;LLL:EXT:tt_news/locallang_tca.xml:tt_news.tabs.media, tx_damnews_dam_images,tx_fitness_musclegroupimage,tx_fitness_vimeo;;;;1-1-1,
--div--;LLL:EXT:tt_news/locallang_tca.xml:tt_news.tabs.access, starttime,endtime,fe_group,editlock';



Полный текст файла ext_tables.php можно взять здесь.

P.S. Это не статья, а по объему уже "Война и Мир"...:)

О себе

Моя фотография
Вадим Гиркало
Фрилансер, веб-разработчик сайтов на базе бесплатной, мощнейшей и очень гибкой CMS системы - TYPO3.
Просмотреть профиль

Позвонить мне в Skype

TOP - 3