How can i create two list and merge them for creating a less file?

Hello Kirby lovers,

This solution will be very important for me and hopefully to others because it helps to only use the CSS that is in use on the website.

I’m creating pages with the pagebuilder plugin and pages with sections.

I want to combine two list that will output one list that will be used to create a less file that contains the modules for creating css.

Both lists contain the same modules/building blocks and need to be reduced with array_reduce

The first list is for all the pages with sections
This code is adapted from k2 to k3 and it almost works.

It needs to function with page delete and update too and in this list the section- attribute needs to be removed in the name.

    'hooks' => [
      'page.create:after'  => function () {
        $activeModules = [];
        foreach(site()->index()->filterBy('intendedTemplate', '*=', 'section-') as $m) {
          $moduleName = $m->intendedTemplate();
          if(!in_array($moduleName, $activeModules)) $activeModules[] = $moduleName;
        }
        
        // convert the list to a LESS string
        $content = array_reduce($activeModules, function($carry, $template) {
          return $carry . '@import' . ' "modules/' . $template . '.less";' . "\n";
        }, '');
        
        // write the LESS file
        f::write(kirby()->root('assets') . DS . 'less' . DS . '_modules.less', $content, false);
      }
    ],

The second list is for all the builder pages with modules/ building blocks.

Something like this

Step 2.1 get all the the builder templates

$site->index()->filterBy('template', 'builder') 

Step 2.2 structure the builder pages

$builder ->pagebuilder()->toStructure()

Step 2.3 get the keys

 print $section->_key()

Final steps

Now we need to combine the two arrays and use array reduce

We write less file with this code.

       // convert the list to a LESS string
        $content = array_reduce($activeModules, function($carry, $template) {
          return $carry . '@import' . ' "modules/' . $template . '.less";' . "\n";
        }, '');
        
        // write the LESS file
        f::write(kirby()->root('assets') . DS . 'less' . DS . '_modules.less', $content, false);

So If i’ve got this right from your code, all your really doing is ripping through all the templates and modules and creating a set of empty less files with matching import statements for each module? Cant you just use array_merge before you reduce it?

… If its code reduction your after, I would suggest instead using something like PurgeCSS which will actually look though PHP and many kinds of files for classes used and trims the css accordingly.

For example, I use it with Laraval Mix since im using that to compile SASS (it will do Less too). So adding this in to the webpack.mix.js file achieves this easily when you do a production build:

mix.sass('src/sass/site.scss', 'public/assets/css', {
  includePaths: ["node_modules/slatecore/slate"],
  precision: 5
}).purgeCss({
  enabled: false,

  folders: ['public'],

  globs: [

    path.join(__dirname, 'public/site/snippets/**/*.php'),
    path.join(__dirname, 'public/site/templates/*.php'),
    path.join(__dirname, 'public/site/plugins/**/*.php'),

  ],

  extensions: ['php', 'js'],

  // Other options are passed through to Purgecss
  whitelistPatterns: [/some/, /classes/, /you/, /need, /to/, /protect/],
});

Thanks for your reaction,

So If i’ve got this right from your code, all your really doing is ripping through all the templates and modules and creating a set of empty less files with matching import statements for each module? Cant you just use array_merge before you reduce it?

Yes, im ripping through all the templates and modules to create one less file with matching import statements for each module. Correct, i need to use array_merge to combine the result of the two list and than use array_reduce. I’m not using any big frameworks that i need to reduce, all modules are hand coded.

Are the less files compiled on the server? If the user adds modules later, what will happen?

but anyway, what exactly is your problem with merging the arrays?

The less files are compliled on the server with the plugin http://leafo.net/lessphp

If a user adds or deletes a module the panelhook execute the code for creating the less file, and changes the site.less for creating the main.css

Alright, that makes sense.

As regards your first array, the problem is that you have an array of template objects, so you better do it like this:
First array:

foreach(site()->index()->filterBy('intendedTemplate', '*=', 'section-') as $m) {
    $activeModules[] = $m->intendedTemplate()->name();
}

That will give you a flat array of all template names that you should be able to merge with your second array.

Second array:

$builderBlocks = [];
foreach ($site->index()->filterBy('intendedTemplate', 'builder) as $page) {
    foreach($$page->pagebuilder()->toStructure()->pluck('_key') as $item) {
      $builderBlocks[] = $item->_key()->value();
    };
  }

Merge:

$mergedArray = array_unique(array_merge($activeModules, $builderBlocks));

Not tested.

Hello I’m realy happy this works!

In K2 you could use

kirby()->hook('panel.page.*', function() {

Is there similar hook that triggers on page create, update and delete?

    'hooks' => [
      'page.create:after'  => function () {

        // find all the visible sections and remove section- in the names
        $activeModules = [];
        foreach(site()->index()->listed()->filterBy('intendedTemplate', '*=', 'section-') as $m) {
          $activeModules[] = str_replace("section-", "", $m->intendedTemplate()->name());
        }
        // find all the visible templates with builder and extract all the building blocks
        $builderBlocks = [];
        foreach(site()->index()->listed()->filterBy('intendedTemplate', 'builder') as $page){
          foreach($page->pagebuilder()->toStructure() as $item){
            $builderBlocks[] = $item->_key()->value(); 
          };
        }
        // merge the two arrays and filter
        $mergedArray = array_unique(array_merge($activeModules, $builderBlocks));
        
        // convert the list to a LESS string
        $content = array_reduce($mergedArray, function($carry, $template) {
          return $carry . '@import' . ' "modules/' . $template . '.less";' . "\n";
        }, '');
        
        // write the LESS file
        f::write(kirby()->root('assets') . DS . 'less' . DS . '_modules.less', $content, false);
      }
    ],

Wildcards for hooks like in Kirby 2 are currently not supported :cry:

You could, however, store you logic in a function that you call in your hooks to keep your code DRY.

That’s good idea, i will wrap the logic in a function!!

Is it possible to make it feature request for Kirby 3, please :innocent:?

You can upvote this idea issue: https://github.com/getkirby/ideas/issues/194