Jump to content

gebeer

Members
  • Posts

    1,489
  • Joined

  • Last visited

  • Days Won

    43

Posts posted by gebeer

  1. Hi there,

    I'm experiencing unexpected behavior with markup regions where multiple pw-append actions are being processed in reverse order. I have been using markup regions for several years in multiple projects and never came across this. There are no project specific potential causes for my problem that I could identify. Involved HTML markup syntax is correct.

    Setup
    Delayed output strategy with site/templates/_main.php (simplified):

    <html>
      <head>
      </head>
      <body>
        <main id="maincontent" class="content">
        </main>
      </body>
    </html>

    site/templates/products.php:

    <div class="bg productsintrobg" pw-append="maincontent">
        <!-- Products Intro Section -->
        <div class="container">
            <!-- Products Intro Content -->
        </div>
    </div>
    
    <div class="bg productcatsbg" pw-append="maincontent">
        <!-- Product Categories Section -->
        <div class="container">
            <section class="productCats teasers row">
                <!-- Product categories content -->
            </section>
        </div>
    </div>

    Expected Behavior
    Since products.php executes before _main.php and contains region actions (pw-append) that should be processed in order, the final output should show:
    Products Intro Section
    Product Categories Section

    Actual Behavior
    The sections are rendered in reverse order:
    Product Categories Section
    Products Intro Section

    Technical Context
    According to the Markup Regions documentation:

    Quote

    Any markup output before the <html> tag is considered to contain region actions
    Any markup output after the <html> tag is considered to contain region definitions

    Since products.php is processed before _main.php (using $config->appendTemplateFile = '_main.php'), the region actions in products.php are technically output "before" the <html> tag and region definitions in _main.php. Therefore, the implementation follows the documentation correctly, but the output order is reversed.

    Current Workaround
    Changed first section from pw-append to pw-prepend to achieve desired order:

    <div class="bg productsintrobg" pw-prepend="maincontent">
        <!-- Products Intro Section -->
    </div>
    
    <div class="bg productcatsbg" pw-append="maincontent">
        <!-- Product Categories Section -->
    </div>

    MarkupRegions Debug Output (shows wrong order of processing):

    • APPEND #maincontent with <div class="bg productcatsbg" ... +24 bytes><div class="container"> <section class="productCats teasers row"> … 14986 bytes</div>
    • APPEND #maincontent with <div class="bg productsintrobg... +26 bytes><div class="container"> <article class='intro row'> … 1264 bytes</div>
    • 0.0014 seconds


    Environment
    ProcessWire version: .3.0.243
    PHP version: 8.1 (same behavior with 8.2)

    Has anyone experienced this before? Couldn't find related forum posts or github issues

    • Like 2
  2. I just managed to get the Blade Formatter extension (https://open-vsx.org/extension/shufo/vscode-blade-formatter) working with Latte files :-)

    I needed to use the Blade Formatter from the actions menu:

    image.png.10353ac505d5d5734367ab15465e96d0.png

    Although I have it defined as a default formatter for latte files in settings.json, the default formatting command always asks for a formatter.

    "[latte]": {
        "editor.defaultFormatter": "shufo.vscode-blade-formatter"
      },

    But pressing Shift+P and enter to trigger the Blade formatter from the list is still much better than manual formatting :-)

    • Like 2
  3. Hi @bernhard,

    in the docs-old folder of the module there is a nice section about prices. It says that we can switch to gross prices with a config setting.

    I was not so sure whether that still applies and how it works with the cart calculations. So I just wanted to share my custom implementation, so that editors can enter gross price and net price will be calculated.

    I added a field (type RockMoney) "price_gross" in addition to the "rockcommerce_net" field to my product template. And in my ProductPage class, I have a saveReady hook that calculates the net price from the gross price and the vat for that product.

     

        /**
         * Calculates the taxrate of the product
         * based on the taxrate field of the product
         * if no custom taxrate is set, the global taxrate is used
         *
         * @return float
         */
        public function getTaxrate(): float
        {
            return (float) $this->rockcommerce_taxrate ? $this->rockcommerce_taxrate/100 : $this->taxrate();
        }
    
    
        /**
         * Executes when the page is ready to be saved.
         * 
         * Only executes if price_gross is given and field rockcommerce_net is present. 
         * Calculates the net price and VAT based on the gross price
         * and tax rate, if a gross price is provided.
         * It saves the net price to rockcommerce_net.
         *
         * @return void
         */
        public function onSaveReady(): void
        {  
            // only execute if we have price_gross
            if ($this->price_gross && $this->template->hasField('rockcommerce_net')) {
                // take price_gross and calculate vat and net price from it using taxrate
                $taxrate = $this->getTaxrate();
                $vat = rockmoney()->parse($this->price_gross)->times($taxrate / (1 + $taxrate));
                /** @var \RockMoney\Money */
                $priceNet = rockmoney()->parse($this->price_gross)->minus($vat);
                $this->rockcommerce_net = $priceNet->getFloat();
            }
        }

    I also use a custom getTaxrate() method from my page class, in case a custom rate is set for that product.

    Page edit screen

    image.thumb.png.4efec4952a42be8d44471166239bd4c5.png

    • Like 2
    • Thanks 1
  4. Thank you so much for the quick reply @bernhard and for adding stuff to the docs so quickly!

    Now the docs are coming together very nicely. I really appreciate that.

    54 minutes ago, bernhard said:

    As you can see in the old docs I thought about adding that, but I decided against it. The goal was to make the experience of setting up a simple shop as simple as possible. Obviously that comes with some limitations.

    I think it would not be too hard to add support for different taxrates per product. But it would also not be trivial. Different taxrates per product means the VAT of the cart is not anymore a simple "net times taxrate" calculation.

    That is totally fine. If we know these limitations as developers, we can maybe implement custom solutions if the existing cart hooks are sufficient and allow us to do those calculations and display them in the cart.

    I have a rather simple shop atm with single tax rate. Just wanted to know how to handle things when tax rates are needed per product. My experence with other shops shows that this will eventually come up at some point in time :-)  

    • Like 2
  5. To expand on this, imo these are the minimum points that should be covered by the docs:

    • defining tax rates
    • setting default tax rate
    • apply tax rate different from default to product
    • explain how prices are calculated:

    - does RC always calculate with default rate if no other rate is set for a product?

    - do we need to implement those calculations ourselves?

    If you can add those, that would be awesome. Thank you.  

  6. Hi @bernhard,

    I'm just getting started with setting up my first project using your RockCommerce module.

    Installation on a new site went fine. Also basic product setup.

    But now I'm stuck on setting up and applying tax rates. I figured out that I can add child pages 
    image.png.25417ea38fcf10c84c7a55a4c1d2aea8.png

    And I can set a default tax rate on the "Taxrates" page.

    But how are non-default rates handled per product? Do I use the field rockcommerce_defaulttaxrate on my product template?

    When I add the field rockcommerce_vat  to my product template, I get a non-editable field

    image.png.f9ebaf95657418691db20137ab4840e5.png

    When I add rockcommerce_taxrate, I get an input for the tax rate
     image.png.0f075e777b3c7b3b5d848b337e103c9e.png

    When I enter a value here, will this be picked up automatically for the price calculations?
    And I think it would be better to have a page select field where we can choose from the tax rates that we added as children of the "Taxrates" page.

    Is this the correct way to set this up?

    Unfortunately I couldn't find anything in the docs. Would be much appreciated if you added a section about tax handling there. The quickstart docs are great, but I think tax handling is a very essential thing in every shop. So this should definitely be covered by the docs.

    EDIT (1h later): I just realized that in the readme.md inside the docs-old/taxes folder of the module there is a section about taxes that answers some of my questions. I was relying on the documentation at https://www.baumrock.com/en/processwire/modules/rockcommerce/docs/ The information in the readme.md is not contained there and I am not sure if it still applies since it is in the docs-old folder? Imo it would be great to have everything in one place.

  7. 3 minutes ago, bernhard said:

    I also have no auto-formatter for latte + vscode, which is a shame. But I try to see that positive and take it as a reminder to keep my template files as simple as possible. Anything more complicated than $page->title is handed over to PHP where I have auto code formatting + intellisense.

    I can kind of understand that. But imagine some html structure with more than 3 levels of depth. Then you remove some wrapper deep in the structure and now you have to manually mark the relevant section and move it back 1 tab. That is just a waste of time imo. I prefer to just hit my short key for Format Document and done. It really is a shame that there are no formatters out there for latte. Guess it is too much of a niche template engine.

    I even tried to use the blade formatter. But didn't work for me.

    Honestly, little things like that have kept me from using Latte at all in the past. Now I took another try and hitting the wall again. Guess, I will just revert to pure PHP and be happy with that :-)

  8. On 3/5/2024 at 2:40 AM, fliwire said:

    How format .latte files in vs code ? 
    Currently there is no formatter for "latte".

    Having the same issue here. VsCode (Windsurf) always asks for a formatter.

    I tried in .prettierrc:

    Still no luck.

    I also have this in my user settings.json:
     

      "[latte]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      },

    Doesn't help either.

    So for the moment I'm stuck. A solution would be much appreciated.

  9. Hi there,

    why the heck would you want to migrate your migrations?

    Since v6 of RockMigrations there's a new feature called "config migrations" which you can read more about here.

    In a nutshell: instead of having an often lengthy migration function like
     

    $rm->migrate([
        'fields' => [...],
        'templates' => [...]
    ])

    with hundreds of lines in either site/migrations.php or site/modules/Site.module.php or other places, we can neatly organize our migration definitions into single files inside site/RockMigrations/fields and site/RockMigrations/templates (...roles, ...permissions). Where each file has the name of the field/template/role/permission and returns an array of  settings. If you have module specific migrations, they go under /site/modules/MyModule/RockMigrations/(fields/templates/etc)

    This is absolutely wonderful for several reasons. Here's just a few that come to mind:

    • clean structure
    • smaller migration files
    • easy to discover in explorer panel
    • faster migrations because only migrations for changed files will fire
    • portability across projects

    Because I immediately fell in love with this concept, I am currently updating migrations for all projects to the new structure. This can be a very tedious task. We could write a script (and maybe this is the ultimate solution). BUT since I'm lazy, I just commanded my favorite AI assistant to do the job for me. I did this in windsurf editor Write mode (Cascade), but you can also use cursor's composer for this. Let me share how I approached it.

    I created the directories "fields" and "templates" under site/RockMigrations.  And one migration file each for a field and a template to have examples to pass to the AI.
    Then I copied the 'fields' array from above mentioned $rm->migrate(['fields => []]) (that was originally in my Site.module.php) and pasted it into the windsurf Cascade (or cursor composer) chat. Along with these instructions:

     

    For each item in this array do the following operations:
    1. create file site/RockMigrations/fields/{array_key}.php
    2. add namespace ProcessWire and return the array found under this {array_key}
    Mirror structure of @seo_fieldset.php 

    Where @seo_fieldset.php is passing the contents of site/RockMigrations/fields/seo_fieldset.php as context to the AI.

    And here's what happened:

    image.thumb.png.a206368f622d17c204aa384024c0b59a.png

    windsurf Cascade decided to do the operation in batches. It would let me check the results after every 4 files or so and asked me if I wanted to continue with the next batch. After about 10 minutes all my 31 field migrations where refactored into the new structure and I was very happy following along while sipping away at my coffee :-)

    After that I did the same for the 'templates' array.

    Some considerations on why I chose this exact approach:

    • I could have given the whole Site.module.php file as context and asked the AI to read the fields array from there. But that file is pretty large and the fields array alone was about 400 lines of code. And that might have exceeded the context window or at least confused the AI.
    • I used the phrase "Mirror structure of ..." because I found that to be a very effective prompt whenever you want the assistant to write code based on an example. Actually I did not find that out by myself (I'm not that great of a prompt engineering genius). Got it from IndyDevDan over at YT. Highly recommended channel btw.

    To wrap things up, I can highly recommend switching to the new config migrations structure. So much cleaner. So much better. A big praise to @bernhard for coming up with this. Also praises to the developers over at cursor and codeium (windsurf) for these amazing tools. And, not to forget: Hail Claude! 

    • Like 1
    • Thanks 1
  10. 3 hours ago, adrian said:

    Hi @gebeer - please try the latest version. I have modified to use $_SERVER['REQUEST_URI'] which I hope should solve things when you guys have these unusual redirects in place and well as @Jonathan Lahijani's issue from here: https://github.com/adrianbj/TracyDebugger/issues/96

    Please let me know if you can now work without that modification to your hook.

    Thank you very much for the fix. I can confirm that Tracy s now working without the modification.

    I think, for larger corporate sites redirects that route from root to a specific language/market URI are not that unusual. So it is great that you made things work for those use cases.

    • Like 1
  11. On 11/20/2024 at 5:08 AM, nurkka said:

    /panels/ConsolePanel.php:

    # /panels/ConsolePanel.php line 406
    xmlhttp.open("POST", "/", true);

    When I change the line to it's previous version, it works for me:

    xmlhttp.open("POST", "./", true);

     

    We were experiencing a similar issues because the POST request now goes to / root instead of the current page loaded like before with ./
    In our case the console result frame would load the homepage of the frontend instead of the actual results.

    I solved it for our case (a hook to PageView::execute on /) by returning if it is a tracy request

    $wire->addHookBefore("ProcessPageView::execute", function (HookEvent $event) {
    
    	// do not redirect if tracy request
    	if(isset($_POST['tracyConsole']) && $_POST['tracyConsole'] == 1) return;
    	// alternative
    	// if (isset($_SERVER['HTTP_X_TRACY_AJAX']) && $_SERVER['HTTP_X_TRACY_AJAX']) return;
    
    	if ($_SERVER['REQUEST_URI'] === '/') {
          ...
          session()->redirect("{$lang}{$marketName}");
        }

    We need that redirect to determine language and market based on a request to GeoApify API.

    I think that others might potentially run into this as well. So if the change of xmlhttp.open("POST", "./", true); to root / is not strictly necessary for Tracy to work, it might be better to revert that? @adrian   

    • Like 1
  12. 6 minutes ago, bernhard said:

    Any sources for that? PW definitely uses camelcase constants like Inputfield::collapsedHidden which I think looks nicer than Inputfield::COLLAPSEDHIDDEN. But I'd be happy to get some links to read.

    https://www.perplexity.ai/search/php-constants-naming-conventio-g4mLG8sbQyu34r4mGXmx6w 

    Although the source for this is not php.net directly. I wanted to read up on that over there but php.net currently throws a 502 error.

    Anyways, that was just a side note.

    8 minutes ago, bernhard said:

    Yes I always use underscores

    That explains why you never saw that error. But imo you should not assume that everybody uses underscores so I think, the renaming logic in my PR at https://github.com/baumrock/RockMigrations/pull/70/commits/a06c6062c61b5264932a8044cf25bac0c689f8cf should be implemented.
    Otherwise people who use hyphens in their template names would have to rename all their templates just to be able to use the RockMigrationsConstants magic.

  13. 7 minutes ago, bernhard said:

    I'm not sure about this. That should only be the case on regular runtime migrations, but when doing a modules refresh all migrations should fire.

    Ah, thank you. That make sense. Will prioritize my Site module migration so that the backup is created before the config migrations kick in.

  14. Hi @bernhard,

    I usually have a line in my Site module that does a DB backup before the migrations are running

      public function migrate()
      {
    
        // backup before migrations (not on ddev)
        if (strpos($this->config->httpHost, 'ddev') === false) $this->wire->database->backups()->backup();
    
    ...

    I moved my migrations from the migrate method to the new phantastic config migration structure in site/RockMigrations. So much cleaner to have each field/template migration defined in a separate file :-)

    But: since the migrations now do not live in Site module, the DB backup is not being triggered because there are no changes to that file anymore.

    Long story short: Is there a setting in $config->rockmigrations that will do automatic backups befor each migration on the live server? Couldn't find anything related in the docs, the GH wiki or even searching the string "backup" in the RockMigrations module folder.

    Could this feature be added or how do handle this?  

  15. 7 hours ago, bernhard said:

    hen I'd appreciate if you could try to create the file /site/modules/Site/RockMigrationsConstants.php and see if that maybe fixes some other of your mentioned issues?

    This solves only the first problem that I described.

    The error caused by hyphens in constant names still persists, even after switching to PHP8.3. Do you name all your templates with underscores like "basic_page" to avoid that?

    On a side note, after reading up on PHP constants, there seems to be a convention to name them in capital letters.

  16. Hi @bernhard,

    I already fell in love with the new config migrations feature in RockMigrations > 6.0.0. I am currently on v6.0.1

    So much cleaner to have migrations organized in this way.

    I just tried to implement the part about the constant traits, described at https://www.baumrock.com/en/processwire/modules/rockmigrations/docs/config-migrations/#class-constant-traits

    First it would not work at all as described in the docs. I noticed that my file at site/modules/Site/RockMigrations/RockMigrationsConstants.php never got picked up because of searching for those files did not recurse into module subdirectries. Fixed in PR#70 commit  https://github.com/baumrock/RockMigrations/pull/70/commits/faba5475a008ab14415c0e35f2c5c78d2e91f0b1.

    First problem:

    After fixing that, I get the Compile Error
    Traits cannot have constants

    image.thumb.png.df38282c3960448d3c4d5a1e6d0c8875.png

    Actually, constants are only allowed in Traits since PHP 8.2.

    So this would change the module dependencies from PHP>=8.0 to PHP>=8.2

    I don't think this was intended. But it should be updated or at least documented that the automated constants thingy requires PHP>=8.2.

    Second problem:

    After upgrading to PHP8.2 I ran into the next issue:

    image.thumb.png.d7a4da7888528c7518e1ce41b3eb88a5.png

    This is caused by the constant name "template_basic-page" containing a hyphen. As it seems, they are not allowed in constant names in PHP at all (https://www.php.net/manual/en/language.constants.php):

    Quote

    The name of a constant follows the same rules as any label in PHP. A valid constant name starts with a letter or underscore, followed by any number of letters, numbers, or underscores. As a regular expression, it would be expressed thusly: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$

    For years I have naming conventions for templates and fields in PW. Template names always contain hyphens like in "basic-page". Field names always contain underscores like in "my_field". This is also helpful when looking at code to discern templates from fields on the first glance.

    I fixed this by replacing - with _ in constant names. See https://github.com/baumrock/RockMigrations/pull/70/commits/a06c6062c61b5264932a8044cf25bac0c689f8cf

    The issue with PHP8.2 still remains to be resolved. I suggest adding the requirement only to docs  about RockMigrationsConstants.php because other than that RockMigrations seemed to work fine on 8.1 in my tests.

     

    • Like 1
  17. On 11/18/2024 at 8:08 PM, joe_g said:

    @gebeer Is what you describe some native setting or is it part of your module?

    This is not native. It is a field definition for a custom field 'langs' that lets you choose one or more language. The array format of the field definition that I posted is used in the @bernhard's fabulous RokMigrations module but you can define the field manually via GUI, too, if you prefer so.

    • Like 1
  18. In one of my projects I have a separate field 'langs' that editors can check. Definition (for RockMigrations):
     

    				'langs' => [
    					'label' => 'Languages',
    					'tags' => 'general',
    					'flags' => 0,
    					'type' => 'FieldtypePage',
    					'derefAsPage' => 0,
    					'inputfield' => 'InputfieldCheckboxes',
    					'parent_id' => '',
    					'labelFieldName' => 'title',
    					'optionColumns' => 1,
    					'template_id' => 'language',
    				],

    So it is a page reference field that gives you checkboxes for each language.

    You could then use the value of this field in your logic for the language switch.

    • Like 1
  19. Hi @bernhard we just updated RockFrontend on a site from 3.20.0 to 3.23.3 and the site stopped working.

    I fixed it by changing  templates/_main.php

    <?= $rockfrontend->renderLayout($page); // worked before update ?> 
    <?= $rockfrontend->render($page); // works after update ?> 

    We use RockPageBuilder and in templates/sections/main.latte

    <div sortable x-ref="additional-sections">
     {$page->get('rockpagebuilder_blocks')->render()|noescape}
    </div>

    Now after the update that threw an error on our home page because template home has no field rockpagebuilder_blocks

    rockfrontend-error.thumb.png.d5addf88423119e2d23b1a0306f41b64.png

    I am wondering why this is. Do you have any idea?

    I looked at the function signature for render() and the examples in the PHPDoc do not mention that we can pass a Page object as $path argument. So I thought we could not call render($page) at all. But it works. I guess because $page is then overwritten with $this->wire->page.

    So renderLayout($page) would be the correct method to use. But something in the inner workings of renderLayout must have changed and stopped the site from working.

    I suspect that the way we have setup rendering with RockPageBuilder, RockFrontend and Latte is fundamentally wrong.

     

×
×
  • Create New...