0

i have a question about improving my solution.

For my task i build a Gutenberg block which shows the latest 5 Posts. This Posts show also the Category Name in the Frontend part which have different styling each.

so I'm using the get_post() function to get all Posts, but there comes no Categories which is the reason i also used wp_get_post_categories() function.

This works fine , i used a foreach in a foreach to get my results. But it kinda feel Hardcoded especially the checking if Category "News" or "informative" is used is definitely hard coded.

is there a better solution how can i archive it that i can get the Posts with there bonded Post categories?

this was the best i can get for now and it works but im not happy with this solution.

my code:

...

$post = get_posts(
     [
        'posts_per_page' => 5,
     ]
);

...

ob_start();

some echos for html 

...

            foreach ($post AS $p)
            {

                $category = wp_get_post_categories($p->ID);
                foreach ($category AS $cat)
                {
                    echo '<div>';
                    echo '<div class="blog-image">';
                    echo '<img alt="" content="'.get_the_post_thumbnail($p->ID).'">';
                    echo '</div>';
                    echo '<div class="blog-data">';
                    echo '<div class="data-top">';
                    if(get_cat_name((int) $cat) === 'News')
                    {
                        echo '<div class="blog-type"><span class="news">'.get_cat_name((int) $cat).'</span></div>';

                    }elseif (get_cat_name((int) $cat) === 'Informative')
                    {
                        echo '<div class="blog-type"><span class="informative">'.get_cat_name((int) $cat).'</span></div>';
                    }
                    echo '<h3 class="blog-title">'.$p->post_title.'</h3>';
                    echo '</div>';
                    echo '<div class="data-bottom">';
                    echo '<div class="blog-info">';
                    echo '<p class="blog-date">'.date("d F Y", strtotime( $p->post_date)).'</p>';
                    echo '<div class="blog-link"><a href='.$p->guid.' >Read more</a></div> ';
                    echo '</div>';
                    echo '</div>';
                    echo '</div>';
                    echo '</div>';

                }
                echo '<br/>';
            }

...

more echos to close all html tags

    return ob_get_clean();

here is also how it should look:

enter image description here

2
  • Welcome to WPSE. Did I understand you correctly, that you want to display all of the categories attached to a post? Or only display the category, if it is either News or Informative? Jul 13, 2022 at 7:32
  • I want to get all the posts but with there categories, in the get_post() function the value of the category is not given. is there a better solution then mine this was my question because i think this is not the right way ?
    – Jaba
    Jul 13, 2022 at 18:56

2 Answers 2

0

One major issue that I see in your code is the nested foreach loop. If a post has more than one category then the post would be rendered again and again for each category.

Another thing is that using wp_get_post_categories() might cause performance issues as the result of the function isn't cached and the database is called every time the function is called.

To replace the hardcoded conditional logic you could consider adding controls to the block with InspectorControls so that the user can choose for example, if the category or categories should be displayed.

Here's an example how I think your code could be improved. The code below is untested and should be checked and modified to fit your exact needs and context.

function my_render_callback($block_attributes, $content) {
  // use plural for variable name to better reflect what it contains
  // could also use WP_Query here directly
  $posts = get_posts([
    'posts_per_page' => 5, // this could also come from $block_attributes
  ]);
  
  // map (i.e. loop posts and apply the callback to each of them) found posts to html
  // pass block attributes to renderer for conditional logic etc.
  $posts_html = array_map(
    'my_post_renderer',
    $posts,
    $block_attributes
  );

  // return html as a string
  return sprintf(
    '<div class="some-wrapper">
      <div class="another-wrapper-maybe">
        %s
      </div>
    </div>',
    implode('', $posts_html)
  );
}

function my_post_renderer(WP_Post $post, array $block_attributes) {
  // control the html output with block attributes set with InspectorControls
  $show_only_first_category = ! empty( $block_attributes['onlyDisplayFirstCategory'] );
  $categories_to_display = $block_attributes['categoriesToDisplay']; // term name/term_id/slug, or an array of them

  // helper variable for conditional logic
  $display_categories = $categories_to_display && has_term( $categories_to_display, 'category', $post );
  
  // format post to html
  // sprintf makes the resulting html a little easier to read than bunch of echos
  // sprintf also eliminates the need to use output buffering
  return sprintf(
    '<div>
      <div class="blog-image">%s</div>
      <div class="blog-data">
        <div class="data-top">
          <div class="blog-type">
            %s
          </div>
          <h3 class="blog-title">%s</div>
        </div>
        <div class="data-bottom">
          <div class="blog-info">
            <p class="blog-date">%s</p>
            <div class="blog-link">
              <a href="%s">%s</a>
            </div>
          </div>
        </div>
      </div>
    </div>',
    // get html img tag with srcset, lazy load, and other good stuff WP provides
    // change size, if needed
    get_the_post_thumbnail( $post, 'medium' ),
    // lets use our own helper function to format the category html
    $display_categories ? my_post_category_renderer( $post, $show_only_first_category ) : '',
    // get title directly from the post object or use get_the_title() for default processing and filtering to be applied
    esc_html( get_the_title( $post ) ),
    // use function to let WP handle date formatting based on the format set in the site settings 
    esc_html( get_the_date( '', $post ) ),
    // use functions instead of guid so that url will use the permalink structure set in the site settings
    esc_url( get_permalink( $post ) ), 
    // make anchor text translatable
    esc_html__( 'Read more', 'your-text-domain' )
  );
}

function my_post_category_renderer(WP_Post $post, bool $only_first_category) {
  // prefer get_the_category() over wp_get_post_categories() as the result of the second one isn't cached
  // get_the_category() returns an array of WP_Terms
  $post_categories = get_the_category($post->ID); 
  // demonstration of how to display only the first or all of the categories attached to the post
  if ( $only_first_category ) {
    return my_post_category_html( $post_categories[0] );
  } else {
    return implode(
      '',
      array_map('my_post_category_html', $post_categories)
    );
  }
}

function my_post_category_html(WP_Term $category) {
  return sprintf(
    '<span class="%s">%s</span>',
    esc_attr( $category->slug ),
    esc_html( $category->name )
  );
}
4
  • Thank you for this answer it helped me a lot ! :)
    – Jaba
    Jul 18, 2022 at 10:11
  • Hey @Jaba If you feel that it's the correct answer then don't hesitate to mark it as the right answer. Or at least vote up for it.
    – djanym
    Jul 22, 2022 at 15:33
  • and a few tips here (If you have not read it yet) wordpress.stackexchange.com/help/behavior
    – djanym
    Jul 22, 2022 at 15:34
  • 1
    @djanym i cant vote for now i would love to :(
    – Jaba
    Aug 11, 2022 at 11:13
0

Sorry for the late answer, It helps me a bit thanks @antti, so i couldn't get the Category Name out of the post without wp_get_post_categories()

i try to change the way i get the posts from get_posts() to get it from WP_Query. but i had to use the wp_get_post_categories(). so i leaved it. but thanks for the tip with the foreach loop issue.

i change the second foreach :

foreach ($postslist->posts AS $p)
            {
                $category = wp_get_post_categories($p->ID);
                foreach ($category AS $key => $cat )
                {
                    if($key == 0)
                    {
.....

so im taking only the first one when there are 2 cats.

the way Antti described didn't work in my case but i guess for another project it is perfect so i will try it again . thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.