Jump to content

Fatal error when outputting multiple page_reference fields


Manuel
 Share

Recommended Posts

Hi everybody!

I'm new to PW and just did the beginner-tutorial “Hello Worlds”, a beginning ProcessWire tutorial --> Link
After extending the template from the original tutorial with a page_reference-Field (Page-Field value type is configured as single page),
PW throws a fatal error when i try to output multiple pages:

$planets = $pages->find("template=planet, sort=-title");

foreach($planets as $planet) {
	echo $planet->title;
	echo $planet->planet_reference->title;  
}

Should return three Pages, but throws the following error:

Quote

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 262144 bytes) in /Applications/MAMP/htdocs/extranet/wire/core/Page.php on line 1651
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 262144 bytes) in Unknown on line 0

When i try to output the field with a single page, everything just works fine:

$planet = $pages->get(1018);

echo $planet->title;
echo $planet->planet_reference->title;

I've tried to solve the problem with myself (and google), but i can't fix the error.
Thanks a lot!

My enviroment:
OSX Mojave / MAMP (PHP 7.2), PW v 3.0.123

Link to comment
Share on other sites

Hi @Manuel and welcome to PW and the forum ? 

I don't see anything strange here. Could you paste the whole code? Are you sure you don't have any endless loop somewhere?

I'd also recommend that you install TracyDebugger - you can try snippets like the one posted above easily in the console: https://adrianbj.github.io/TracyDebugger/#/debug-bar?id=console

  • Like 1
Link to comment
Share on other sites

Hi @bernhard - and greetings from Tirol ?
Yes I've allready installed Tracy-Debugger and get the same error.
It can't be an endless loop, because, even if I enter the code directly into the tracy debugger console, the same error happens.
Two outputs work fine, after a third one a error occurs...

screenshot_1.png.7c58c48778494a6ccee068fbdbc27b6e.png
screenshot_2.png.77367c90b7ab4f42cc0c13629ce7c833.png

Thanks for your help!

Manuel

Edited by Manuel
Link to comment
Share on other sites

7 minutes ago, bernhard said:

Is planet_reference of page 1018 pointing to itself?

 

If that's indeed the case: To avoid this, in your pageref field definition, use "selectable pages" > "selector string" and then insert this condition: id!=page.id to avoid references to itself.

  • Like 1
Link to comment
Share on other sites

15 hours ago, bernhard said:

Is planet_reference of page 1018 pointing to itself?

BTW: Use dump() or the shorthand d() better than echo in the console ? 

Thank you for the hints! Unfortunately the error occures on every page, also on the backend (TracyDebugger Console).

Link to comment
Share on other sites

Here's a way to find all self-referencing page fields. Open your MySQL interface (MySQL Workbench, phpMyAdmin, adminer, whatever...) and execute the following query:

SET @oldgcmaxlen = @@group_concat_max_len;
SET SESSION group_concat_max_len = 32768;

SELECT GROUP_CONCAT(querystring SEPARATOR 'UNION\n') as sqlstring
FROM (
	SELECT 1 as something,
	CONCAT(
		'SELECT x.pages_id as id, y.name, z.data as title, \'',
        a.name,
        '\' as fieldname\n',
        'FROM field_',
		a.name,
		' x\n',
        'JOIN pages y ON y.id = x.pages_id JOIN field_title z ON z.pages_id = x.pages_id WHERE x.pages_id = x.data\n'
	) as querystring
	FROM fields a
	WHERE a.type = 'FieldtypePage'
) as inmemtbl
GROUP BY something
;

SET SESSION group_concat_max_len = @oldgcmaxlen;

The result will be a UNION query that checks all your page fields for direct recursions. Here's an example:

SELECT x.pages_id as id, y.name, z.data as title, 'permissions' as fieldname
FROM field_permissions x
JOIN pages y ON y.id = x.pages_id JOIN field_title z ON z.pages_id = x.pages_id WHERE x.pages_id = x.data
UNION
SELECT x.pages_id as id, y.name, z.data as title, 'roles' as fieldname
FROM field_roles x
JOIN pages y ON y.id = x.pages_id JOIN field_title z ON z.pages_id = x.pages_id WHERE x.pages_id = x.data
UNION
SELECT x.pages_id as id, y.name, z.data as title, 'language' as fieldname
FROM field_language x
JOIN pages y ON y.id = x.pages_id JOIN field_title z ON z.pages_id = x.pages_id WHERE x.pages_id = x.data
UNION
SELECT x.pages_id as id, y.name, z.data as title, 'pagesel' as fieldname
FROM field_pagesel x
JOIN pages y ON y.id = x.pages_id JOIN field_title z ON z.pages_id = x.pages_id WHERE x.pages_id = x.data

Copy that query into the SQL editor and execute it. If there are any recursions, you will see them listed with the page id, page name, page title and field name.

recursionexample.png.2f5bf2d48d661c51942543e065e3152a.png

  • Like 6
Link to comment
Share on other sites

1 hour ago, Manuel said:

Thank you for the hints! Unfortunately the error occures on every page, also on the backend (TracyDebugger Console).

This statement seems like you didn't get what I tried to say.

17 hours ago, dragan said:

Is planet_reference of page 1018 pointing to itself?

Means that you have maybe selected page 1018 in the page-reference-field "planet_reference" of page 1018. That means the planet_reference of 1018 points to page 1018 (itself) and could maybe end in an endless loop?

No idea if that is possible and it would throw such an error. I think PW should be clever enough to prevent such errors. If not, that would be worth an issue report!

  • Like 1
Link to comment
Share on other sites

47 minutes ago, Manuel said:

No - there is another id selected (1017).

And page 1017 references page 1018, right? It's a circular reference issue that seems to happen when two pages that reference each other are both loaded into memory. Probably connected to this issue: https://github.com/processwire/processwire-issues/issues/572

 

  • Like 1
Link to comment
Share on other sites

17 minutes ago, Robin S said:

And page 1017 references page 1018, right? It's a circular reference issue that seems to happen when two pages that reference each other are both loaded into memory. Probably connected to this issue: https://github.com/processwire/processwire-issues/issues/572

 

Hi @Robin S,

that would make sense. But after checking the references (only 4 pages), I can tell you they don't reference each other. Here's another screenshot.
The references are as follows:

earth (1016) --> mars (1017)
mars(1017) --> jupiter (1018)
jupiter(1018) --> merkur (1045)
merkur(1045) --> earth(1016)
screenshot.png.e0aa515e08cec5faf06647e921e839b1.png

After removing one of the d() functions as shown above, the fatal error doesn't occure.
The problem is, I want to get all pages with there references, not only n-1 ?

Link to comment
Share on other sites

29 minutes ago, Manuel said:

But after checking the references (only 4 pages), I can tell you they don't reference each other.

But they do. Not as directly as I first suggested, but there is a kind of circularity within the pages you are loading...

2019-04-18_002135.png.8dd43c264f6be780b30842eae4c45344.png

As soon as your loaded pages form a circularity the out of memory occurs. This kind of problem seems to happen particularly when a "single page" value type is defined in the field settings. I expect that if you change the field setting to "multiple pages" then the error won't occur.

2019-04-18_003019.png.f8f3ce7652f120af609bf01721f528ef.png

To be clear, I think that it ought to be possible to load pages like this using a single page field without any error occurring. I encourage you to make a report in the PW issues repo: https://github.com/processwire/processwire-issues/issues  add a comment to the existing issue: https://github.com/processwire/processwire-issues/issues/572

  • Like 2
Link to comment
Share on other sites

2 minutes ago, Robin S said:

I think that it ought to be possible to load pages like this using a singe page field without any error occurring

Wouldn't that be fixed too with the correction in the linked issue? The cited error (Wire.php line 1069) points to setTrackChanges.

  • Like 1
Link to comment
Share on other sites

2 minutes ago, BitPoet said:

Wouldn't that be fixed too with the correction in the linked issue? The cited error (Wire.php line 1069) points to setTrackChanges.

Ah, yes, you're right. So no need for a new issue, but maybe @Manuel might like to add a link to this topic in the issue discussion to hopefully bring the issue back to Ryan's attention. ?

Link to comment
Share on other sites

@Manuel, could you try commenting out the setTrackChanges method in core/Wire.php and replacing it with the following code?

	protected static $resetTrackChangesItems = array();

	public function resetTrackChanges($trackChanges = true) {
		$isRoot = count(self::$resetTrackChangesItems) == 0;
		
		self::$resetTrackChangesItems[$this->id] = true;
		
		parent::resetTrackChanges($trackChanges);
		foreach($this->data as $key => $value) {
			if(is_object($value) && $value instanceof Wire && $value !== $this) {
				if($value instanceof Page && isset(self::$resetTrackChangesItems[$value->id])) continue;
				$value->resetTrackChanges($trackChanges);
			}
		}
		
		if($isRoot) self::$resetTrackChangesItems = array();
		
		return $this; 
	}

 

  • Like 1
Link to comment
Share on other sites

7 hours ago, Manuel said:

Switching the field to Multiple Pages works fine, but for now the user is able to assign multiple planets instead of just one

The single/multiple value type determines the type of value returned by the field. The number of pages the user can assign in Page Edit is determined by the input field type. So it's possible to use a single page selection input type (e.g. select) with a multiple page value type. But regardless we all agree that the issue should be fixed for the single page value type.

Link to comment
Share on other sites

11 hours ago, Robin S said:

The single/multiple value type determines the type of value returned by the field. The number of pages the user can assign in Page Edit is determined by the input field type. So it's possible to use a single page selection input type (e.g. select) with a multiple page value type. But regardless we all agree that the issue should be fixed for the single page value type.

You're right, a select-input would be a neat workaround to prevent a user to assign multiple pages. I agree, issue should be fixed. As a newbie tat behaviour was a bit confusing to me. Thanks to everybodies help: @Robin S @adrian @BitPoet @bernhard ?

  • Like 1
Link to comment
Share on other sites

My final solution to output all fields as a JSON-Object:

 

<?php

$planets = $pages->find("template=planet, sort=-title");
$planets_array = array();

foreach($planets as $planet) {
    
	$moon_array = array();
	$moons = $planet->planet_moons;
	
	foreach($moons as $moon)
	{
		$moon_array[] = $moon->title;
	}
	
    $planets_array[] = array(
        'title' => $planet->title,
		'created' => date("H:i:s d.m.Y", $planet->created),
		'modified' => date("H:i:s d.m.Y", $planet->modified),
		'type' => $planet->planet_type,
        'age' => $planet->planet_age,
		'color' => ucfirst($planet->planet_color->title),
		'moons' => $moon_array,
		'summary' => $planet->planet_summary,
		'foto' => $_SERVER['SERVER_NAME'] . $planet->planet_foto->url,
		'planet-reference' => $planet->planet_reference[0]->title
    );
}

$planets_json = json_encode($planets_array, true);
echo $planets_json;

?>

Output (JSON):

[{
	"title": "Merkur",
	"created": "20:25:29 16.04.2019",
	"modified": "14:06:19 17.04.2019",
	"type": "W\u00fcstenplanet",
	"age": "800.812",
	"color": "Brown",
	"moons": ["Merkur Mond 1", "Merkur Mond 2"],
	"summary": "Beschreibung gibt es noch erst eine sehr kurze. hier gibt es noch nicht viel zu lesen...",
	"foto": "extranet\/site\/assets\/files\/1045\/merkur.jpg",
	"planet-reference": "Earth"
}, {
	"title": "Mars",
	"created": "17:54:58 07.04.2019",
	"modified": "14:06:08 17.04.2019",
	"type": "Terrestrial moon",
	"age": "4.500.000.712",
	"color": "Red",
	"moons": ["Phobos", "Deimos"],
	"summary": "Mars is the fourth round planet from the Sun...",
	"foto": "extranet\/site\/assets\/files\/1018\/mars.jpg",
	"planet-reference": "Merkur"
}, {
	"title": "Jupiter",
	"created": "17:28:14 07.04.2019",
	"modified": "14:05:52 17.04.2019",
	"type": "Great Gas Giant",
	"age": "4,5 Billion",
	"color": "Yellow",
	"moons": ["Io", "Europa", "Kallisto", "Ganymed", "Amalthea", "Himalia", "Elara"],
	"summary": "Jupiter is the fifth planet...",
	"foto": "extranet\/site\/assets\/files\/1017\/jupiter.jpg",
	"planet-reference": "Mars"
}, {
	"title": "Earth",
	"created": "17:13:23 07.04.2019",
	"modified": "14:05:40 17.04.2019",
	"type": "Terrestrial Planet",
	"age": "15.56 Billion",
	"color": "Purple",
	"moons": ["Moon"],
	"summary": "Earth (or the Earth) is the third planet from the Sun...",
	"foto": "extranet\/site\/assets\/files\/1016\/earth-1.jpg",
	"planet-reference": "Jupiter"
}]

 

  • Like 1
Link to comment
Share on other sites

Nice! ?

On mobile, but you can even make it simpler by using 

1 hour ago, Manuel said:

'moons' => $moon_array,

'moons' => $planet->planet_moons->each('title')

See the API docs for WireArray. It has some nice features ?

Link to comment
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
 Share

×
×
  • Create New...