“Call to a member function root() on null” when using object from cache

Hi!

I know this is not exactly the format one should ask questions here but maybe you have an idea on where to go next.

I’m currently adjusting the kirby-shopify plugin to the graphql api and it’s mostly working out. The only thing that’s left is storing the fetched products in a cache to no fetch the products on every pageload.

My function of fetching looks like this:

    public static function fetchProductsData($query){

        $shopifyApiCache = kirby()->cache('moritzebeling.kirby-shopify.api');
        $products        = $shopifyApiCache->get('products');

        if($products === null) {
    
            $products = [];
            $result = self::query($query);

            $products = array_map(function($product){
                $product = $product['node'];

                $product['priceRange'] = [
                    'min' => $product['priceRange']['minVariantPrice']['amount'],
                    'max' => $product['priceRange']['maxVariantPrice']['amount'],
                    'currencyCode' => $product['priceRange']['minVariantPrice']['currencyCode']
                ];

                $product['variants'] = array_map(function($variant){
                    $variant = $variant['node'];
                    $variant['price'] = $variant['priceV2'];
                    $variant['currencyCode'] = $variant['priceV2']["currencyCode"];
                    unset($variant['priceV2']);
                    return $variant;
                },$product['variants']['edges']);

                $product['images'] = array_map(function($image){
                    $image = $image['node'];
                    $image['src'] = $image['originalSrc'];
                    unset($image['originalSrc']);

                    return $image;
                } ,$product['images']['edges']);

                return $product;
            }, $result['products']);

            $shopifyApiCache->set('products', $products);
        }

        return $products;
    }

which gets called in the shopify.products pagemodel:

<?php

use Kirby\Data\Data;
use Kirby\Data\Yaml;

class ShopifyProductsPage extends Page
{
    public function subpages()
    {
        return Pages::factory($this->inventory()['children'], $this);
    }

    public function children()
    {

        $products = KirbyShopify::fetchProductsData();
        $productsPage = KirbyShopify::$productsPage;
        $pages = [];
        $counter = 0;

        foreach($products as $product){
          $slug = Str::slug($product['handle']);
          $kirbyProductRoot = $productsPage->root() . '/' . $counter . '_' . $slug . '/shopify.product.txt';
          $kirbyProduct = F::exists($kirbyProductRoot) ? new \Kirby\Toolkit\Collection(\Kirby\Data\Data::read($kirbyProductRoot)) : false;
          if($kirbyProduct) $kirbyProduct = $kirbyProduct->toArray();

          $shopifyProduct = [
            'title'                       => $product['title'],
            'shopifyTitle'                => $product['title'],
            'shopifyID'                   => $product['id'],
            'shopifyHandle'               => $product['handle'],
            'shopifyImages'               => \Kirby\Data\Yaml::encode($product['images']),
            'shopifyFeaturedImage'        => count($product['images']) > 0 ? \Kirby\Data\Yaml::encode([$product['images'][0]]) : '',
            'shopifyDescriptionHTML'      => $product['descriptionHtml'],
            'shopifyPrice'                => count($product['variants']) > 0 ? $product['variants'][0]['price']['amount'] : '',
            'shopifyType'                 => $product['productType'],
            'shopifyTags'                 => $product['tags'],
            'shopifyVariants'             => \Kirby\Data\Yaml::encode($product['variants']),
          ];

          if ($kirbyProduct) {
            foreach ($shopifyProduct as $k => $value) {
              unset($kirbyProduct[strtolower($k)]);
            }
          }

          $pages[] = [
              'slug'     => $slug,
              'num'      => $counter,
              'template' => 'shopify.product',
              'model'    => 'shopify.product',
              'content'  => $kirbyProduct ? array_merge($shopifyProduct, $kirbyProduct) : $shopifyProduct,
          ];

          $counter++;
        }

        return Pages::factory($pages, $this);
    }
}

With this code I get the error in the panel on the shopify.products page:

The section "products" could not be loaded: Call to a member function root() on null

What really boggles my mind is that when I test the two arrays, the one thats fetched directly and the other that should be stored in the cache, they are the same:

$shopifyApiCache = kirby()->cache('moritzebeling.kirby-shopify.api');
$productsCache = $shopifyApiCache->get('products');

$products = KirbyShopify::fetchProductsData();
$arraysAreEqual = $products === $productsCache;
var_dump($arraysAreEqual); // returns true

What exactly does the error message mean and what could I try next?

My page blueprint looks like this:

title: Products
options:
  delete: false
  changeTemplate: false
  changeStatus: true
sections:
  products:
    headline: Products
    type: pages
    info: "{{ page.shopifyHandle }}"
    template: shopify.product

It’s probably this line causing the error. And it means exactly what it says, you are calling the method root() which is a page method on null, so $productsPage, which is defined above with

doesn’t seem to return anything useful.

1 Like

Thanks @textnixe! That really was the problem. I have no idea why it worked when using the fetched products, but I just replaced the line

$productsPage = KirbyShopify::$productsPage;

with

$productsPage = kirby()->site()->pages()->filterBy('intendedTemplate', 'shopify.products')->first();