Roots Discourse

Using different images in a partial depending on page template - best logic for this?

Hello. Still new to this and just want to set out on the right foot. Just hoping someone can sense check my logic here, and come up with some advice on the best way forward. I’ve followed the help docs as much as possible. I want to display a different image in a partial depending on which page template is loading the partial. I’m making the assumption that I need to somehow build the link through the @asset directive or similar or I will run into trouble at build time (???). The first thing I tried was passing variables into the page template:

    @include('partials.savvy-home-menu',  ['img_top' => 'images/banners/banner-bottom-coral.svg'])

Code in page template <img loading="lazy" src="{{ $img_top[0] }}">

This works nicely except BUT, I can’t use the asset directive inside the include statement to properly build the path so not perfect. (this is right, right? no blade inside blade?)

The next idea was to try and put the data into the app controller, and then also make another controller for page templates where I want to override the image returned with a different one. But it seems that the controller only works with ACF? Is this right? My tests were:

  public function getinvolved_menu_imgt()
    {
        return get_field('subtitle');
    }

//worked

    public function getinvolved_menu_imgt()
    {
        return "test";
    }

//failed

But of course, doh, the @asset directive again won’t work in this instance because I’m returning from a javascript function and I can’t use a blade directive in there. (right?)

So rifling through the help docs again, my next idea was to use a filter.

add_filter('sage/template/page/data', function (array $data) {
    $data['header_image'] = "image_url";

    return $data;
});

But this hits the same issue this is another javascript function and therefore the @asset directive won’t work either.

I’m wondering if there is a way in javascript to properly resolve the path like the asset directive does in blade? Or would the best solution be to make ACF for all these images (which I’d prefer not to do)?

Or, maybe I’m missing something really obvious here, would be super grateful for any guidance, assurance and confirmation on things I think I’m starting to understand!

This is not correct. Controller is not limited to ACF. It’s hard to debug why it didn’t work here because you haven’t provided any other information about how your Controller is set up, or what view it should be passing data to.

Where is the JavaScript here? You mention JavaScript several times but all of your code looks like PHP and I’m not sure where or how you’re using JavaScript.

I don’t totally understand this sentence, but the @asset directive is just a wrapper for the App\asset_path() function, which you can use in any PHP context.

1 Like

Thank you for your reply and sorry for the late response - just back from holiday.

Ok, now I know it should work I’ll look into it in more detail. I’ll make a separate post if appropriate.

You’re right, brain fart mentioning javascript. Was rushing a bit trying to get this solved before the holiday and posted too quickly… making silly errors! :woozy_face:

So great, that really helps clear things up for me. I can call the asset_path function directly, that makes perfect sense. The approach of using the controller seems like the most sense for me. So in my app/Controllers/App.php file I’ve tried:

public function getinvolved_menu_imgt()
{
    return asset_path('images/clouds-inkwell-t.svg');
}

Here is my template

{{--
  Template Name: Shop Pages Template
--}}

@extends('layouts.app')

@section('content')
  @while(have_posts()) @php the_post() @endphp
    @include('partials.page-header')
    @include('partials.content-page')
  @endwhile
@endsection

@section('fullrowsbottom')
    @include('partials.savvy-shop-hours')
    @include('partials.savvy-home-menu')
    @include('partials.savvy-fact')
@endsection

with this in my template part partials.savvy-home-menu

<img loading="lazy" title="Clouds Image" src="{!! $getinvolved_menu_imgt !!}"> 

However I get the following error message:

Fatal error: Uncaught Error: Call to undefined function App\Controllers\asset_path() in /Users/{user}/Websites/{url}/wp-content/themes/{theme}/app/Controllers/App.php:79 Stack trace: #0 /Users/{user}/Websites/{url}/wp-content/themes/{theme}/vendor/soberwp/controller/src/Controller.php(211): App\Controllers\App->getinvolved_menu_imgt() #1 /Users/{user}/Websites/{url}/wp-content/themes/{theme}/vendor/soberwp/controller/src/Controller.php(94): Sober\Controller\Controller->__setDataFromMethods() #2 /Users/{user}/Websites/{url}/wp-content/themes/{theme}/vendor/soberwp/controller/controller.php(69): Sober\Controller\Controller->__setData(Array) #3 /Users/{user}/Websites/{url}/wp-includes/class-wp-hook.php(292): Sober\Controller\{closure}(Array, '/Users/naomispi...') #4 /Users/{user}/Websites/{url}/wp-includes/plugin.php(212): WP_Hook->apply_filters(Array, Array) #5 /Users/naomis in /Users/{user}/Websites/{url}/wp-content/themes/{theme}/app/Controllers/App.php on line 79

For clarity here is the App.php file:

<?php

namespace App\Controllers;

use Sober\Controller\Controller;

class App extends Controller
{
    public function siteName()
    {
        return get_bloginfo('name');
    }

    public static function title()
    {
        if (is_home()) {
            if ($home = get_option('page_for_posts', true)) {
                return get_the_title($home);
            }
            return __('Latest Posts', 'sage');
        }
        if (is_archive()) {
            return get_the_archive_title();
        }
        if (is_search()) {
            return sprintf(__('Search Results for %s', 'sage'), get_search_query());
        }
        if (is_404()) {
            return __('Not Found', 'sage');
        }
        return get_the_title();
    }
    public static function blogDescription()
    {
        return get_bloginfo('description');
    }
    public static function subtitle()
    {
        return get_field('subtitle');
    }
    public static function sectionslug()
    {
        $sectionslug = "";
        if (is_page() ){
            if (is_front_page()) {
                $sectionslug = "home";
            }else if (get_post_parent()){
                $sectionslug = basename(get_permalink(get_post_parent()));
            }else{
                $sectionslug = basename(get_permalink());
            }
            
        }
        if (is_home() || is_single()){
            $sectionslug = "news-events";
        }
       return $sectionslug;
    }
    public static function sectiontitle()
    {
        if (is_page() ){
            if (is_front_page()) {
                $sectiontitle = "home";
            }else if (get_post_parent()){
                $sectiontitle = get_the_title(get_post_parent());
            }else{
                $sectiontitle = get_the_title();
            }
            
        }
        if (is_home() || is_single()){
            $sectiontitle = "News & Events";
        }
       return $sectiontitle;
    }

    public function getinvolved_menu_imgt()
    {
        $assetpath = asset_path('images/clouds-inkwell-t.svg');
        echo $assetpath;
        return $assetpath;
    }

}

So I’m not sure why it thinks the function is undefined? Do I have to somehow make App functions available to the template or partial?

I decided to try making it a static function to see if that helped

   public function getinvolved_menu_imgt()
        {
            $assetpath = asset_path('images/clouds-inkwell-t.svg');
            echo $assetpath;
            return $assetpath;
        }

But then I encounter an error, this time in the html

<img loading="lazy" title="Clouds Image" src="<br />
<b>Notice</b>:  Undefined variable: getinvolved_menu_imgt in <b>/Users/{user}/Websites/{website}/wp-content/uploads/cache/93c78dbb5e4048cc6b5c309c5694f4882c748cd9.php</b> on line <b>9</b><br />
">

Just not sure where to go from there.

So I thought I would go back to the approach with filters to have variables available to the template parts.

add_filter('sage/template/page/data', function (array $data) {
    $data['gi_menu_imgt'] = asset_path("images/clouds-inkwell-t.svg");
    return $data;
});

add_filter('sage/template/template-shop/data', function (array $data) {
    $data['gi_menu_imgt'] = asset_path("images/banner-bottom-coral.svg");
    return $data;
});

The asset_path function worked as expected, but unfortunately, when I was in the template-shop it returned the data for the page instead which I guess takes higher priority. I can just make the page the only data filter for that value and use an if statement to assign the correct value to the variable - unless there is a better way of doing this? This can be the way forward for me if advisable.

(sorry if I’m not explaining this well… 10 years of lone coding will do that to a person - not used to having to share my thought processes!)

Thanks for your time.
Naomi

I’d recommend reading our post on namespacing and autoloading: https://roots.io/namespacing-and-autoloading/

It’s throwing this error because asset_path() is in the App namespace: https://github.com/roots/sage/blob/9.x/app/helpers.php

You need to call it like:

\App\asset_path()

Ah perfect! I had actually tried that but without the backslash at the start. This works now, thank you! Have the controller method working nicely now for different variables per template, very slick. There are a couple of wee errors in the documentation around that, should I point them out?