Home>Support>Recursive widget

Recursive widget

Is there a way to make a widget call itself recursively? I am trying to build something similar to the native menus of WordPress, but I cant seem to get it done.

This is the code of what I have tried:

class SCTSubsections extends SiteOrigin_Widget
{

    function __construct()
    {
        parent::__construct(
            'sct-subsections',
            __('Subsections', 'sct-subsections-text-domain'),
            [
                'description' => __('Subsections', 'sct-subsections-text-domain'),
                'panels_title' => false,
                'panels_groups' => ['dummy-widgets'],
            ],
            [],
            [
                'title' => [
                    'type' => 'text',
                    'label' => __('title', 'sct-subsections-text-domain'),
                    'default' => ''
                ],
                'sections' => array(
                    'type' => 'repeater',
                    'label' => __( 'Sections.' , 'sct-subsections-text-domain' ),
                    'item_name'  => __( 'Section', 'siteorigin-widgets' ),
                    'item_label' => array(
                        'selector'     => "[id*='repeat_text']",
                        'update_event' => 'change',
                        'value_method' => 'val'
                    ),
                    'fields' => array(
                        'section' => [
                            'type' => 'widget',
                            'label' => __( 'Subsections.', 'sct-subsections-text-domain' ),
                            'class' => 'SCTSubsections',
                        ],
                    )
                ),
            ],
            plugin_dir_path(__FILE__)
        );
    }
}

siteorigin_widget_register('sct-subsections', __FILE__, 'SCTSubsections');

But this do not seem to be working. When this code is present, WordPress just returns me a blank screen. Am I going about this the wrong way?

This is our free support forum. Replies can take several days. If you need fast email support, please purchase a SiteOrigin Premium license.

  1. 4 years, 6 months ago Alex S
    Hi, I Work Here

    Hi Enrique,

    The provided PHP is likely resulting in an infinite loop – the widget is infinitely loading and embedding a new instance of itself. It is possible to make a widget load itself again but you need to add something in place to prevent this from happening infinitely. What that something could be I’m not too sure. Can you please elaborate on what you’re specifically trying to do? Can you partially offload this into two widgets (one of which doesn’t load itself again)?

  2. 4 years, 6 months ago Enrique Moreno

    Well, it is only an infinite loop in the repeater has more than 0 elements, no? If the repeater is empty, there should be no infinite loop.

    It is kinda like recursion, where you have a “base case” that stops the loop.

  3. 4 years, 6 months ago Enrique Moreno

    About the “separate in 2 widgets” technique: I had already thought of that and the outcome is exactly the same.

    What I am trying to build is a menu, like the ones that WordPress uses. It is really an index for a document, where each index may (or may not) have subsections. I am going to use it to set links to different pages inside a PDF file.

  4. 4 years, 6 months ago Alex S
    Hi, I Work Here

    Hi Enrique,

    In theory, yes. However, we need to generate the repeater template when the widget form options are loaded and that will result in the repeater repeatability loading the same widget over and over again.

    Can you please provide me with a copy of the second widget code so I can check over that? Ideally, if possible, can you please provide me with a copy of your entre plugin? This will allow me to test using your copy and rule out any structural differences caused by the (potentially) different method of adding the widget(s) code.

  5. 4 years, 6 months ago Enrique Moreno

    This is what I have tried to do, with the “separate in 2 widgets” strategy:

    <?php
    
    /*
    Widget Name: SCT - Section
    Description: SCT - Section
    Author: Enrique Moreno Tent
    Author URI: https://enriquemorenotent.com
    */
    
    class SCTSection extends SiteOrigin_Widget
    {
    
        function __construct()
        {
            parent::__construct(
                'sct-section',
                __('SCT - Section', 'sct-section-text-domain'),
                [
                    'description' => __('SCT - Section', 'sct-section-text-domain'),
                    'panels_title' => false,
                    'panels_groups' => array('sct-widgets'),
                ],
                [],
                [
                    'name' => [
                        'type' => 'text',
                        'label' => __('Section name', 'sct-section-text-domain'),
                    ],
                    'subsections' => [
                        'type' => 'widget',
                        'label' => __('Subsections', 'sct-section-text-domain'),
                        'class' => 'SCTListOfSections'
                   ],
                ],
                plugin_dir_path(__FILE__)
            );
        }
    }
    
    siteorigin_widget_register('sct-section', __FILE__, 'SCTSection');
    
    <?php
    
    /*
    Widget Name: SCT - List of sections
    Description: SCT - List of sections
    Author: Enrique Moreno Tent
    Author URI: https://enriquemorenotent.com
    */
    
    class SCTListOfSections extends SiteOrigin_Widget
    {
    
        function __construct()
        {
            parent::__construct(
                'sct-list-of-sections',
                __('SCT - List of sections', 'sct-list-of-sections-text-domain'),
                [
                    'description' => __('SCT - List of sections', 'sct-list-of-sections-text-domain'),
                    'panels_title' => false,
                    'panels_groups' => array('sct-widgets'),
                ],
                [],
                [
                    'sections' => [
                        'type' => 'repeater',
                        'label' => __('Sections', 'sct-list-of-sections-text-domain'),
                        'item_name'  => __('Section', 'sct-list-of-sections-text-domain'),
                        'item_label' => [
                            'selector'     => "[id*='repeat_text']",
                            'update_event' => 'change',
                            'value_method' => 'val'
                        ],
                        'fields' => [
                            'file' => [
                                'type' => 'widget',
                                'label' => __('Subsection', 'sct-list-of-sections-text-domain'),
                                'class' => 'SCTSection'
                            ],
                        ]
                    ],
                ],
                plugin_dir_path(__FILE__)
            );
        }
    }
    
    siteorigin_widget_register('sct-list-of-sections', __FILE__, 'SCTListOfSections');
    
  6. 4 years, 6 months ago Alex S
    Hi, I Work Here

    Hi Enrique,

    Thanks. I can confirm an infinite loop is occurring with the provided widgets file:

    [17-Mar-2020 16:48:45 UTC] PHP Fatal error:  Uncaught Error: Maximum function nesting level of '256' reached, aborting! in C:\Users\Alex\Documents\local-sites\siteorigin\app\public\wp-includes\formatting.php:2331
    Stack trace:
    #0 C:\Users\Alex\Documents\local-sites\siteorigin\app\public\wp-includes\formatting.php(2331): preg_replace('|%[a-fA-F0-9][a...', '', 'siteorigin-widg...')
    #1 [internal function]: sanitize_html_class('siteorigin-widg...')
    #2 C:\Users\Alex\Documents\local-sites\siteorigin\app\public\wp-content\plugins\so-widgets-bundle\base\inc\fields\base.class.php(343): array_map('sanitize_html_c...', Array)
    #3 C:\Users\Alex\Documents\local-sites\siteorigin\app\public\wp-content\plugins\so-widgets-bundle\base\inc\fields\base.class.php(324): SiteOrigin_Widget_Field_Base->render_CSS_classes(Array)
    #4 C:\Users\Alex\Documents\local-sites\siteorigin\app\public\wp-content\plugins\so-widgets-bundle\base\inc\fields\container-base.class.php(65): SiteOrigin_Widget_Field_Base->render_field_label(NULL, NULL)
    #5 C:\Users\Alex\Documents\local-sites\siteorigin\a in C:\Users\Alex\Documents\local-sites\siteorigin\app\public\wp-includes\formatting.php on line 2331
    

    This infinite loop is caused when the widgets loading itself for templating purposes and due to the widget infinitely loading itself, it’ll hit the maximum function nesting limit. To avoid this, it’s recommended you create a dedicated widget that handles creating sections and then insert that widget into another widget rather than having that widget load itself.

  7. 4 years, 6 months ago Enrique Moreno

    You mean like a “Sections wrapper”?

  8. 4 years, 6 months ago Alex S
    Hi, I Work Here

    Hi Enrique,

    Yes. The problem with your code is that the widget is embedding itself and that results in the infinite loop. You need to either embed another widget that doesn’t embed itself (or the parent widget), or add the fields directly to the widget rather than embedding any widget.

  9. 4 years, 6 months ago Enrique Moreno

    Do you mean something like this?

    <?php
    
    /*
    Widget Name: SCT - Section
    Description: SCT - Section
    Author: Enrique Moreno Tent
    Author URI: https://enriquemorenotent.com
    */
    
    class SCTSection extends SiteOrigin_Widget
    {
    
        function __construct()
        {
            parent::__construct(
                'sct-section',
                __('SCT - Section', 'sct-section-text-domain'),
                [
                    'description' => __('SCT - Section', 'sct-section-text-domain'),
                    'panels_title' => false,
                    'panels_groups' => array('sct-widgets'),
                ],
                [],
                [
                    'name' => [
                        'type' => 'text',
                        'label' => __('Section name', 'sct-section-text-domain'),
                    ],
                    'subsections' => [
                        'type' => 'widget',
                        'label' => __('Subsections', 'sct-section-text-domain'),
                        'class' => 'SCTListOfSectionsWrapper'
                   ],
                ],
                plugin_dir_path(__FILE__)
            );
        }
    }
    
    siteorigin_widget_register('sct-section', __FILE__, 'SCTSection');
    
    <?php
    
    /*
    Widget Name: SCT - List of sections Wrapper
    Description: SCT - List of sections Wrapper
    Author: Enrique Moreno Tent
    Author URI: https://enriquemorenotent.com
    */
    
    class SCTListOfSectionsWrapper extends SiteOrigin_Widget
    {
    
        function __construct()
        {
            parent::__construct(
                'sct-list-of-sections-wrapper',
                __('SCT - List of sections Wrapper', 'sct-list-of-sections-wrapper-text-domain'),
                [
                    'description' => __('SCT - List of sections Wrapper', 'sct-list-of-sections-wrapper-text-domain'),
                    'panels_title' => false,
                    'panels_groups' => array('sct-widgets'),
                ],
                [],
                [
                    'sections' => [
                        'type' => 'widget',
                        'label' => __('Subsections', 'sct-section-text-domain'),
                        'class' => 'SCTListOfSections'
                   ],
                ],
                plugin_dir_path(__FILE__)
            );
        }
    }
    
    siteorigin_widget_register('sct-list-of-sections-wrapper', __FILE__, 'SCTListOfSectionsWrapper');
    
    <?php
    
    /*
    Widget Name: SCT - List of sections
    Description: SCT - List of sections
    Author: Enrique Moreno Tent
    Author URI: https://enriquemorenotent.com
    */
    
    class SCTListOfSections extends SiteOrigin_Widget
    {
    
        function __construct()
        {
            parent::__construct(
                'sct-list-of-sections',
                __('SCT - List of sections', 'sct-list-of-sections-text-domain'),
                [
                    'description' => __('SCT - List of sections', 'sct-list-of-sections-text-domain'),
                    'panels_title' => false,
                    'panels_groups' => array('sct-widgets'),
                ],
                [],
                [
                    'sections' => [
                        'type' => 'repeater',
                        'label' => __('Sections', 'sct-list-of-sections-text-domain'),
                        'item_name'  => __('Section', 'sct-list-of-sections-text-domain'),
                        'item_label' => [
                            'selector'     => "[id*='repeat_text']",
                            'update_event' => 'change',
                            'value_method' => 'val'
                        ],
                        'fields' => [
                            'file' => [
                                'type' => 'widget',
                                'label' => __('Subsection', 'sct-list-of-sections-text-domain'),
                                'class' => 'SCTSection'
                            ],
                        ]
                    ],
                ],
                plugin_dir_path(__FILE__)
            );
        }
    }
    
    siteorigin_widget_register('sct-list-of-sections', __FILE__, 'SCTListOfSections');
    

    Because this doesnt work neither

  10. 4 years, 6 months ago Alex S
    Hi, I Work Here

    Hi Enrique,

    The provided widgets will result in the same infinite loop due to the widgets embedding each other an infinite number of times – a always loads b, b always a, and the cycle always repeats. If you add a widget to a widget, that same widget cannot load itself (ie. SCTSection cannot load SCTSection) or the parent (SCTSection cannot load SCTListOfSections as SCTListOfSections will load SCTSection which in turn will load SCTListOfSections, etc) or an infinite loop will occur.

    SCTListOfSectionsWrapper should load SCTListOfSections. SCTListOfSectionsWrapper doesn’t load SCTListOfSectionsWrapper or that would result in an infinite loop.
    SCTListOfSections should load SCTSection. It doesn’t load SCTListOfSectionsWrapper, or itself as that would result in an infinite loop.
    SCTSection doesn’t load SCTListOfSectionsWrapper, SCTListOfSections, or itself as that would result in an infinite loop.

    Kind regards,
    Alex

  11. 4 years, 6 months ago Enrique Moreno

    > SCTSection doesn’t load SCTListOfSectionsWrapper, SCTListOfSections, or itself as that would result in an infinite loop.

    If `SCTSection` cannot load any of the 3 classes, how am I supposed to make it recursive? What should SCTSection load then?

  12. 4 years, 6 months ago Alex S
    Hi, I Work Here

    Hi Enrique,

    The SCTSection should contain the desired fields and then you can repeat wrapping it inside of a the repeater form field – the widget itself shouldn’t embed itself as that will result in an infinite loop.

Replies on this thread are closed. Please create a new thread if you have a question, or purchase a SiteOrigin Premium license if you need one-on-one email support.

Get The Most Out of SiteOrigin with SiteOrigin Premium

Find Out More