We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 40385
    • 75 Posts
    After some tests and discussions I decided to change my my first post on this topic to be a little bit more clear on the actual issue:

    When toArray() is called within an if clause it won't refresh uncached content fields from chunks that are nested within the actual chunk if they have already been cached once.

    For example: Having two chunks templateA and templateB. Each chunk includes a chunk which is called head. This head chunk shows the userAgent via a snippet that is called uncached within the head chunk. When using this snippet to determine which template to show

    if ( condition ) {
        return $modx->getChunk('templateChunkA',$modx->resource->toArray());
    } else {
    	return $modx->getChunk('templateChunkB',$modx->resource->toArray());
    }


    the output of the useragent snippet will always return the value of the first useragent that requested the website after the cache was cleaned.

    When moving the useragent snippet call from the head chunk to templateA or templateB chunk (so that it is no longer located within a nested chunk relatively to the templateswitcher snippet) it will be refreshed for each user. This is the expected behavior.

    The other solution would be to leave the useragent snippet call within the nested head chunk and restructure the templateswitcher snippet so that the toArray() function will be outside of the if clause like this

    <?php
    $templateChunkA = $modx->getChunk('templateChunkA',$modx->resource->toArray());
    $templateChunkB = $modx->getChunk('templateChunkB',$modx->resource->toArray());
    
    if ( condition ) {
        return $templateChunkA;
    } else {
    	return $templateChunkB;
    }


    This will also show the user agent of the actual user instead of the cached value.

    My question: Is this behavior expected or is this a bug?


    Quote from: OldPost


    Hello,

    I'm facing a really interesting (as far as I am concerned) problem. I'm using a snippet to determine which template chunk to show depending on a certain condition. This snippet is called uncached in the home template

    [[!templateSwitcher]]


    This is the code of the templateSwitcher snippet:

    <?php
    $templateChunkA = $modx->getChunk('templateChunkA',$modx->resource->toArray());
    $templateChunkB = $modx->getChunk('templateChunkB',$modx->resource->toArray());
    
    if ( condition ) {
        return $templateChunkA;
    } else {
    	return $templateChunkB;
    }


    This is working fine...

    However I thought about changing it to the following code to just grab the templateChunk which is actually needed instead of grabbing both and then just returning one (speed?! maybe I'm wrong?!)

    <?php
    if ( condition ) {
        return $modx->getChunk('templateChunkA',$modx->resource->toArray());
    } else {
    	return $modx->getChunk('templateChunkB',$modx->resource->toArray());
    }


    "EDIT: Changed to BobRay's version"

    Unfortunately this has the odd effect that the site_url is no longer being called uncached. So depending on the url the website is first requested from this will be cached for the site_url, leading to problems in low IE-versions.

    after cleaning cache first request from http://www.example.com -> site_url http://www.example.com
    second request from http://subdomain.example.com -> site_url still says http://www.example.com

    I thought about this many times, but I have no clue why this only happens when I use the latter snippet. From my point of view it's actually the same...I'd really appreciate if somebody has an answer for this behaviour.
    [ed. note: paul_kemp last edited this post 14 years, 1 month ago.]
      • 3749
      • 24,544 Posts
      Try this:

      <?php
      if ( condition ) {
          return $modx->getChunk('templateChunkA',$modx->resource->toArray());
      } else {
          return $modx->getChunk('templateChunkB',$modx->resource->toArray());
      }



      ------------------------------------------------------------------------------------------
      PLEASE, PLEASE specify the version of MODX you are using.
      MODX info for everyone: http://bobsguides.com/modx.html
        Did I help you? Buy me a beer
        Get my Book: MODX:The Official Guide
        MODX info for everyone: http://bobsguides.com/modx.html
        My MODX Extras
        Bob's Guides is now hosted at A2 MODX Hosting
        • 40385
        • 75 Posts
        Quote from: BobRay at Sep 04, 2012, 05:47 PM
        Try this:

        <?php
        if ( condition ) {
            return $modx->getChunk('templateChunkA',$modx->resource->toArray());
        } else {
            return $modx->getChunk('templateChunkB',$modx->resource->toArray());
        }


        Thanks @BobRay but unfortunately this is not working (the script of course is)...but the site_url is not being updated when I use toArray() in the if clause...I need to store it in a variable first...but why? I don't understand it.

        Why/In which way is snippet A different from snippet B?

        PS: I'm running revo 2.2.4-pl (traditional) [ed. note: paul_kemp last edited this post 14 years, 1 month ago.]
          • 3749
          • 24,544 Posts
          I can't think of any reason why that would affect the site_url.


          ------------------------------------------------------------------------------------------
          PLEASE, PLEASE specify the version of MODX you are using.
          MODX info for everyone: http://bobsguides.com/modx.html
            Did I help you? Buy me a beer
            Get my Book: MODX:The Official Guide
            MODX info for everyone: http://bobsguides.com/modx.html
            My MODX Extras
            Bob's Guides is now hosted at A2 MODX Hosting
            • 40385
            • 75 Posts
            Maybe it is already "too late" to start the toArray function when return is called...?!
              • 40385
              • 75 Posts
              I was just able to reproduce this issue in another modx installation. I even tried this

              <?php
              if ( condition ) {
                  $finalTpl = $modx->getChunk('templateChunkA',$modx->resource->toArray());
              } else {
                  $finalTpl = $modx->getChunk('templateChunkB',$modx->resource->toArray());
              }
              return $finalTpl;


              site_url is not being updated. No matter what I try or how I restructure the script, as long as the toArray() function is called within the if-clause it doesn't update the site_url parameter...stupid

              PS: both templateChunks hold different headChunks...both containing site_url uncached in the base href...

              Please have a look at my updated first post on this issue. Thanks! [ed. note: paul_kemp last edited this post 14 years, 1 month ago.]
                • 39932
                • 483 Posts
                Welcome to Template Switching! This was a bear for me to figure out... all of the right calls and where. Caching musses up things a lot. If your Resource is cacheable, you've just opened a can of worms. (I know).

                So, the question here is "why doesn't it work if I place it in a condition?" That answer is can be explained simply (its far from simple). It has to do with the way that PHP handles inline function calls and MODx handles its class members. toArray() basically makes a manual copy of the Resource, but transfers it into an Array(). When called out of line, it makes the copy and runs everything accordingly.

                Inline is a slightly different process. The results are not treated as a full copy. They are a reference to the object and method.

                Since in the first example, its a copy, effectively the Chunk asks it to get all of its information again. In the subsequent examples, it believes it has all of the information, so it doesn't have to get them. In other words, for speed $modx->resource is not the same as $modx->getObject('modResource', $id). This is true even if they point to the same resource.

                If you would like it to be different, this might work:

                <?php
                if ( condition ) {
                    $finalTpl = $modx->getChunk('templateChunkA',$modx->getObject('modResource', $modx->resource->get('id'))->toArray());
                } else {
                    $finalTpl = $modx->getChunk('templateChunkB',$modx->getObject('modResource', $modx->resource->get('id'))->toArray());
                }
                return $finalTpl;
                
                  Website: Extended Dialog Development Blog: on Extended Dialog
                  Add-ons: AJAX Revolution, RO.IDEs Editor & Framework (in works) Utilities: Plugin Compatibility List
                  Tutorials: Create Cross-Context Resources, Cross-Context AJAX Login, Template-Based Actions, Remove Extensions from URLs

                  Failure is just another word for saying you didn't want to try. "It can't be done" means "I don't know how".
                  • 39932
                  • 483 Posts
                  Another alternative is to do REAL template switching. Its a little slower and more involved, but you never have to worry about little issues like this. Different issues entirely resulting around the same thing... the Cache.

                  This means creating Two Templates (not Chunks), that have the differences in output. It then requires a little plugin... possibly two, depending on how you are wanting to switch. I use Template Switching in my AJAX Framework. The Plugin you would need to look at in there is OnDegradeGracefully. It does the work of actually performing the switch. [ed. note: fuzzicallogic last edited this post 14 years, 1 month ago.]
                    Website: Extended Dialog Development Blog: on Extended Dialog
                    Add-ons: AJAX Revolution, RO.IDEs Editor & Framework (in works) Utilities: Plugin Compatibility List
                    Tutorials: Create Cross-Context Resources, Cross-Context AJAX Login, Template-Based Actions, Remove Extensions from URLs

                    Failure is just another word for saying you didn't want to try. "It can't be done" means "I don't know how".
                    • 40385
                    • 75 Posts
                    Wow...thanks for this excellent detailed explanation Fuzzical Logic. I will definitely have a look at your Ajax Tutorial and especially the OnDegradeGracefully Plugin.

                    When I was starting with templateSwitching I was also working with a seld-made plugin but I couldn't really get it to work so I switched to using chunks and snippets instead which at first seemed to be much easier in handling that kind of work...but obviously this assumption is wrong.

                    However I'm still having a few questions on this topic:

                    (1) You are saying that

                    In other words, for speed $modx->resource is not the same as $modx->getObject('modResource', $id). This is true even if they point to the same resource.

                    Does this imply that when I call toArray() outside of a condition I can always go with $modx->resource because the output will be the same as modx->getObject('modResource', $id) but it is faster?

                    (2) This question also refers to your quote mentioned above. When modx->getObject('modResource', $id) is slower than $modx->resource which kind of snippet should be preferred.

                    Snippet A: Where both templateChunks are stored in variables via $modx->resource before the if condition that determines which templatechunk to show.

                    Snippet B: Where only the actual templateChunk is returned in the condition via modx->getObject('modResource', $id)?

                    So my question is basically, two times $modx->resource outside the condition vs one time modx->getObject('modResource', $id) inside the condition?

                    Thanks again for all the help!!! [ed. note: paul_kemp last edited this post 14 years, 1 month ago.]
                      • 39932
                      • 483 Posts
                      Does this imply that when I call toArray() outside of a condition I can always go with $modx->resource because the output will be the same as modx->getObject('modResource', $id) but it is faster?

                      In general, yes. The issue isn't the toArray() specifically, but that toArray() is a method of an object of a class that is already instantiated. It's the fact that you call it inside a function as an argument in a condition. Every OOP language handles function calls slightly differently, but often have similar implementations when it comes to inline calls. Most inline function calls necessitate a combination of "by reference" and "by value".

                      Generally, non-inline calls in PHP are all "by value" unless explicitly stated. This means that it copies the object and then runs the function. However (which has cause a lot of criticism for PHP), inline calls work a little differently. If the argument is a method, the object is passed by reference, but the method's return are by value. This is because you are passing a function, not the object, itself.

                      toArray() is essentially (not quite) a clone for the xPDO object data, which means that as a function, it forces a "by value". But since you pass IT as the argument, because it is a class method, PHP does something funny to it.

                      When modx->getObject('modResource', $id) is slower than $modx->resource which kind of snippet should be preferred.

                      Snippet A: Where both templateChunks are stored in variables via $modx->resource before the if condition that determines which templatechunk to show.

                      Snippet B: Where only the actual templateChunk is returned in the condition via modx->getObject('modResource', $id)?

                      So my question is basically, two times $modx->resource outside the condition vs one time modx->getObject('modResource', $id) inside the condition?

                      It depends on how complex your templates are and its surrounding stuff. You have to understand that rarely is the speed dependent entirely on a single function. Often it is repeated operations that make the difference. Looping is typically the largest cause of lack of speed.

                      If your template requires a lot of snippet calls that perform complex loops, or a ton (and I mean ton) of embedded chunks, Method A would be preferred.

                      If your page is fast enough and the only thing slowing it down is the actual network transmission, Method B is best.

                      There are other considerations, of course, but performance is normally the thing most web developers have a problem with, so that was my focus. Honestly, though, optimization like this is the first mistake of great programming. I can't take credit for this, but a programmer I very much agree with once said:

                      On the topic of Optimization:

                      Rule #1: Don't do it.
                      Rule #2 (for experts only): Don't do it. (At least until you already have a viable working solution)

                      In other words, even though I brought up the point of speed (because that's why $modx->resource is there and accessible as a convenience), don't focus on the semantics. Get a solution.

                      P.S. Did that sample fix your issue?
                        Website: Extended Dialog Development Blog: on Extended Dialog
                        Add-ons: AJAX Revolution, RO.IDEs Editor & Framework (in works) Utilities: Plugin Compatibility List
                        Tutorials: Create Cross-Context Resources, Cross-Context AJAX Login, Template-Based Actions, Remove Extensions from URLs

                        Failure is just another word for saying you didn't want to try. "It can't be done" means "I don't know how".