Shared controller not working

I’m refactoring my code and i’m trying to setup shared controllers and moving some common logic into them. I think I have set it up right, according to this post by @jenstornell but I get a whoops when try to use it. It DOES however work, if I put the code into the controller that belongs to the template, without trying to share code (which is the way nature intended).

So for instance i have a newsarticle.php in the controllers folder containing this:

<?php

require_once kirby()->roots()->controllers() . '/shared/global.php';

My global.php looks like this:

<?php

return function($site, $pages, $page) {

  // Article Image
  $articleimgsrc = $page->articleimage()->toFile();

  if($articleimgsrc) {

    $articleimage = brick('img');

    $articleimage->attr('src', $articleimgsrc->url());
    $articleimage->attr('alt', $articleimgsrc->alt());
    $articleimage->attr('style', 'max-width:'.$articleimgsrc->width().'px');
    $articleimage->addClass('articleimage');

  } else {
    echo 'Article image is missing';
  }

  // pass these off to the template
  return compact('articleimage');

};

And my template tries to use it like this:

<?= $articleimage ?>

However, i get a whoops. If i put the contents of the global.php into the controller for newsarticle.php it works

What am i doing wrong?

$articleimage is not defined in the else-case.

I think you mean change it to this:

<?php

return function($site, $pages, $page) {

  // Article Image
  $articleimgsrc = $page->articleimage()->toFile();

  if($articleimgsrc) {

    $articleimage = brick('img');

    $articleimage->attr('src', $articleimgsrc->url());
    $articleimage->attr('alt', $articleimgsrc->alt());
    $articleimage->attr('style', 'max-width:'.$articleimgsrc->width().'px');
    $articleimage->addClass('articleimage');

  } else {
    $articleimage = 'Article image is missing';
  }

  // pass these off to the template
  return compact('articleimage');

};

I still get a whoops though, $articleimage undefined. It should not be getting that far anyway, because the image field does have a value. It only works if i put it directly into the controller belonging to the corresponding template.

I don’t get a variable notice but an headers already sent. If I change it to this:

template specific controller:

<?php
return function($site, $pages, $page) {
  require_once kirby()->roots()->controllers() . '/shared/global.php';

  return [
    'articles'   => $articles,
    'pagination' => $articles->pagination()
  ];

};

global.php

<?php
  $perpage  = $page->perpage()->int();
  $articles = $page->children()
                   ->visible()
                   ->flip();
                   ->paginate(($perpage >= 1)? $perpage : 5);


 }
 $articles = $articles->paginate(1);

it works.

Awesome. That did the trick! Thanks @texnixe

Actually, is there a way to pass options? I want to be able to crop or resize the image so can I do something like:

<?= $articleimage(600, 300) ?> // 600 width, 300 height

I cant see anything in the docs for doing something like that, is it possible?

I know i can do <?= $articleimage->resize(600, 300) ?> but i was looking to shorten the typing :slight_smile:

A variable is not a function. But you can, of course, create a function that you can pass your parameters to.

Right… this is driving me nuts.

So ive turned it into a function like this:

function imagetag($mode, $width, $height){

$articleimgsrc = $page->articleimage()->toFile();

if($articleimgsrc) {

  if ($mode == 'resize') {
    $articleimage = brick('img');
    $articleimgsrc = $articleimgsrc->resize($width, $height);
  }

  if ($mode == 'crop') {
    $articleimage = brick('img');
    $articleimgsrc = $articleimgsrc->focuscrop($width, $height);
  }

  $articleimage->attr('src', $articleimgsrc);
  $articleimage->attr('alt', $articleimgsrc->alt());
  $articleimage->attr('style', 'max-width:'.$articleimgsrc->width().'px');
  $articleimage->addClass('articleimage');

  } else {
    $articleimage = brick('p', 'Article Image Not Set');
  }

}

Which im using im my template like:

<?= imagetag('resize', 300, 200) ?>

But i get whoops because it cant see $page if i change that to page() i get an empty result… sigh … how can it work out side of a function and not side? Im guess i need to tell the function about $page but there i am lost…

Two things (if I haven’t overlooked anything, am a bit tired):

  • you need to pass the page to the function as a parameter
  • your function does not return anything, so it is quite useless;) It has to return $articleimage
function imagetag($page, $mode, $width, $height){
 // bla bla bla

return $articleimage;
}

Call

<?= imagetag($page, 'resize', 300, 200) ?>

Ahh of course.

yup… sums me up. Tired to… hot chocolate time…

So it goes further now i have done that… but i get a whoops lower down, when it hits $articleimage->attr('alt', $articleimgsrc->alt());

Call to undefined method Asset::alt()

Now let’s clean this up a bit to keep your code DRY:

function imagetag($page, $mode, $width, $height){


if($articleimgsrc = $page->articleimage()->toFile()) {

  if ($mode == 'resize') {
    $articleimgsrc = $articleimgsrc->resize($width, $height);
  }

  if ($mode == 'crop') {
    $articleimgsrc = $articleimgsrc->focuscrop($width, $height);
  }

  $articleimage = brick('img');
  $articleimage->attr('src', $articleimgsrc);
  $articleimage->attr('alt', $articleimgsrc->alt());
  $articleimage->attr('style', 'max-width:'.$articleimgsrc->width().'px');
  $articleimage->addClass('articleimage');

  } else {
    $articleimage = brick('p', 'Article Image Not Set');
  }
 return $articleimage;
}

You can’t call alt on the resized image.

function imagetag($page, $mode, $width, $height){


if($articleimgsrc = $page->articleimage()->toFile()) {

  if ($mode == 'resize') {
    $thumb = $articleimgsrc->resize($width, $height);
  }

  if ($mode == 'crop') {
    $thumb = $articleimgsrc->focuscrop($width, $height);
  }

  $articleimage = brick('img');
  $articleimage->attr('src', $thumb);
  $articleimage->attr('alt', $articleimgsrc->alt());
  $articleimage->attr('style', 'max-width:'.$thumb->width().'px');
  $articleimage->addClass('articleimage');

  } else {
    $articleimage = brick('p', 'Article Image Not Set');
  }
 return $articleimage;
}

Well thanks, very much :slight_smile: … still get the error above though.

See my last post…

Oh man…finally… no more whoops… thanks very much @texnixe

lol… i’m so tired i’ve been starring at this for hours, and didn’t even notice i haven’t added a width and height attribute…

… and so to bed.

Just noticed its building a malformed tag, but I’ll sort that tommorrow…

<img src="&lt;img src=&quot;//localhost:3002/thumbs/news/news-article-one/shareimagehome-300x158.png&quot; alt=&quot;&quot;&gt;" width="300" height="200" alt="" style="max-width:1200px" class="articleimage">

I just found another error. This line:

$articleimage->attr('src', $thumb);

should be

$articleimage->attr('src', $thumb->url());

This is working a treat now.

My only niggle is having to put $page in the tag every time:

<?= imagetag($page, 'crop', 1140, 400) ?>

Is there a way to put that somewhere else, since its always $page.?

You could use a page model, a custom page method or a custom file method instead, depending on where you want to use this function. If it is just related to one template, I’d opt for a page model.