Error: Call to protected Timber\Post::__construct() from global scope
luukee opened this issue · comments
We are updating our theme from Timber 1.0 to 2.0 but we cannot seem to get past the Call to protected Timber\Post::__construct()
error.
We are trying our best to follow the Upgrade to 2.0 docs (extending Timber, class maps, etc.)
Main point in our code where the issue lies (the links ex. /archive.php: are to the repo code):
$context = Timber::context();
$options = [];
$globals = new Engage\Managers\Globals();
use Engage\Models\Archive;
$teamGroups = [];
$options = [
'filters' => $globals->getVerticalMenu(get_query_var('verticals'))
];
$context['archive'] = new Archive($options);
Timber::render( 'archive.twig', $context );
namespace Engage\Models;
class Archive extends \Timber\Post
{
public $posts,
$pagination,
$slug,
$intro = [],
$vertical = false,
$category = false;
public function init($query = false)
{
$this->setVertical();
$this->setCategory();
$this->setPostType();
parent::__construct($query);
$this->setQueriedObject();
$this->posts = $this->queryIterator->get_posts();
$this->pagination = $this->pagination();
$this->taxonomy = $this->queriedObject->taxonomy;
$this->slug = $this->queriedObject->slug;
$this->setIntro();
}
functions.php classmap
use Engage\Models\Archive; // Import your custom Archive class
add_filter('timber/post/classmap', function ($classmap) {
$custom_classmap = [
'research' => Archive::class, // 2. Define a custom class map for the 'research' post type
];
return array_merge($classmap, $custom_classmap); // 3. Merge the custom class map with the default class map
});
Managing Timber with composer & using Timber 2.x-dev.
If I did not provide enough info/code/error logs please let me know and I will update the question. Any help is very much appreciated.
https://github.com/timber/timber/blob/dbb7139152ac95f67c7bd1cbf354276a59d82842/src/Post.php#L213
I think you are calling the above constructor as parent::__construct($query);
This is how to create an Archive class in v2
https://github.com/timber/timber/blob/dbb7139152ac95f67c7bd1cbf354276a59d82842/src/Archives.php#L90-L102
Good point @szepeviktor. But the curious thing is, when I switch to the __construct method I get the same exact error.
Let me send more code & error output when I'm back at my computer. . .
Thank you for commenting 👍
I think you’re mixing different things. Your Archive
class extends Timber\Post
, so that would mean that you would use this to work with single posts. But then you call this->posts = $this->queryIterator->get_posts();
and $this->pagination = $this->pagination();
inside the init()
function of your post. What do you want to do with that?
Should your archive.php display multiple archive posts? Or should Archive
be a class that handles multiple posts?
If your Archive
class should handle multiple posts, then you probably shouldn’t extend Timber\Post
.
If Archive
should act as the class for posts in archive.php, then you shouldn’t instantiate the post directly.
$context['archive'] = new Archive($options);
You’ve added a class map, which means you can use Timber::get_post( $post_id )
to get an archive posts and Timber::get_posts()
to get the collection of archive posts if you’re in archive.php.
public function init($query = false) {
// ...
parent::__construct($query);
}
If you’re overwriting the init()
method, you should call parent::init()
, but not parent::construct()
. If you extend Timber\Post
and run parent::construct()
, then it will do nothing, because we use empty constructors.
Thank you @gchtr for checking out my issue. I've been trying everything I can come up with to no avail.
What do you want to do with
this->posts = $this->queryIterator->get_posts();
and$this->pagination = $this->pagination();
inside theinit()
function?
These are actually used to build the sidebar taxonomy filters/categories.
Should your archive.php display multiple archive posts?
Yes, it should show a list of archive research
posts with a sidebar taxonomy filter menu. Here's an screenshot:
Here's the Timber 1.0 original src/Models/Archive.php
constructor:
public function __construct($query = false, $class = 'Engage\Models\Article')
{
// Set the 'verticals' taxonomy term
$this->setVertical();
$this->setCategory();
// Set the post type
$this->setPostType();
parent::__construct($query, $class);
$this->setQueriedObject();
$this->posts = $this->queryIterator->get_posts();
$this->pagination = $this->pagination();
// Assign the taxonomy term. ex. 'verticals' tax
// "$this->queriedObject" is a WP_Term
$this->taxonomy = $this->queriedObject->taxonomy;
// Set the slug ex. 'journalism'
$this->slug = $this->queriedObject->slug;
// Set the intro ex {{ archive.intro }} - tax term, tax title, tax excerpt
$this->setIntro();
}
Here's the original (Timber 1.0) archive.php
file:
$context = Timber::get_context();
$options = [];
$globals = new Engage\Managers\Globals();
$articleClass = 'Engage\Models\Article';
$teamGroups = [];
$options = [
'filters' => $globals->getVerticalMenu(get_query_var('verticals'))
];
// build intro
$query = false;
$archive = new Engage\Models\TileArchive($options, $query, $articleClass);
$context['archive'] = $archive;
Timber::render( ['archive.twig'], $context, ENGAGE_PAGE_CACHE_TIME);
getVerticalMenu
from src/Managers/Globals.php
:
public function getVerticalMenu($vertical) {
$menu = get_transient('vertical-filter-menu--'.$vertical);
if(!empty($menu)) {
return $menu;
}
$vertical = get_term_by('slug', $vertical, 'verticals');
// The filter menu will be built in this order
$postTypes = [ 'research', 'blogs', 'announcement', 'tribe_events', 'post', 'team' ];
$posts = new Timber\PostQuery([
'post_type' => $postTypes,
'tax_query' => [
[
'taxonomy' => 'verticals',
'field' => 'slug',
'terms' => $vertical->slug
]
],
'posts_per_page' => -1
]);
$options = [
'title' => $vertical->name,
'slug' => $vertical->slug.'-menu',
'posts' => $posts,
'taxonomies' => ['research-categories', 'blogs-category', 'announcement-category', 'tribe_events_cat', 'category', 'team_category'],
'postTypes' => $postTypes
];
// we don't have the vertical menu, so build it
$filters = new \Engage\Models\FilterMenu($options);
$menu = $filters->build();
set_transient('vertical-filter-menu--'.$vertical->slug, $menu );
return $menu;
}
Lastly the src/Models/TileArchive.php
file:
namespace Engage\Models;
class TileArchive extends Archive
{
public $filters = []; // when you want things organized by vertical
public function __construct($options, $query = false, $class = 'Engage\Models\Article')
{
$defaults = [
'filters' => []
];
$options = array_merge($defaults, $options);
$this->filters = $options['filters'];
parent::__construct($query, $class);
// loop through the posts and if it's an event, set it as the event model instead
foreach($this->posts as $key => $val) {
if($val->post_type === 'tribe_events') {
$this->posts[$key] = new Event($val->ID);
}
}
// This is usually already set from a global. If it's empty, then there's no sidebar
if(!empty($this->filters)) {
// get the current filter menu item
$this->setCurrentFilter();
}
}
// set the current filter based on the archive
// this is way too confusing, but seems to work fine... :/
public function setCurrentFilter() {
// search for the current slug.
// If we're displaying all verticals, we'll be looking for the vertical slug as the current match.
// if it's by postType, then we're looking for the current displayed postType
// Needed to add ability to add team category info to array and filter css.
// Might consider re-doing some of the filter stuff to acount for things like these.
if ($this->filters['structure'] === 'vertical') {
if (isset($this->vertical->slug)) {
$currentSlug = $this->vertical->slug;
} elseif (isset($this->category->slug)) {
$currentSlug = $this->category->slug;
}
} else {
$currentSlug = $this->postType->name;
}
if($this->filters['terms']) {
foreach($this->filters['terms'] as $parentTerm) {
if($currentSlug === $parentTerm['slug']) {
// found the parent match!
$this->filters['terms'][$parentTerm['slug']]['currentParent'] = true;
// now see if this is just the current parent or actually the current one
if($this->category->taxonomy === 'verticals') {
$this->filters['terms'][$parentTerm['slug']]['current'] = true;
} else {
if(!empty($parentTerm['terms'])) {
// let's find the child
foreach($parentTerm['terms'] as $childTerm) {
if($childTerm['slug'] === $this->category->slug) {
$this->filters['terms'][$parentTerm['slug']]['terms'][$this->category->slug]['current'] = true;
break;
}
}
}
}
break;
}
}
}
}
}
Also, if I {{ dump(archive) }}
from the archive.twig
file on the Timber 1.0 theme I get this:
slug: "journalism"
intro: array:3 [▼
"vertical" => WP_Term {[#5943 ▶](http://localhost:3003/vertical/journalism/research/#sf-dump-1957750531-ref25943)}
"title" => "Journalism"
"excerpt" => ""
]
vertical: WP_Term {[#5943 ▼](http://localhost:3003/vertical/journalism/research/#sf-dump-1957750531-ref25943)
+term_id: 240
+name: "Journalism"
+slug: "journalism"
+term_group: 0
+term_taxonomy_id: 240
+taxonomy: "verticals"
+description: ""
+parent: 0
+count: 184
+filter: "raw"
}
category: WP_Term {[#5942 ▶](http://localhost:3003/vertical/journalism/research/#sf-dump-1957750531-ref25942)}
filters: array:5 [▼
"title" => "Journalism"
"slug" => "journalism-menu"
"structure" => "postTypes"
"link" => false
"terms" => array:3 [ …3]
]
My attempt is to migrate this code to Timber 2.0, but running into troubles with the class maps & using the classes on the archive.php
file. I really appreciate your help.
I can always provide more into if you need.
@szepeviktor @gchtr I was able to resolve the issue. I will share the fix when I get back to work tomorrow.
Thanks for your help.
Glad you were able to fix your issue @luukee , I will close the issue here but please do share your fix.
Of course @Jonathan-Scapin & @Levdbas. With the help from @gchtr I was able to get Timber 2.0 working on our theme.
All code below is compared to my original post,
src/Models/Archive.php
I was able to replace my constructor (__construct
) with init
and excluded the $class
parameter. Then for the constructor's parent::__construct($query, $class);
I just removed the $class
parameter and lastly instead of getting the posts with $this->queryIterator->get_posts()
I used Timber::get_posts($query)
.
These are three changes you'll want to take note of for any other files with similar structure.
Original (Timber 1.0):
public function __construct($query = false, $class = 'Engage\Models\Article')
{
// other code
parent::__construct($query, $class);
$this->setQueriedObject();
$this->posts = $this->queryIterator->get_posts();
// other code
}
Updated (Timber 2.0):
public function init($query)
{
// other code
parent::__construct($query);
$this->setQueriedObject();
$this->posts = Timber::get_posts($query); // Get posts using Timber
// other code
}
archive.php
For archive.php
file:
Original (Timber 1.0):
$context = Timber::get_context();
$options = [];
$globals = new Engage\Managers\Globals();
$articleClass = 'Engage\Models\Article';
$archive = new Engage\Models\TileArchive($options, $query, $articleClass);
$context['archive'] = $archive;
Timber::render( ['archive.twig'], $context, ENGAGE_PAGE_CACHE_TIME);
Updated (Timber 2.0):
$context = Timber::context();
$options = [];
$globals = new Engage\Managers\Globals();
global $wp_query; // Get WP global query
use Engage\Models\TileArchive; // from class maps in functions.php
// remove $articleClass
// same code
$archive = new TileArchive( $options, $wp_query );
$context['archive'] = $archive;
Timber::render( ['archive.twig'], $context, ENGAGE_PAGE_CACHE_TIME);
src/Managers/Globals.php
For my src/Managers/Globals.php
file I only updated the $posts
var:
Original (Timber 1.0):
public function getVerticalMenu($vertical) {
// other code
$posts = new Timber\PostQuery([
// other props
]);
// other code
}
Updated (Timber 2.0):
public function getVerticalMenu($vertical) {
// same code
$posts = Timber::get_posts([
// same props
]);
// same code
}
src/Models/TileArchive.php
I left the constructor, removed the $className
param and called the parent init
function instead of __construct
:
Original (Timber 1.0):
public function __construct($options, $query = false, $class = 'Engage\Models\Article')
{
parent::__construct($query, $class);
}
Updated (Timber 2.0):
public function __construct($query = false, $options')
{
parent::init($query, $options);
}
I hope that helps. I tried to outline everything I changed to get it working. Let me know if you have any other questions.