Cleaning up templates with controllers?

Hi! I’m trying to clean up some templates in a project by moving the logic to controllers, however I’m a bit stumped as to how to clean up the following: The project i’m working on has a central collection of people, and these are referenced in all kinds of ways throughout the different templates, using structures:

	<section>
		<h2>Website Credits</h2>
		<? foreach($page->sitecredits()->toStructure() as $collaborator): ?>
				<div class="profile">
					<div class="profile__image"><?= $collaborator->name()->toPage()->image() ?></div>
					<div class="profile__meta">
						<h3 class="profile__name"><?= html::a($collaborator->url(), $collaborator->name()->toPage()->title() ,['target'=>'_blank']) ?></h3>
						<h3 class="profile__function"><?= $collaborator->role()->html() ?></h3>
						
					</div>
				</div>
		<? endforeach ?>
	</section>

The blueprint for sitecredits looks like this:

   sitecredits:
        label: Website Credits
        type: structure
        fields:
            role:
              label: Role
              type: text
            name:
              label: Name
              type: pages
              empty: select a collaborator that worked on the website
              icon: user
              query: site.index.filterBy('template', 'in', ['member', 'collaborator'])
              multiple: false
            url:
              label: URL
              type: url

As you can see in the template, I need to convert the reference to a collaborator that is stored in in the structure to a page in order to access the fields that its referencing. Ideally I’d move all these conversions to a controller so that I just have a simple $credits collection that I can iterate over in the templates (having taken care of the references in the controller).

What’s the best approach to implement this in a controller?

Well, you could of course create an array with all the data you need in the controller.

But your code in general has a problem: you never check if the page object actually exists before calling member methods like image(), title() etc.

Also, I’d convert once an store the result in a variable instead of calling toPage() multiple times.

That was my initial thought as well, but then I lose the capabilities of Kirby’s Structure Object…

Completely right wrt checking for page existence, that’s just laziness on my part in getting stuff on screen quickly…

It’s more work to add this afterwards and not forget stuff…

From my perspective, it doesn’t make sense to put the logic you need inside a loop into the controller. Define your converted page once and store it in a variable, then call the different methods on it.

You can also define the structure collection in your controller

Controller:

$credits = $page->sitecredits()->toStructure();
<section>
  <h2>Website Credits</h2>
  <?php foreach($credits as $collaborator): ?>
    <?php if ( $p = $collaborator->name()->toPage() ) : ?>
      <div class="profile">
        <div class="profile__image"><?= $p->image() ?></div>
        <div class="profile__meta">
          <h3 class="profile__name"><?= html::a($collaborator->url(), $p->title() ,['target'=>'_blank']) ?></h3>
          <h3 class="profile__function"><?= $collaborator->role()->html() ?></h3>
        </div>
      </div>
    <?php endif ?>
  <?php endforeach ?>
</section>

(On a side note, I don’t think it’s valid HTML to have two consecutive headings of the same level)

Yes, that would be fine if I were to need that specific profile code in a single template, but as I stated in my initial post; that profile is referenced in multiple templates. In some cases its referenced directly (in which case I don’t need to convert the page reference) and in some cases it’s referenced indirectly (in which case I do need to convert).

Ideally I just have a collection of profile objects I can iterate over and call a profile snippet with the profile object as a variable, having already taking care of converting the reference in the controller if necessary for that specific page…

But then we are back to pregenerating the array from the structure field as already suggested above (where array can also be turned into a collection again, that doesn’t matter).