Another wtf moment that cost me quite some time to find out. And, as usual with such problems, once you know how to ask the right question, the answer and solution is just round the corner, or in this case, literally right in front of your eyes - aka RTFM! :-)

In ProcessWire, there is a field in which you can reference other pages. Without going too deep into the (great) workings of ProcessWire and its API, this will offer the editor a select or multiselect of those pages you as the integrator has specified. Think of something like a collection of team members for example, where the info/data on each member is managed on its own page. Now on the page "team" you want to show those, but have the chance to select which of the team should be shown. This is where you may use the "pages" field.
By default, this field will "offer" the editor only published and hidden pages - these are pages that are usable in the front end, i.e. a visitor can see them and they have an url. Think "our-team/tom/". If the page "tom" is unpublished by the editor, a "not found 404" error will be returned. Usually not being able to select unpublished pages is exactly what you want.

But recently I had the need to a) select unpublished pages and b) show the data in the front end, even if the originating page of that data should not be reachable in the front end.

Luckily there is an option in the setup of the "pages" field which is disabled per default. A "enable unpublished pages" checkbox. With that option set, the unpublished pages will be selectable in the "pages" field. And ProcessWire is kind enough to give the info on those by adding "(unpublished)" behind the page title in the select.

But -- and this is what took me quite some time to find out -- even with the unpublished pages in the select, the resulting collection of pages that you can use in your templates for the front end won't have them. So if "team_select" is the name of the "pages" field, then

$team = $page->team_select;
foreach ( $team as $member ) {
  echo $member->name;

Will only echo the published and/or hidden members.

If you're familiar with the ProcessWire API and the selectors, you know that you can get to all pages with something like $pages->find('template=member,include=all');
But in the context of accessing the values/pages from the "pages" field, there is no way to add this "include=all". You can use $team = $page->get('team_select');, but "$team = $page->get('team_select,include=all');" or "$team = $page->get('team_select','include=all');" don't work and either return nothing or throw errors.

But with "$page->get('team_select');" we are very close to the actual solution, which is:


D'oh. Who would have thought? Well, everyone beside me who is actually reading the text NEXT TO THE OPTION in the field set-up. Mega-D'oh.

So, to use your template code as before, but with the selected unpublished pages included, this is needed:

$team = $page->getUnformatted('team_select');
foreach ( $team as $member ) {
  $member->of(true); // turn on formatting for the item
  echo $member->name;

What's up with this "formatting" anyway?

The way I understand it, ProcessWire will use "formatted" data for the front end. This includes several internal sanitizers and for example stuff like having nicely formatted date and time strings.
With getUnformatted this is bypassed and the "raw" data is returned. And this includes the unpublished pages in the case of the "pages" field.
Now, to make sure that the front end renders as before, it is a good idea to not use the unformatted data, but the re-apply the formatting that ProcessWire wanted to do anyway. This is what "->of()" is for.