Skip to content
Steve Pfisterer edited this page Aug 7, 2017 · 12 revisions

Examples

Field Group

Simply create a class with a configure function that accepts a FieldsBuilder and configure the fields and return the builder.

namespace MyProject\Fields;

use Timber\Image;
use Understory\ACF\FieldsBuilder;
use Understory\ACF\FieldGroup;

class Banner extends FieldGroup
{
    private $title;
    private $backgroundImage;

    public function configure(FieldsBuilder $builder)
    {
        $builder
            ->addText('title')
            ->addWysiwyg('content')
            ->addImage('background_image');
        
        return $builder;
    }

    public function getBackgroundImage()
    {
        // Cache \Timber\Image object
        return $this->backgroundImage ?: $this->backgroundImage = new Image($this->getMetaValue('background_image'));
    }

    // Perform all style attribute logic in the class
    public function getBackgroundStyle()
    {
        $css = [
            'min-height' => $this->getBackgroundImage().height(),
            'background-image' => "url({$this->getBackgroundImage().src())})"
        ];

        return array_reduce(array_keys($css), function($style, $key) use ($css) { 
            return $style . "{$key}:{$css[$key]};";
        }, '');
    }

    public function getTitle()
    {
        return $this->title ?: $this->title = $this->getMetaValue('title');
    }

    public function setTitle($title)
    {
        $this->title = $title;
    }

    public function getContent()
    {
        // Apply the content filter to our wysiwyg content field
        return apply_filters('the_content', $this->getMetaValue('content');
    }
}

Then you can associate it with a post type, view, etc.

namespace MyProject\Models;

use MyProject\Fields\Banner;
use Understory\CustomPostType;
use Understory\ACF\PostTypeBuilder;

class NewsItem extends CustomPostType
{
    private function configure(PostTypeBuilder $postTypeBuilder)
    {
        $postTypeBuilder->setConfig([
            'supports' => 'title, thumbnail, content, excerpt',
        ]);

        // Register post type with the banner custom field group
        $this->has('banner', new Banner);

        return $postTypeBuilder;
    }
}

In the Views\single-news-item.php view, you can have the logic to display the banner's title if one exists, otherwise default to the News post type's title.

namespace MyProject\Views;

use Understory\View;
use MyProject\Fields\Banner;

class SingleNewsItem extends View
{
   private $banner;

   /**
    * Use the banner associated with the News Item, customize to use the News Item title
    * if the title field of the banner was left blank
    */
   public function getBanner()
   {
        if(!$this->banner) {
            $this->banner = $this->getPost()->getBanner();

            // Use News Item title if a custom banner title wasn't entered
            if (!$this->banner->getTitle()) {
                $$this->banner->setTitle($this->getPost()->getTitle());
            }
        }

       return $this->banner;
   }
}

Then in your twig template single-news-item.twig simply embed the banner template and pass in the banner

{# single-news-item.twig #}

{% extends "base.twig" %}

{% block content %}
    {% embed 'section/banner.twig' with { banner: page.banner } %}
    {% endembed %}
    
    {# ... Rest of template ... #}
{% endblock %}

Then in the banner twig template section/banner.twig write once, use everywhere

{# section/banner.twig #}

<section class="section__banner" style="{{ banner.backgroundStyle }}">
  <div class="banner__container">
    <h1 class="banner__title">{{ banner.title }}</h1>
    <div class="banner__content">{{ banner.content }}</div>
  </div>
</section>

Reuse

We can then reuse this banner field and twig template (along with any CSS or javascript written for it) anywhere else, like a View.

/**
 * Template Name: Home
 */
namespace MyProject\Views;

use MyProjectFields\Banner;
use Understory\View;


class HomeTemplate extends View
{
    public function configure()
    {
        $this->has('banner', new Banner);
    }

    /**
     * Use the banner associated with the page, customize to use the page title
     * if the title field of the banner was left blank
     */
     public function getBanner()
     {
         // Use Page title if a custom banner title wasn't entered
         if (!$this->banner->getTitle()) {
             $$this->banner->setTitle($this->getPost()->getTitle());
         }

         return $this->banner;
     }
}

Then just like the single-news-item template, simply embed the banner template and pass in the banner

{# HomeTemplate.twig #}

{% extends "base.twig" %}

{% block content %}
    {% embed 'section/banner.twig' with { banner: page.banner } %}
    {% endembed %}
    
    {# ... Rest of template ... #}
{% endblock %}

Since we're doing the same thing in both instances, defaulting to the post title if the banner title field was left blank, perhaps we should move this logic to the field itself.

namespace MyProject\Fields;

use Understory\ACF\FieldGroup;

class Banner extends FieldGroup
{
    ...

    public function getTitle()
    {
        return $this->getMetaValue('title') ?: $this->getMetaDataBinding()->getTitle();
    }

    ...
}

The scope of a field group is the object (custom post type, options page, etc) that the field group is acting upon.

This will significantly DRY up our view object

namespace MyProject\Views;

use Understory\View;

class SingleNewsItem extends View;
{
    private $banner;

    public function getBanner()
    {
        return $this->getPost()->getBanner();
    }
}
namespace MyProject\Views;

use Understory\View;

class HomeTemplate extends View
{
    private $banner;

    public function configure()
    {
        $this->has('banner', new Fields\Banner);
    }
}
Clone this wiki locally