Search code examples
phpwordpressadvanced-custom-fields

Count words in post with many ACF blocks


I am trying to count the amount of words there are on a given post on a WordPress install.

When I target the content

$word_counter = str_word_count(the_content());

It works, but this amounts to a small part, the rest it's made out of various ACF layout blocks, they are all gathered by get_template_part and required into the single.php file.

Is there a way I could count the amount of words that all these blocks generate, from the single.php page?

My problems is that I have been going into each layout template, and gone through each field to count the words:

app/builder.php

if (get_field('layouts_templates', 'option')):
  $word_counter_main = 0;
  while (have_rows('layouts')) : the_row();

    $word_counter_main = $word_counter_main + str_word_count(get_sub_field('content'));

    if (get_row_layout() == 'template') {
     $template_builders[get_sub_field('layouts_template')] = null;                 
    }

  endwhile;
endif;

But I dont know how to pass it back to single.php to add all the word counters into a total.

single.php

...
// layouts
get_template_part('app/builder');

UPDATE

There are a few more templates with in the layouts, this is the structure

array(9) {
  ["layouts"] =>
  array(3) {
    array(12) {
      ["acf_fc_layout"]=> "main-content"
      ["acfe_flexible_toggle"]=>  ""
      ["acfe_flexible_layout_title"]=>  ""
      ["content"]=> "Need to collect the content here"
    }
    array(2) {
      ["acf_fc_layout"]=> "car_details"
      ["cars"]=>
      array(3) {
        [0]=>
        array(2) {
          ["name"]=>
          string(23) "Audi"
          ["content"]=> "Need to collect the content here"
        }
        [1]=>
        array(2) {
          ["name"]=>
          string(23) "Seat"
          ["content"]=> "Need to collect the content here"
        }
        [2]=>
        array(2) {
          ["name"]=>
          string(23) "Opel"
          ["content"]=> "Need to collect the content here"
        }
      }
    }
  }
}

I not sure how to get all the contents into one variable


Solution

  • If you want to use your current logic you could create a helper function in functions.php that returns the $word_counter_main per post.

    functions.php

    function get_current_post_word_count($post_id) {
    
       $word_counter_main = 0;
       $word_counter_main += str_word_count(get_the_content($post_id));
    
       if (get_field('layouts_templates', 'option')):
         $word_counter_main = 0;
         while (have_rows('layouts', $post_id)) : the_row();
    
         $word_counter_main += str_word_count(get_sub_field('content'));
    
         if (get_row_layout() == 'template') {
          $template_builders[get_sub_field('layouts_template')] = null;                 
         }
    
       endwhile;
     endif;
    
     return $word_counter_main;
    }
    

    single.php

    $post_word_count = NAMESPACE\get_current_post_word_count(get_the_ID());
    

    Another approach would be to create a meta field per post that would get updated whenever you update a post, then you could just load the meta field value. That way you wouldn't have to loop through all the content each time you load a post.

    UPDATE:

    Here's a neat way to get all values by a key from a multidimensional array using PHP. This could work in your case as well.

    functions.php

        $example_array = array(
            'layouts' => array(
                array(
                    'acf_fc_layout' => 'main-content',
                    'acfe_flexible_toggle' => '',
                    'acfe_flexible_layout_title' => '',
                    'content' => 'This is main content',
                ),
                array(
                    'acf_fc_layout' => 'car_details',
                    'cars' => array(
                        array(
                            'name' => 'Audi',
                            'content' => 'This is content from audi',
                        ),
                        array(
                            'name' => 'Seat',
                            'content' => 'This is content from seat',
                        ),
                        array(
                            'name' => 'Opel',
                            'content' => 'This is content from opel',
                        ),
                    ),
                ),
            ),
        );
    
        function array_value_recursive($key, array $arr)
        {
            $val = array();
    
            array_walk_recursive($arr, function ($v, $k) use ($key, &$val) {
                if ($k == $key) array_push($val, $v);
            });
    
            return count($val) > 1 ? $val : array_pop($val);
        }
    
        $only_content_fields = array_value_recursive('content', $example_array);
    
    

    Console output

    Array
    (
        [0] => This is main content
        [1] => This is content from audi
        [2] => This is content from seat
        [3] => This is content from opel
    )
    

    Now you could loop this newly created array and count the words using the str_word_count function. Hopefully this helps :)