Skip to content
Ultivo Toolkit 1.0 is here! Get 10% off your first year with LAUNCH10 until September 30. See pricing
Documentation menu

Template API

ultivo_field() and ultivo_rows() are how you read values in a template. They are short, a default value is an argument rather than an ?:, and a repeater is a plain foreach with no global loop state to keep track of.

Coming from ACF, the classic functions (ultivo_get_field(), ultivo_have_rows(), and so on) exist as well and stay fully supported: see Classic functions. They are the fastest way to get an existing project running, because the names map one to one. This page is where that project ends up.

ultivo_field(): get one field#

$title = ultivo_field( 'hero_title' );                  // field on the current post
$title = ultivo_field( 'hero_title', 12 );               // field on post 12
$color = ultivo_field( 'color', false, '#fff' );         // with a default when empty
$address = ultivo_field( 'company_address', 'options' ); // from an options page
$bio = ultivo_field( 'bio', 'user_5' );                  // from a user ("term_9" works too)

Signature: ultivo_field( $selector, $post_id = false, $default = null ). An image field still returns an array: ultivo_field( 'logo' )['url'].

ultivo_rows(): loop over a repeater#

No more ultivo_have_rows() / ultivo_the_row(): just a foreach. An empty repeater simply iterates zero times, so no surrounding if is needed.

foreach ( ultivo_rows( 'slides' ) as $slide ) {
    echo $slide['title'];                  // sub-field via array access
    echo $slide->get( 'button', 'Read more' ); // or with a default
}

Row count: count( ultivo_rows( 'slides' ) ).

Flexible content: layout per row#

Each row knows its own layout via ->layout():

foreach ( ultivo_rows( 'content_blocks' ) as $block ) {
    switch ( $block->layout() ) {
        case 'text':
            echo $block['content'];
            break;
        case 'gallery':
            foreach ( $block['photos'] as $photo ) {   // nested repeater in the layout
                echo $photo['photo']['url'];
            }
            break;
    }
}

Nested flexible content (flexible inside flexible) works the same way: $block->rows( 'sub_blocks' ).

render(): one line per page template#

Loads a theme file per row automatically, and skips layouts with no matching file:

ultivo_rows( 'content_blocks' )->render( 'template-parts/blocks' );
  • Flexible content: loads template-parts/blocks/{layout}.php per row (e.g. text.php, gallery.php).
  • Repeater: loads template-parts/blocks/row.php for every row.
  • Inside that file, the row is available as $row:
<?php // template-parts/blocks/text.php ?>
<section class="text-block">
    <?php echo wp_kses_post( $row['content'] ); ?>
</section>

Classic vs. recommended#

ACF-style Recommended
ultivo_get_field( 'x' ) ultivo_field( 'x' )
ultivo_get_field( 'x', 12 ) ultivo_field( 'x', 12 )
$v = ultivo_get_field('x') ?: 'default'; ultivo_field( 'x', false, 'default' )
have_rows + while + the_row foreach ( ultivo_rows( 'x' ) as $row )
ultivo_get_sub_field( 'y' ) $row['y']
ultivo_get_row_layout() $row->layout()
layout switch + get_template_part() per block ultivo_rows( 'x' )->render( 'path/blocks' )

Writing values still goes through the existing functions: ultivo_update_field() and ultivo_add_row().