Web
Analytics
Skip to content
Concrete5 на русском языке

Порядок вывода Express формы

Что ж, так мы осуществляем код вывода нашей формы:

$controller = $express->getEntityController($entity);
$context = new FrontendFormContext();
$renderer = new Renderer($context, $form);

И так выводим его на странице:

print $renderer->render();

И так выглядит выведенная форма:

Форма Экспресс

Как же этот код работает? Давайте пройдем через него. 

Form\Express\Renderer::render()

Concrete\Core\Form\Express\Renderer::render() извлекает объект Control View для самой формы, делая вызов Concrete\Core\Entity\Express\Form::getControlView, который возвращает  Concrete\Core\Express\Form\Control\View\FormView объект. Поскольку этот объект использует Concrete\Core\Form\Control\ViewInterface интерфейс, мы знаем, что он должен содержать метод  getControlRenderer(). Итак этот метод вызван.

getControlRenderer() должен вернтуь объект класса  Concrete\Core\Form\Control\RendererInterface (который указывает, что этот класс должен включать метод  render(). В этом конкретном случае, класс FormView возвращает Concrete\Core\Form\Control\Renderer из его getControlRenderer() вызова, и передаёт следующие параметры этому классу:

  • Concrete\Core\Form\Control\View\FormView как объект View.
  • FrontendFormContext как контекст.

Вы увидете, что здесь многое происходит – последний класс Concrete\Core\Form\Control\Renderer выводит единственный элемент контроля. Этот элемент контроля может быть внешним элементом формы, или может быть единственным специальным элементом контроля в Express Форме (похожий на элемент контроля связей или элемент контроля ключа атрибута.) В любом из этих случаев, специфический объект вида (который содержит данные о выводимом объекте) и контекст, передаваемый этому общему объекту вывода.

Form\Control\Renderer::render()

Когда этот общий проводник управления начинает работать, он сначала вызывает объект Concrete\Core\Filesystem\TemplateLocatorиз объекта Concrete\Core\Form\Control\ViewInterface , в который он был передан (в этом случает это был  FormView). Этот метод является частью класса ViewInterface и используется, чтобы сказать  concrete5 откуда этот конкретный вид должен загрузить свой шаблон. В этом случае, это будет  ViewInterface из Concrete\Core\Form\Control\View\FormView. Давайте исследуем его метод createTemplateLocator :

public function createTemplateLocator()
{
    $locator = new TemplateLocator('form');
    return $locator;
}

Он не много нам говорит. Что это за объект? Что он делает?

Объект TemplateLocator 

Давайте на секунду вернемся назад. Зачем нужен этот TemplateLocator класс? Просто, это объектно ориентированный способ находить конкретный  concrete5 файл в нескольких возможных местах. В версии concrete5 5.7 вы могли видеть тонны кода, выглядящих более или менее похожих на этот:

if (file_exists(DIR_APPLICATION.'/'.$segment)) {
    $file = DIR_APPLICATION.'/'.$segment;
    $override = true;
    $url = REL_DIR_APPLICATION.'/'.$segment;
} elseif ($pkgHandle) {
    $dirp1 = DIR_PACKAGES.'/'.$pkgHandle.'/'.$segment;
    $dirp2 = DIR_PACKAGES_CORE.'/'.$pkgHandle.'/'.$segment;
    if (file_exists($dirp2)) {
        $file = $dirp2;
        $url = ASSETS_URL.'/'.DIRNAME_PACKAGES.'/'.$pkgHandle.'/'.$segment;
    } elseif (file_exists($dirp1)) {
        $file = $dirp1;
        $url = DIR_REL.'/'.DIRNAME_PACKAGES.'/'.$pkgHandle.'/'.$segment;
    }
} else {
    $file = DIR_BASE_CORE.'/'.$segment;
    $url = ASSETS_URL.'/'.$segment;
}

Этот же код может быть написан с использованием локатора шаблонов таким образом:

$app = Facade::getFacadeApplication();
$locator = $app->make(FileLocator::class);
if ($pkgHandle) {
    $locator->addLocation(new FileLocator\PackageLocation($pkgHandle));
}
$record = $locator->getRecord($segment);
$file = $record->getFile();
$url = $record->getURL();

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

TemplateLocator полезен не только потому, что делает код более чистым; он также является неотъемлемой частью системы контекстного вывода ( view/context system), когда возвращается в паре с конкретным шаблоном.

Таким образом, в этом конкретном примере, мы создали объект TemplateLocator и передали его в идентификатор шаблона с именем "form". Он говорит  concrete5, что мы ищем файл с именем  form.php для вывода этого конкретного элемента управления. Но куда? Вот что происходит в следующем разделе метода Renderer::render(). Затем, мы запускаем setLocation() вместе с передаваемым объектом  $locator переданному объекту Concrete\Core\Form\Context\ContextInterface  (в этом случае, FrontendFormContext). Этот метод необходимая часть из любого контекста объектов, и возвращает измененную версию объекта  Concrete\Core\Filesystem\TemplateLocator , который был ему передан. В этом примере, давайте посмотрим, что произойдет, когда  setLocation работает над объектом FrontendFormContext. Здесь метод setLocation($locator) найденный в классе  FormContext , который расширяет FrontendFormContext:

public function setLocation(TemplateLocator $locator)
{
    $locator = parent::setLocation($locator);
    $locator->prependLocation(DIRNAME_ELEMENTS .
        DIRECTORY_SEPARATOR .
        DIRNAME_EXPRESS .
        DIRECTORY_SEPARATOR .
        DIRNAME_EXPRESS_FORM_CONTROLS .
        DIRECTORY_SEPARATOR .
        DIRNAME_EXPRESS_FORM_CONTROLS // not a typo
    );
    return $locator;
}

В этом случае мы добавляем дополнительное место переданному локатору шаблонов, говоря ему, где предпочтительно нужно искать шаблоны элементов контроля для этого элемента. В этом примере, мы передаем такой путь  'elements/express/form/form'. Поскольку мы создали наш локатор шаблонов и сказали ему, что нас интересует шаблон  "form.php", что значит, что мы собираемся посмотреть в директории  'elements/express/form/form/form.php' для вывода этого элемента контроля. По умолчанию,  TemplateLocator будет сначала искать в application/, а затем в concrete/. Он позаботится об автоматической перезаписи ( и также кешировании для повышения производительности.)

Затем, мы извлечем текущий объект вида  view в переменную $view, текущий объект контекста в  $context, и включим переданный шаблон. Это означает, что эти переменные будут доступны в шаблонах на локальном уровне. Итак, давайте проверим  `concrete/elements/express/form/form/form.php':

concrete/elements/express/form/form/form.php

Здесь видим как выглядит шаблон по умолчанию:

<input type="hidden" name="express_form_id" value="<?=$form->getID()?>">
<?=$token->output('express_form')?>

<div class="ccm-dashboard-express-form">
    <?php
    foreach ($form->getFieldSets() as $fieldSet) { ?>

        <fieldset>
            <?php if ($fieldSet->getTitle()) { ?>
                <legend><?= $fieldSet->getTitle() ?></legend>
            <?php } ?>

            <?php

            foreach($fieldSet->getControls() as $setControl) {
                $controlView = $setControl->getControlView($context);

                if (is_object($controlView)) {
                    $renderer = $controlView->getControlRenderer();
                    print $renderer->render();
                }
            }

            ?>
        </fieldset>
    <?php } ?>
</div>

Вы должно быть смогли заметить наиболее важную часть этого шаблона:

foreach($fieldSet->getControls() as $setControl) {
    $controlView = $setControl->getControlView($context);

    if (is_object($controlView)) {
        $renderer = $controlView->getControlRenderer();
        print $renderer->render();
    }
}

Мы проходим циклом через все группы полей формы, и получаем каждый элемент контроля группы. . Каждый элемент контроля  Express Формы выполняет  Concrete\Core\Form\Control\ControlInterface, это означает, что он должен быть указан методом  getControlView , который берет контекст текущей формы, как её параметр.  Этот вид элемента контроля затем отвечает за доставку проводника элемента управления, который работает с каждым элементом контроля, как и со всей формой в целом.

Давайте посмотрим на два конкретных элемента контроля:   "Name" и "Binder". Первый из них это пользовательский экспресс атрибут для этого объекта. Второй это элемент управления связями, который мы включили, чтобы вы смогли указать какому переплётчику ( который является вторым Экспресс объектом) принадлежит этот документ. 

Вывод Управляющего элемента Name

Когда работает метод getControlView над управляющим элементом ключа атрибута, мы сначала смотрим контекст, в котором мы его используем, чтобы знать какой класс вида доставить. Мы доставляем различные классы вида, когда мы смотрим Экспресс объект, и когда мы его создаем или обновляем. В этом примере, мы вызываем экземпляр объекта Concrete\Core\Express\Form\Control\View\AttributeKeyFormView, который содержит управляющий элемент контроля и контекст. 

На этом этапе всё становится немного сложным; этот класс является нашим видом управляющего элемента экспресс формы - но сами атрибуты также обладают управляющими элементами вида (так как атрибуты могут выводится вне экспресс форм.) Поэтому, сначала нам нужно получить соответствующий контекст атрибута (используемый атрибутами) из контекста нашей экспресс формы (используемой элементами управления экспресс формы.) Этот метод является частью класса  Concrete\Core\Express\Form\Context\ContextInterface – поэтому он  должен быть частью любого контекста экспресс формы, которая используется. В нашем примере, метод FrontendFormContext::getAttributeContext доставляет следующий объект:

public function getAttributeContext()
{
    return new BasicFormContext();
}

BasicFormContext является контекстом атрибута, находящегося в классе Concrete\Core\Attribute\Context\BasicFormContext. Этот контекст атрибута затем сохраняется внутри нашего вида элемента управления ключа экспресс атрибута - и помните, он  отделен  от контекста экспресс формы. Эти вещи чем-то похожи, они разделяют объекты и работают независимо. 

Затем, нам нужно взять вид элемента управления атрибута, который опять таки отличается от вида элемента управления ключа атрибута формы. Мы это осуществляем внутри  AttributeKeyFormView.

$this->view = $this->key->getController()->getControlView($this->context);

$this->context в этом коде является контекстом ключа атрибута, который мы только что получили. В этом примере текстовый атрибут возвращает стандартный вид элемента управления  Concrete\Core\Attribute\Form\Control\View\View. Этот специфический для этого атрибута вид формы расширяет базовый вид формы используемы всеми элементами управления формы (экспресс или других), который содержит повсеместно нужные методы, такие как  isRequired() и getLabel(). Эти значения затем устанавливаются внутри  AttributeKeyFormView класса. Поскольку объект вида позднее будет включён в шаблон, у нас будет к ним доступ.

Работа render()

В конце всего этого, этот код выполняет:

$renderer = $controlView->getControlRenderer();
print $renderer->render();

Это совершенно также, как предыдущий код был ответственным за вывод внешней экспресс формы; он создает базовый объект Concrete\Core\Form\Control\Renderer , обеспечивая его текущим видом объекта (в этом случае AttributeKeyFormView) и текущим методом FrontendFormContext.

Итак, что происходит в этом случае, когда выполняется  render() ? Опять, сначала мы создаем локатор шаблона, используя  AttributeKeyFormView::createTemplateLocator; в этом случае, он просто делегирует ответственность атрибуту, который им обёртывается:

public function createTemplateLocator()
{
    return $this->view->createTemplateLocator();
}

Сам вид атрибута затем берёт на себя заботу об этом.

public function createTemplateLocator()
{
    $locator = new TemplateLocator();
    $locator->addLocation(DIRNAME_ELEMENTS . DIRECTORY_SEPARATOR . DIRNAME_FORM_CONTROL_WRAPPER_TEMPLATES);
    return $locator;
}

Это говорит  concrete5, что он в итоге должен посмотреть в директории elements/form. Это общедоступная директория содержащая обёртки шаблонов форм. Потом, мы выполняем  setLocation($locator) из переданного контекста. Так как мы переключили контекст на использование класса  Concrete\Core\Attribute\Context\BasicFormContext на этом этапе, наш метод выглядит так:

public function setLocation(TemplateLocator $locator)
{
    $locator->setTemplate('bootstrap3');
    return $locator;
}

Это означает, что мы будем загружать обёртку шаблона для этого элемента управления из concrete/elements/form/bootstrap3.php (ил application/elements/form/bootstrap3.php или из другого места, по умолчанию.) Как выглядит этот шаблон?

<div class="form-group">
    <?php if ($view->supportsLabel()) { ?>
        <label class="control-label"><?=$view->getLabel()?></label>
    <?php } ?>

    <?php if ($view->isRequired()) { ?>
        <span class="text-muted small"><?=t('Required')?></span>
    <?php } ?>

    <?php $view->renderControl()?>
</div>

Довольно просто, правда? Когда $view->renderControl() существует в этой обёртке шаблона, тогда атрибут будет выводится. Опят, его вывод зависит от контекста этого атрибута.

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

Вывод элемента управления Стиль (Связь)

Когда выполняется getControlView над элементом управления связи, мы делаем нечто похожее на то, когда выводим элемент управления ключа атрибута: мы проверяем контекст, который мы передаем ему, так что мы знаем, какой класс вида доставить. В этом примере, мы вызываем экземпляр класса  Concrete\Core\Express\Form\Control\View\AssociationFormView.

Выполнение render()

Потом мы выполняем render() для этого элемента вида. Сначала этот метод создаёт локатор шаблона, со специфичной для связи логикой:

public function createTemplateLocator()
{
    // Это владеющий объект с сортировкой вывода? Если да, мы выводим отдельный элемент управления для изменения порядка вывода
    $element = $this->getFormFieldElement($this->control);
    $association = $this->association;
    if ($association->isOwningAssociation()) {
        if ($association->getTargetEntity()->supportsCustomDisplayOrder()) {
            $element = 'select_multiple_reorder';
        } else {
            return false;
        }
    }
    $locator = new TemplateLocator('association/' . $element);
    return $locator;
}

Это выглядит непонятно, но код довольно простой: если этот тип связи поддерживает сортировку вывода, мы создаём локатор шаблона с шаблоном association/select_multiple_reoder; в противном случае, если это связь один-ко-многим мы используем  association/select_multiple, или для одной связи  association/select. Затем, наш метод  render()  вызывает  setLocation($locator) в наш локатор, который говорит нам, куда загрузить эти шаблоны форм, внутри объекта контекста формы. Комбинированный означает, что наш Много-к-одному объект документа будет загружать свой элемент управления связью из  elements/express/form/form/association/select.php.

Пользовательская настройка

Давайте резюмируем:

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

Теперь, когда мы немного знаем о том, как эти формы выводятся, давайте определим, как настроить их для нашего конкретного сайта.

Загрузка беседы