Can I manage article posts and projects in one structured data field?

Hi all, just beginning to wrap up my first Kirby project, loving it so far, but encountered a few issues along the way.

I’m stuck at the moment with a biggie for me, maybe not for others. I want to be able to feature article posts and snippets of video relating to specific projects on the home page and display them at the front end as masonry or packery grid items.

From the panel on the home page I would like the admin to be able to handpick featured page objects from either ‘articles’ or ‘projects’, ideally managing them in a structured field with table style, so they can be dragged around and reordered, based on the admin’s preference.

I had tried to place a select inside (which grabs the page) inside a structured field (see screengrabs). Could anybody show me what I’m doing wrong, or help me with a solution?

Many thanks,

Aaron

Unfortunately, you don’t say what exactly your problem is.

I would do it differently and not use two separate fields for the pages, but rather dynamic options via JSON API.

Also, it makes more sense to store the URI instead of the ID.

Could you please post your code as code block instead of an image in the future? Thank you :slightly_smiling_face:

Sorry, texnixe - and thanks for the quick reply.

I would like to be able to display featured blog articles and project items on the home page like can be seen on https://www.outpostvfx.co.uk/

  • For the news articles I want to output the title, intro, text and page href

  • For the project pages I just want to output the video snippet (iframe, title, page href)

On top of this I would like the user to be able to manually pick and administer the order (drag) the items on the home page panel (see screenshot),

Finally, these articles and video snippets will be wrapped in packery.js grid items.

Unfortunately, i’m more of a web designer than programmer so I’m sure I’m going about things the wrong way and spent hours yesterday trying to work out how to do this and have come up with nothing. I managed to fetch all the children going about things this way:

   featuredcontent:
      label: Choose featured work to show on the home page
      type: checkboxes
      options: query
      columns: 3
      default: snippet
      query:
         page: news-and-reviews
         fetch: children
         text: '{{title}}'
         value: '{{uri}}'

And this is the code I used to output it.

<ul>
 <?php foreach($page->featuredcontent()->split() as $category): ?>
 <li><?php echo html($category) ?></li>
 <?php endforeach ?>
   </ul>

which returns:

- newsandreviews: "" work: work/jason-bourne contenttype: "2" - contenttype: "1" newsandreviews: news-and-reviews/47-meters-down work: "" - newsandreviews: news-and-reviews/studio-expansion work: "" contenttype: "1" - contenttype: "2" newsandreviews: "" work: work/orange-is-the-new-black

I can’t remember how but eventually, I managed to output all work items and all news items in one big array.

Finally, I tried to output it later on like this - below is the yaml in my home.yml file which shows what I’m trying to achieve.

   featuredcontent:
      label: Featured content
      type: structure
      style: table
      fields:
         contenttype:
            label: Content type
            type: radio
            default: 1
            options:
               1: News excerpt
               2: Video snippet
         newsandreviews:
            label: News and Reviews
            type: select
            options: query
            query:
               page: news-and-reviews
               fetch: children
               value: '{{id}}'
         work:
            label: Work
            type: select
            options: query
            query:
               page: work
               fetch: children
               value: '{{id}}'

and the code to output it…

   <ul class="grid o-grid">
      <?php foreach($page->featuredcontent()->toStructure() as $featureditem):  ?>

      <?php
         if ($featureditem->contenttype() == '1'): { ?>
         <?php foreach($featureditem as $featuredcontent => $article): ?>
         <li class="grid-item grid-item--width1 o-grid-item small">
            <div class="content-wrapper"><h3 class="article-title"><?php echo $article->title()->html() ?></h3>
               <p class="article-date">Posted on <?php echo $article->date('d.m.Y') ?></p>
               <p><?php echo $article->intro()->kirbytext() ?></p>
               <p><?php echo $article->text()->excerpt(30, 'words') ?></p>
               <a href="<?php echo $article->url() ?>">News &amp; Reviews</a>
            </div>
         </li>
         <?php endforeach ?>

      <?php } else: { ?>
         <?php foreach($featureditem as $featuredcontent => $project): ?>

         <li class="grid-item grid-item--width2 o-grid-item large">
            <div class="video-wrapper">
               <iframe class="product-card-media" type="text/html" src="<?php echo $project->url() ?>" id="player " width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
            </div>
            <div class="description"><h3><?php echo $project->title() ?></h3><a href="<?php echo $project->url() ?>">Work</a></div>
         </li>


         <?php endforeach ?>
      <?php }

         endif ?>

      <?php endforeach; ?>

   </ul>

This renders the content on to the homepage, but it duplicates everything two or three times and fragments it. I’m looking for help crafting the right solution if you or anyone can help please.

Are you able to elaborate on how to achieve the above using the JSON API and how to store the URI?

Many thanks.

Aaron

Why do you need the content type at all if it depends on the type of subpage? That way you only introduce a potential error if the user mistakenly selects the wrong content type?

Hi Sonja,

No I probably don’t need it - particularly if it’s going to cause issues.

I’m taking a look at the JSON API documentation at the moment, but also wondering if it can be achieved with a controlled list or engineer field?

Well, the inner foreach loop does not make sense.

Try this

<ul class="grid o-grid">
  <?php foreach($page->featuredcontent()->toStructure() as $featureditem):  ?>
    
    <?php
      if($featureditem->newsandreviews()->isNotEmpty()): 
        $article = page($featureditem->newsandreviews());
        if($article):
    ?>
      <li class="grid-item grid-item--width1 o-grid-item small">
        <div class="content-wrapper"><h3 class="article-title"><?php echo $article->title()->html() ?></h3>
          <p class="article-date">Posted on <?php echo $article->date('d.m.Y') ?></p>
          <p><?php echo $article->intro()->kirbytext() ?></p>
          <p><?php echo $article->text()->excerpt(30, 'words') ?></p>
          <a href="<?php echo $article->url() ?>">News &amp; Reviews</a>
        </div>
      </li>
    <php endif ?>  
    <?php elseif($featureditem->work()->isNotEmpty()):   
      $project = page($featureditem->work());
      if($project):
    ?>
      <li class="grid-item grid-item--width2 o-grid-item large">
        <div class="video-wrapper">
          <iframe class="product-card-media" type="text/html" src="<?php echo $project->url() ?>" id="player " width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
        </div>
        <div class="description"><h3><?php echo $project->title() ?></h3><a href="<?php echo $project->url() ?>">Work</a></div>
      </li>
      <?php endif ?>
      
    <?php endif ?>
    
  <?php endforeach; ?>
  
</ul>

This requires that you store the uri if the pages instead of the ID though. In your blueprint, change this line

value: '{{id}}'

to

value: '{{uri}}'

Whoa… yes, that is just the ticket - thank you so much :smiley: :sweat_smile:.

Was the theory right, but just badly executed php?

Thank you again.

I still think that the current setup is irritating for the users, because currently they could actually pick two pages in a single entry and there is nothing to prevent that.

Options:

  • the JSON API
  • the Fieldtoggle plugin. If you use the Fieldtoggle plugin, you would keep your original setup where the user would select a content type and then be shown one of the fields “work” or “newsandreviews” depending on content type. The template would be the same as above.

As regards the API solution (this is based on Kirby 2.5)

  1. Creating the JSON in a route in config.php
c::set('routes', array(
    array(
        'pattern' => 'api/picks',

        'action' => function() {
          $works = page('work')->children()->visible();
          $news = page('news-and-reviews')->children()->visible();
          $all = new Pages(array($works, $news));
          $options = array();
          foreach($all as $option) {
            $options[$option->uri()] = $option->title()->value();
          }
          return response::json($options);
        }
    )
));
  1. The blueprint
featuredcontent:
    label: hightlights
    type: structure
    style: table
    fields:
      featuredpage:
        label: Select page
        type: select
        options: url
        url: api/picks
  1. In your template
<ul class="grid o-grid">
  <?php foreach($page->featuredcontent()->toStructure() as $featureditem):  ?>
    
    <?php
       // get the page from the uri in the structure field
       $page = page($featureditem->featuredpage());
      
      // check if the parent UID is news-and-reviews
       if($page && $page->parent()->uid() == 'news-and-reviews'):
    ?>
      <li class="grid-item grid-item--width1 o-grid-item small">
        <div class="content-wrapper"><h3 class="article-title"><?php echo $page->title()->html() ?></h3>
          <p class="article-date">Posted on <?php echo $page->date('d.m.Y') ?></p>
          <p><?php echo $page->intro()->kirbytext() ?></p>
          <p><?php echo $page->text()->excerpt(30, 'words') ?></p>
          <a href="<?php echo $page->url() ?>">News &amp; Reviews</a>
        </div>
      </li>
    <php endif ?>  

    <?php
     // check if the parent UID is work
     if($page && $page->parent()->uid() == 'work'):   ?>
      <li class="grid-item grid-item--width2 o-grid-item large">
        <div class="video-wrapper">
          <iframe class="product-card-media" type="text/html" src="<?php echo $page->url() ?>" id="player " width="800" height="450" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
        </div>
        <div class="description"><h3><?php echo $page->title() ?></h3><a href="<?php echo $page->url() ?>">Work</a></div>
      </li>
      <?php endif ?>
          
  <?php endforeach; ?>
  
</ul>