Jump to content

How to use a variable from one template in another

Go to solution Solved by diogo,

Recommended Posts

I only stumbled across WP a few weeks ago, but have already been able to accomplish far more than over months spent banging my head against the baroque code of several shall-remain-nameless frameworks. I could weep thinking of how much time I have wasted elsewhere.
I have, however, hit a speed bump--possibly due to the fact that I am still a bit shaky on how templates interact. 
My set-up (a sample travel site): 


"Interests” act a bit like tags assigned to the "poi"s (this is an internal tagging system, open to admin only; no free tags/users cannot assign). 
For the record, that odd "poi" nomenclature stands for "point of interest" (as a unique name, it's handier than using something vague like "item").
(Aside: Yes, I realize "see, do, etc." could exist instead as a third "Categories" tree. I fiddled with that, but in the end figured I'd just use the already powerful and straightforward parent/child nesting format of pages that PW provides. Also [ahem], I couldn’t get urlSegments to work; another time.)
A simple foreach loop on my _main.php page creates a link-list of the Interests assigned to a given poi. 
<?php foreach ($page->interests as $i) {
echo "<li><a href='{$i->url}'>{$i->title}</a></li>";
} ?>

When you click on these generated links, it takes you to the appropriate "Interest" page, populated by all POIs assigned that Interest. 

Here is the relevant part of my Interest.php code:
$int = $page->id;
    $pois = $pages->find("template=poi, interests=$int");
    $content .= renderNav($pois);  
Problem: I do not want to see _all_ the POIs assigned that Interest. I need to limit it to other POIs in this particular Place (the grandparent of the referring POI).
In other words, I only want to see other "Museums" (interest) in London, not those in Bath, Manchester, and Oxford as well.
This is easy to do in absolute terms (I can limit to "has_parent=London" or something); but I need it to be some kind of $place variable instead, one assigned by the referring POI.
For the life of me, I cannot figure out what code to use and where to stick it--in the foreach loop of the poi.php page? On the "Interests" template page? In short, how does this variable (the "Place" grandparent of a given POI) get assigned to the link and hence carry over to be able to be used in the functions being applied by another template (Interests)?
I suspect the answer is so glaringly obvious only an interactivity noob like me would not know. That would explain why I canot seem to find the answer after a solid week of trolling every forum thread, tutorial, and wiki at PW. 
While I’ve built my own static HTML sites for 20 years (wow, that seems long; anyone else remember dividing text blocks with HRs, or the glorious day HTML3 finally allowed us to align GIFs so text could actually flow around them?) I am only a journeyman at PHP and MySQL. I've never even created a form. I know. Shameful.
Thanks for your help.
Link to post
Share on other sites

Maybe you could use something with $page->parents to get which are the parent pages, and make a selector accordingly?

$parents = $page->parents;
$pois = $pages->find("parent=" .getPathString($parents). ",template=poi, interests=$int");

I included that getPathString() because $page->parents returns a PageArray that I don't know how if has a method to make it into a string like "/this/is/a/path/".

Link to post
Share on other sites
  • Solution

@elabx, not possible. We are in one of the interests pages at this point.


You have only two ways of passing info to the future page when pressing a url: the url itself or session. With session there is no way to create a permanent shareable page, so i would go for the url.

you can do this with url segments or using a get in the url referring to the place.


I would simulate with url segments that interests is at the same level of the tree as (do, see, etc) and link to there instead, leaving the interests pages without a template file. You could do it like this:

<?php foreach ($page->interests as $i) {
    $place = $page->closest("template=place"); //or whatever works
    $url = $place->url . "/interest_" . $i->name
    echo "<li><a href='{$url}'>{$i->title}</a></li>";
} ?>

On the template from the places pages you would have something like (rough draft):

if ($input->urlSegment1) {

    if (!substr($input->urlSegment1,0,9) == "interest_" ) throw new Wire404Exception();
    $reduced = str_replace("interest_", "", $input->urlSegment);
    $sanitized = $sanitizer->name($reduced);
    $int = $pages->get("name=$sanitized");
    $pois = $pages->find("template=poi, interests=$int");
    $content .= renderNav($pois);


Edit: And welcome to PW!

Edit2: corrected typo

  • Like 3
Link to post
Share on other sites

I think it can be much simpler:

Turn on url segments for interests, and link to /interests/interest1/PLACE/, where PLACE is the first url segment.

  $url = "/interests/interest/{$page->parent->parent->id}/";

  if ($input->urlSegment1){
    //validate that the url segment isn't bullshit
    $place = $pages->get((int)$input->urlSegment1);
    if (!$place->id || $place->template != 'place') { $session->redirect( … ); }

    $pois = $place->find('template=poi, interest={$page->id}');
    // here you go.

    // alternatively, if you want to fiter out the 'sender',
    // add it as second url segment (and after check&validation), use:
    $pois = $place->find("template=poi, interest={$page->id}, id!={$poi_from_segment_id}");
  • Like 2
Link to post
Share on other sites

Not sure if I would. Depending on how the site would be built I would probably use your solution and create the views for those. Probably with a listing of all the interests and a sublist of all the places in each interest, but then, maybe it makes more sense interests inside places and not the opposite. It really depends on the content.

  • Like 1
Link to post
Share on other sites

Thanks, guys.

I got the first halves of both suggestions working: Diogo's spoofing the system to think Interests was on the same level as Cat [see, Do, etc] (though I also had to remove the / before interests since my template's defaults are set to have trailing slashes and it was doubling them up), and Adam's fooling it into sticking the place at the end of an interests/interest url (though I tried a variation using the user-friendlier "name" instead of the nonsensical id number for place).

The second half of each of your fine suggestions, however, only produced frustration.

As I mentioned, I had no luck fiddling with urlSegments earlier, when I was trying to make a Categories tree work, and my failure stands. I am not sure why, and it looks as if I might have to solve this problem before being able to move forward on anything else.

Might it have anything to do with the profile I am using? I didn't just do a "Blank" install, but rather started building from the Default profile. It was close to where I was heading anyway, already had a top nav and all, and had sliced things into a _func.php for renders and a _main.php for page content, leaving all the template-based phps little more than containers to string together brief calls of $content .= [something].

As I said, I am new to the structure (and mindset) of building a site based on including files from other files, so I was happy to have someone else (Ryan) do this for me so I could muddle through figuring out how it worked as I went. However, given my noobiness, I may be missing something here? Is that setup what is fouling my attempts to use urlSegments? (I've already turned them on for pretty much every template in my experimentations, just to be sure that setting its not what's tripping me up.)

Diogo: I do not mind creating views for template levels. In fact, it might be best. I'd like the site to be navigable (no offense to the lovely "navigatable") in various ways, since no two people consume data the same way. Some want a hierarchy, others a flat map, still other visual cues. I just want to help people find the information they are looking for in whatever way makes the most sense to them.

Besides, in the long run I would like to have the "Interests" results reflect whatever level of "places" sent the request. So if you clicked on "History" from a page within, say, Florence, it would show all Florence results with the History "Interest" tag, but if you did so from within its parent in the Places tree, Tuscany, it would show all Tuscan results, and from its parent, Italy, all Italian results.  

I tried to keep my original question limited because I was hoping that learning a simple way to carry a set variable ("where are we?") over to another template would give me the tool to build the more complex site I had in mind than the example I gave.

However, just so you know where I am heading (and in case it changes your above suggestions), I envision these Interests acting kind of like sidebar filters, as you might see on an ecommerce site (size, brand, color, etc.). The user just ticks off all the ones she wants, and the site returns a page showing the POIs (if any) that match all her choices.


I am imagining the overall backend architecture to have three main page trees: Places, Categories, and Interests, each with two levels underneath (Places has cities, and under that the POIs; Categories and Interests both have main cats and interest groupings, and then more finely sliced subcats and interests under that). These would all interact. This makes things tricky, but I am sure if can be done!

The Places tree is where all the real contents lies; Categories helps organize it all; and Interests (and Categories and their subcats) are there to help users filter results more finely. (The Interest groupings are largely just to provide smaller subsections of Interest tags so that I don't end up with a single, leviathan list on the left sidebar!)


See [cat]
Monument [subcat]
Museum [subcat]
Do [cat]
Tour [subcat]
Get active
History [grouping]
Ancient [interest]
Gastronomy [grouping]
Food [interest]

You get the idea... The reason there are separate Interest and Categories trees is that Interests are horizontal tags, applicable to any POI across all categories (the Interest "Family-friendly" would apply to a Museum under "See" or a B&B under "Sleep"), whereas Categories are vertical (a POI has only one unique Category and subcat).

Thanks again for your help.

Link to post
Share on other sites

Oh, snap! Adam, your solution did, in fact, work. Before biting the bullet and doing a fresh install to try and track down my problem, I moused over the links one more time in hopes of a revelation... and got one! It was linking to interest/interest from the root of localhost, not from the site root. All I did was add a ".." before the initial slash in the $url call in poi.php and it worked!

$url = "../interests/interest/{$page->parent->parent->id}/"; //added ".." before /interests/interest


But the question remains: Will this be scalable, and work with the Grand Backend Architecture I mentioned above?

I mean, I am about to put on my hardhat and descend into the coding mines to see if I can make it work, but if you Jedi masters can tell at a glance whether or not this padawan will be wasting his time, please do let me know. (Sorry; just finished ordering the just-released Millennium Falcon LEGO set for my son's 8th birthday next month; they had sold out of the old one before last Christmas, and it had been the only thing on his list, so he has been waiting an eternity, in seven-year-old time.)

Link to post
Share on other sites

Ack! In my excitement, I posted too soon. While it is, indeed, now limiting things to just the appropriate "place," it is rendering all pois in the cat (see, do, etc.), not filtering them by interest at all. Also, I get an unwieldy url: "places/london/so/interests/interest/london," when it woudl be far better to have "places/london/do/interests/interest/[name of interest]."

I will tinker and see if I can't get it working properly. At least it is doing _something_ now!

Link to post
Share on other sites

Not much time here, but just to say that you can always link to a search page where you filter the results by get variables.

for example:

Link to post
Share on other sites

OK, after trying many permutations and substitutions, i think I am having a problem back at square one with Alex's solution. Where, exactly, do I stick that 

$url = "/interests/interest/{$page->parent->parent->id}/";

into my poi.php (or, rather, my _main.php that is being called into poi.php)? Is it in the foreach loop thusly:

foreach ($page->interests as $i) {
$url = "/interests/interest/{$page->parent->parent->id}/";
echo "<li><a href='{$url}'>{$i->title}</a></li>";

...because that doesn't work. It just sends me to a (non-existent) "interests/interest/1024" page (1024 is the id for London) at my localhost root.

I've tried jamming it into every level of the site by prefacing the folder with varying strings of "../../," but it doesn't seem to be calling the interest.php template no matter where I send it, from the site root (siteroot/interests/interest/1024) all the way to a sub of the current POI page (siteroot/places/london/do/poi2/interests/interest/1024).  

I've also tried changing things up, but to no avail (I keep feeling that the {$page->parent->parent->id} really should be something more like interest->name, since that is what we're actually talking about, but figured this hack was how we were going to piggyback the place into the url to be played with later).

Link to post
Share on other sites

You should probably take a pen, a paper, draw a sitemap and define a use case: (example) "when user clicks on interest on the page /places/london/see/, he should be taken to /interests/interest/{$name}, where he'll see other interests from this place".

Because otherwise this is a guessing game; ProcessWire allows you to structure your content any way you might want (and some you definitely do not want). Unless you have an idea what you want, every opinion will just confuse you.

On the other hand, when you'll have the sitemap (and well defined usecase), we can better assist you in achieving what you want/need.

Link to post
Share on other sites

Thanks, guys. A few tweaks to diogo's code got me the result I was hoping for. (I was simultaneously trying to figure out if I would instead hack a search function to get what I wanted—nice thing about PW is that you can find many avenues and answers to the same result—but this is far more useful and extensible.)   

And believe me, Adam. I have the whole thing intricately mapped out. Before I discovered PW, I had a labyrinthian MySQL database model all planned out, with all the one-to-many and many-to-many connections, and a spreadsheet of all the scripts to interconnect them. I;m just having to figure out how to apply PW's logic of pages, templates, template pages, and fields to the site I have in mind.

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By humanafterall
      I would like to set an admin template to 'https only' as recommended in the Processwire security docs.
      However if I do this it forces this setting locally too, resulting in https://localhost requests which result in an error page.
      Is there a simple way round this? Setting https for templates in the config?
    • By Chris Bennett
      Hi all, I am going round and round in circles and would greatly appreciate if anyone can point me in the right direction.
      I am sure I am doing something dumb, or missing something I should know, but don't. Story of my life 😉

      Playing round with a module and my basic problem is I want to upload an image and also use InputfieldMarkup and other Inputfields.
      Going back and forth between trying an api generated page defining Fieldgroup, Template, Fields, Page and the InputfieldWrapper method.

      InputfieldWrapper method works great for all the markup stuff, but I just can't wrap my head around what I need to do to save the image to the database.
      Can generate a Field for it (thanks to the api investigations) but not sure what I need to do to link the Inputfield to that. Tried a lot of stuff from various threads, of varying dates without luck.
      Undoubtedly not helped by me not knowing enough.

      Defining Fieldgroup etc through the api seems nice and clean and works great for the images but I can't wrap my head around how/if I can add/append/hook the InputfieldWrapper/InputfieldMarkup stuff I'd like to include on that template as well. Not even sure if it should be where it is on ___install with the Fieldtype stuff or later on . Not getting Tracy errors, just nothing seems to happen.
      If anyone has any ideas or can point me in the right direction, that would be great because at the moment I am stumbling round in the dark.
      public function ___install() { parent::___install(); $page = $this->pages->get('name='.self::PAGE_NAME); if (!$page->id) { // Create fieldgroup, template, fields and page // Create new fieldgroup $fmFieldgroup = new Fieldgroup(); $fmFieldgroup->name = MODULE_NAME.'-fieldgroup'; $fmFieldgroup->add($this->fields->get('title')); // needed title field $fmFieldgroup->save(); // Create new template using the fieldgroup $fmTemplate = new Template(); $fmTemplate->name = MODULE_NAME; $fmTemplate->fieldgroup = $fmFieldgroup; $fmTemplate->noSettings = 1; $fmTemplate->noChildren = 1; $fmTemplate->allowNewPages = 0; $fmTemplate->tabContent = MODULE_NAME; $fmTemplate->noChangeTemplate = 1; $fmTemplate->setIcon(ICON); $fmTemplate->save(); // Favicon source $fmField = new Field(); $fmField->type = $this->modules->get("FieldtypeImage"); $fmField->name = 'fmFavicon'; $fmField->label = 'Favicon'; $fmField->focusMode = 'off'; $fmField->gridMode = 'grid'; $fmField->extensions = 'svg png'; $fmField->columnWidth = 50; $fmField->collapsed = Inputfield::collapsedNever; $fmField->setIcon(ICON); $fmField->addTag(MODULE_NAME); $fmField->save(); $fmFieldgroup->add($fmField); // Favicon Silhouette source $fmField = new Field(); $fmField->type = $this->modules->get("FieldtypeImage"); $fmField->name = 'fmFaviconSilhouette'; $fmField->label = 'SVG Silhouette'; $fmField->notes = 'When creating a silhouette/mask svg version for Safari Pinned Tabs and Windows Tiles, we recommend setting your viewbox for 0 0 16 16, as this is what Apple requires. In many cases, the easiest way to do this in something like illustrator is a sacrificial rectangle with no fill, and no stroke at 16 x 16. This forces the desired viewbox and can then be discarded easily using something as simple as notepad. Easy is good, especially when you get the result you want without a lot of hassle.'; $fmField->focusMode = 'off'; $fmField->extensions = 'svg'; $fmField->columnWidth = 50; $fmField->collapsed = Inputfield::collapsedNever; $fmField->setIcon(ICON); $fmField->addTag(MODULE_NAME); $fmField->save(); $fmFieldgroup->add($fmField); // Create: Open Settings Tab $tabOpener = new Field(); $tabOpener->type = new FieldtypeFieldsetTabOpen(); $tabOpener->name = 'fmTab1'; $tabOpener->label = "Favicon Settings"; $tabOpener->collapsed = Inputfield::collapsedNever; $tabOpener->addTag(MODULE_NAME); $tabOpener->save(); // Create: Close Settings Tab $tabCloser = new Field(); $tabCloser->type = new FieldtypeFieldsetClose; $tabCloser->name = 'fmTab1' . FieldtypeFieldsetTabOpen::fieldsetCloseIdentifier; $tabCloser->label = "Close open tab"; $tabCloser->addTag(MODULE_NAME); $tabCloser->save(); // Create: Opens wrapper for Favicon Folder Name $filesOpener = new Field(); $filesOpener->type = new FieldtypeFieldsetOpen(); $filesOpener->name = 'fmOpenFolderName'; $filesOpener->label = 'Wrap Folder Name'; $filesOpener->class = 'inline'; $filesOpener->collapsed = Inputfield::collapsedNever; $filesOpener->addTag(MODULE_NAME); $filesOpener->save(); // Create: Close wrapper for Favicon Folder Name $filesCloser = new Field(); $filesCloser->type = new FieldtypeFieldsetClose(); $filesCloser->name = 'fmOpenFolderName' . FieldtypeFieldsetOpen::fieldsetCloseIdentifier; $filesCloser->label = "Close open fieldset"; $filesCloser->addTag(MODULE_NAME); $filesCloser->save(); // Create Favicon Folder Name $fmField = new Field(); $fmField->type = $this->modules->get("FieldtypeText"); $fmField->name = 'folderName'; $fmField->label = 'Favicon Folder:'; $fmField->description = $this->config->urls->files; $fmField->placeholder = 'Destination Folder for your generated favicons, webmanifest and browserconfig'; $fmField->columnWidth = 100; $fmField->collapsed = Inputfield::collapsedNever; $fmField->setIcon('folder'); $fmField->addTag(MODULE_NAME); $fmField->save(); $fmFieldgroup->add($tabOpener); $fmFieldgroup->add($filesOpener); $fmFieldgroup->add($fmField); $fmFieldgroup->add($filesCloser); $fmFieldgroup->add($tabCloser); $fmFieldgroup->save(); /////////////////////////////////////////////////////////////// // Experimental Markup Tests $wrapperFaviconMagic = new InputfieldWrapper(); $wrapperFaviconMagic->attr('id','faviconMagicWrapper'); $wrapperFaviconMagic->attr('title',$this->_('Favicon Magic')); // field show info what $field = $this->modules->get('InputfieldMarkup'); $field->name = 'use'; $field->label = __('How do I use it?'); $field->collapsed = Inputfield::collapsedNever; $field->icon('info'); $field->attr('value', 'Does this even begin to vaguely work?'); $field->columnWidth = 50; $wrapperFaviconMagic->add($field); $fmTemplate->fields->add($wrapperFaviconMagic); $fmTemplate->fields->save(); ///////////////////////////////////////////////////////////// // Create page $page = $this->wire( new Page() ); $page->template = MODULE_NAME; $page->parent = $this->wire('pages')->get('/'); $page->addStatus(Page::statusHidden); $page->title = 'Favicons'; $page->name = self::PAGE_NAME; $page->process = $this; $page->save(); } }  
    • By benbyf
      Hello, and welcome to what I though was either my client being silly and changing things, or some evil doer. Turns out its reproducible and therefore something in Proceswire (I checked my templates and modules but couldnt find anything that would be doing this...). So what is it doing? Check out the video for evidence.
      A repeater field is interacting with a page template and another repeater field somehow to swap the fields in the template and repeater over...
      I have a template called team, and a repeater field called team_repeater with label Team. Some how and for some reason, when I change my fields on repeater called main_menu_links my team template gets those fields and when I try and revert the team template fields to the fields it should have, they get given to the repeater main_menu_links. Also this to say HELP!!!!!
      video: https://www.dropbox.com/s/exkdhc6n7x0xpsa/strange-repeater-PW-mega-bug.mov?dl=0
    • By Robin S
      Displays image tags overlaid on the thumbnail using customisable colours. This makes it easier to see which images have which tags without needing to open the edit pane for individual images or changing to the list view.

      Enable tags for one or more image fields. Install the Image Thumbnail Tags module. Optionally configure colours for any of your tags.
    • By Andi
      Continuing my journey into PW hooks, I'm trying to find a way to retrieve all images from a page that explicitly *do not* have a certain tag (or tags) attached to them.
      Found this post from 2015
      But I'm wondering if there's a more elegant way to go about this.
      Let's say I have a multi-image field called "images_header" and instead of
      $page->images_header->findTag('mytag'); I would like to do this:
      $page->images_header->excludeTag('mytag'); So I'd be able to do
      // find images that don't have the tag "mytag" $images = $page->images_header->excludeTag('mytag'); // check if there's any images if (count($images)>0) { // do something.. } Would this be possible by hooking into Pagefiles somehow?
      There's this bit in /wire/core/Pagefiles.php Line 626 that I'd basically just need to reverse (or at least in my mind 😄 )
      public function findTag($tag) { $items = $this->makeNew(); foreach($this as $pagefile) { if($pagefile->hasTag($tag)) $items->add($pagefile); } return $items; } Any ideas on how this could be done in a graceful manner?
      Thanks in advance!
  • Create New...