1
\$\begingroup\$

I've been working on a WordPress pricing page template and would appreciate feedback on the code structure, organization, and any potential improvements. The template is used to display a pricing list with categories and items.

How can we reduce this whole amount of nestiness and code?

<?php
get_header();

$cennik_kat_order = get_field('cennik_kat', 'option');

if (!is_array($cennik_kat_order)) {
    $cennik_kat_order = [];
}
$sorted_cennik_categories = get_sorted_pricelist_categories($cennik_kat_order);
$mediraty = get_field('mediraty', 'option');

?>
<section class="prl c d-grid l-d">
    <div class="prl--s">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed at odio vel tortor rhoncus lacinia ac ac nulla. Vestibulum facilisis justo nec congue facilisis. Morbi venenatis, nisi at lacinia fermentum, neque justo fermentum nulla, a laoreet metus ex id velit. Vivamus auctor erat vel augue tincidunt, in vehicula ex laoreet. Cras ullamcorper metus at mi sodales, at commodo urna varius. Vestibulum ac iaculis velit. Duis eu turpis risus. Nulla facilisi. Donec posuere erat eu purus congue, non tempus lorem tristique. Nulla a volutpat arcu, non rhoncus odio. Nam bibendum pharetra turpis, nec vehicula ipsum fermentum at. Aliquam erat volutpat. Fusce nec efficitur ex. Sed congue metus ac ligula iaculis, ac cursus massa cursus. Quisque nec libero sed elit fringilla tempus nec at felis. Sed ut viverra risus.

        <?php
        $image = new MiLossy($mediraty);
        $image->Draw();
        ?>
    </div>
    <h3 class="prl__heading h3 f-400 c-black-800 m-reset">Lorem Ipsum</h3>
    <div class="prl__cats hm d-flex f-c">
        <?php
        foreach ($sorted_cennik_categories as $key => $category) :
            $icon  = get_field_from_oferta_parent_by_taxonomy('icon', $category->term_id, 'taxonomy_pricelist');
            $color = get_field_from_oferta_parent_by_taxonomy('color', $category->term_id, 'taxonomy_pricelist');
        ?>

            <div class="details prl__cat hm__item--wr br" id="<?= $category->slug; ?>">
                <summary class="hm__title--main p d-flex f-c ais jcb cp w-100 zero-level-hierarchy">
                    <span class="c-black-800 h5 f-400 m-0">Lorem Ipsum
                        <?php if (DEBUG) : ?>
                            <a style="font-size: 1em;" tabindex="-1" href="<?= get_edit_term_link($category->term_id, 'cennik_kat'); ?>">(Edytuj)</a>
                        <?php endif; ?></span>
                    <div class="prl__cat-btn d-flex aic jce">
                        <span class="toggle-text level-zero">Rozwiń</span>
                        <div class="hm__icon-wr d-flex aic jcc br">
                            <svg class="hm__icon" width="16" height="16" viewBox="0 0 16 16">
                                <use xlink:href="#arrow-down"></use>
                            </svg>
                        </div>
                    </div>
                </summary>
                <div class="content-wrapper prl__hm--wrapper w-100">
                    <div class="content-inner prl__hm--inner">
                        <div class="real-content prl__gap d-flex f-c">
                            <?php
                            $term_posts_order_ids = get_field('cennik_order_' . $category->slug, 'option');
                            $query                = new WP_Query(
                                [
                                    'post_type'      => 'cennik',
                                    'posts_per_page' => -1,
                                    'post_parent'    => 0,
                                    'tax_query'      => [
                                        [
                                            'taxonomy' => 'cennik_kat',
                                            'field'    => 'term_id',
                                            'terms'    => $category->term_id,
                                            'include_children' => true,
                                            'operator' => 'IN',
                                        ],
                                    ],
                                    'orderby'        => 'title',
                                    'order'          => 'ASC',
                                ]
                            );

                            if (is_array($term_posts_order_ids) && !empty($term_posts_order_ids)) {
                                $query->posts = sort_objects_by_id_order($query->posts, $term_posts_order_ids);
                            }

                            if ($query->have_posts()) :
                                while ($query->have_posts()) :
                                    $query->the_post();

                                    $is_pakiet      = has_term(get_pakiet_term_id(), 'cennik_kat', $post->ID);
                                    $maybe_children = get_pages(['child_of' => $post->ID, 'post_type' => 'cennik']);
                                    $has_children   = !empty($maybe_children);
                                    $is_pakiet      = $has_children && $is_pakiet;
                                    $is_group       = $has_children && !$is_pakiet;
                                    $is_single      = !$has_children && !$is_pakiet;
                            ?>
                                    <?php if ($is_group) : ?>
                                        <div class="details prl__item prl__gap prl__item--gr d-flex f-c w-100">
                                            <summary class="hm__title p d-flex f-c ais jcb cp w-100 first-level-hierarchy">
                                                <span class="c-black-800 h5 f-400 m-0">Lorem Ipsum
                                                    <?php if (DEBUG) : ?>
                                                        <a style="font-size: 1em;" tabindex="-1" href="<?= get_edit_post_link($post->ID); ?>">(Edytuj)</a>
                                                    <?php endif; ?>
                                                </span>
                                                <div class="prl__cat-btn d-flex aic jce">
                                                    <span class="toggle-text level-one">Rozwiń</span>
                                                    <div class="hm__icon-wr d-flex aic jcc br">
                                                        <svg class="hm__icon" width="16" height="16" viewBox="0 0 16 16">
                                                            <use xlink:href="#arrow-down"></use>
                                                        </svg>
                                                    </div>
                                                </div>
                                            </summary>
                                            <div class="content-wrapper prl__hm--wrapper">
                                                <div class="content-inner prl__hm--inner">
                                                    <div class="real-content prl__items-gr prl__gap--small d-flex f-c">
                                                        <div class="prl__hr w-100"></div>
                                                        <?php
                                                        if ($has_children) {
                                                            $children           = $maybe_children;
                                                            $children_order_ids = get_field('children_order', $post->ID);
                                                            $maybe_children     = get_pages(['child_of' => $post->ID, 'post_type' => 'cennik']);
                                                            $has_children       = !empty($maybe_children);
                                                            $grandchildrens     = [];

                                                            if (is_array($children_order_ids) && !empty($children_order_ids)) {
                                                                $children = sort_objects_by_id_order($children, $children_order_ids);
                                                            }

                                                            foreach ($children as $key => $page) {
                                                                $is_group       = has_term(get_pakiet_term_id(), 'cennik_kat', $page->ID);
                                                                $group_children = get_pages(['child_of' => $page->ID, 'post_type' => 'cennik']);
                                                                if ($is_group) {
                                                        ?>
                                                                    <div class="details details--last prl__item prl__gap prl__item--gr d-flex f-c w-100">
                                                                        <summary class="hm__title p d-flex f-c ais jcb cp w-100">
                                                                            <span class="c-black-800 h6 f-500 m-0 l-3">Lorem Ipsum
                                                                                <?php if (DEBUG) : ?>
                                                                                    <a style="font-size: 1em;" tabindex="-1" href="<?= get_edit_post_link($page->ID); ?>">(Edytuj)</a>
                                                                                <?php endif; ?>
                                                                            </span>
                                                                            <div class="prl__cat-btn d-flex aic jce">
                                                                                <span class="toggle-text level-one">Rozwiń</span>
                                                                                <div class="hm__icon-wr d-flex aic jcc br">
                                                                                    <svg class="hm__icon" width="16" height="16" viewBox="0 0 16 16">
                                                                                        <use xlink:href="#arrow-down"></use>
                                                                                    </svg>
                                                                                </div>
                                                                            </div>
                                                                        </summary>
                                                                        <div class="content-wrapper prl__hm--wrapper w-100">
                                                                            <div class="content-inner prl__hm--inner">
                                                                                <div class="real-content prl__gap d-flex f-c">
                                                                                    <div class="prl__hr w-100"></div>
                                                                                    <?php
                                                                                    foreach ($group_children as $grandchild) {
                                                                                        $displayed_grandchildren[] = $grandchild->ID;
                                                                                        print_pricelist_item($grandchild->ID, true);
                                                                                    }
                                                                                    ?>
                                                                                </div>
                                                                            </div>
                                                                        </div>
                                                                    </div>
                                                        <?php
                                                                }
                                                            }

                                                            foreach ($children as $page) {
                                                                $is_group = has_term(get_pakiet_term_id(), 'cennik_kat', $page->ID);
                                                                if (!$is_group && !in_array($page->ID, $displayed_grandchildren)) {
                                                                    print_pricelist_item($page->ID, false);
                                                                }
                                                            }
                                                        }
                                                        ?>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                    <?php endif; ?>
                                    <?php if ($is_single) : ?>
                                        <div class="real-content prl__item prl__gap d-flex f-c w-100">
                                            <?php print_pricelist_item($post->ID); ?>
                                        </div>
                                    <?php endif; ?>
                            <?php
                                endwhile;
                                wp_reset_postdata();
                            endif;
                            ?>
                        </div>
                    </div>
                </div>
            </div>
        <?php endforeach; ?>
    </div>
</section>

<?php try_attach_resource('js/priceListOpening.min.js'); ?>

<?php
get_footer();
?>

function get_sorted_pricelist_categories( array $ordered_terms_id, bool $hide_empty = true ): array {
    $all_terms = get_terms(
        [
            'taxonomy'   => 'cennik_kat',
            'hide_empty' => $hide_empty,
        ]
    );

    if ( ! empty( $ordered_terms_id ) ) {
        $sorted_terms = sort_objects_by_id_order( $all_terms, $ordered_terms_id, 'term_id' );
    } else {
        usort(
            $all_terms,
            function ( $a, $b ) {
                return strcmp( $a->slug, $b->slug );
            }
        );
        $sorted_terms = $all_terms;
    }

    foreach ( $sorted_terms as $key => $term ) {
        if ( $term->term_id == get_pakiet_term_id() ) {
            unset( $sorted_terms[ $key ] );
        }
    }

    return array_values( $sorted_terms );
}
/**
 * Recursively retrieves a custom field value from a post or its ancestors.
 *
 * @param WP_Post $post_object The post object to start searching from.
 * @param string $field_name The custom field name to retrieve.
 * @return int|null The field value or null if not found.
 */
function get_recursive_category_id( WP_Post $post_object, string $field_name ): ?int {
    if ( $cat_id = get_field( $field_name, $post_object->ID ) ) {
        return $cat_id;
    }

    if ( ! $post_object->post_parent ) {
        return null;
    }

    $parent = get_post( $post_object->post_parent );
    return get_recursive_category_id( $parent, $field_name );
}

/**
 * Retrieves and formats the price for a specific post.
 *
 * @param int $post_id The ID of the post for which to retrieve the price.
 * @return string Formatted price or a call-to-action text if the price is not set.
 */
function show_price( int $post_id ): string {
    $maybe_price = get_field( 'price', $post_id );
    $maybe_from  = get_field( 'prefix_from', $post_id );
    $maybe_to    = get_field( 'prefix_to', $post_id );
    $price_upper = get_field( 'price_upper', $post_id );

    if ( ! $maybe_price ) {
        return 'Zadzwoń';
    }

    $price = format_price( $maybe_price );

    if ( $maybe_price && $maybe_from ) {
        $price = 'od ' . $price;
    }

    if ( $maybe_price && $maybe_to ) {
        $price .= ' do ' . $price_upper . ' zł';
    }

    return $price;
}

function show_pricelist_item_type( int $post_id ) {
    $type = get_field( 'type', $post_id );
    if ( $type ) {
        return $type;
    }

    $parent_id = get_post( $post_id )->post_parent;
    if ( $parent_id ) {
        return show_pricelist_item_type( $parent_id );
    }

    return '';
}

/**
 * Print a pricelist item.
 *
 * @param int $post_id The post ID of the pricelist item.
 * @param bool $white_style Whether to use white style for the item (default: false).
 */
function print_pricelist_item( int $post_id, bool $second_level = false ) {
    ?>
    <div class="prl__price-item<?= $second_level ? ' prl__test' : null; ?> d-flex aic jcb">
        <div class="d-flex f-c" style="gap: 8px;">
            <div class="prl__heading h6 m-0">
                <?= get_the_title( $post_id ); ?>
                <?php if ( DEBUG ) : ?>
                    <a style="font-size: 1em;" tabindex="-1" href="<?= get_edit_post_link( $post_id ); ?>">(Edytuj)</a>
                <?php endif; ?>
            </div>
            <div class="prl__desc"><?= get_field( 'desc', $post_id ); ?></div>
        </div>
        <div class="d-flex f-c jcc aie" style="gap:8px">
            <div class="prl__price h6 f-500 t-r"><?= show_price( $post_id ); ?></div>
            <!-- show_pricelist_item_type -->
            <div class="prl__type h6 f-400 t-r c-black-700">
                <?= show_pricelist_item_type( $post_id ); ?>
                <?php if ( DEBUG ) : ?>
                    <a style="font-size: 1em;" tabindex="-1" href="<?= get_edit_post_link( $post_id ); ?>">(Edytuj)</a>
                <?php endif; ?>
            </div>
        </div>
    </div>
    <?php
}

Here's what I'd like feedback on:

Code Structure: Any suggestions for improving the organization and structure of the code to make it more modular and maintainable.

Performance: Any potential performance optimizations or areas where code can be optimized.

Readability: Suggestions for improving code comments, variable names, and overall code readability.

WordPress Best Practices: Any recommendations for following WordPress best practices and conventions.

Code Quality: Any potential bugs or issues in the code that I may have missed.

Logic on screen: pricelist

I appreciate your time and assistance in reviewing this code. Your feedback will help me improve my coding skills and the quality of my project.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Incorporating advice from an answer into the question violates the question-and-answer nature of this site. You could post improved code as a new question, as an answer, or as a link to an external site - as described in I improved my code based on the reviews. What next?. I have rolled back the edit, so the answers make sense again. \$\endgroup\$ Commented Feb 4 at 13:51
  • \$\begingroup\$ Well I didnt use it, his code is far away from logic from my code. \$\endgroup\$ Commented Feb 4 at 14:36
  • \$\begingroup\$ You definitely did use the advice about $icon and $color, and that invalidated the answer you'd received. \$\endgroup\$ Commented Feb 4 at 14:45
  • \$\begingroup\$ Well, that's the only things. \$\endgroup\$ Commented Feb 4 at 14:46

2 Answers 2

2
\$\begingroup\$

I don't know anything about Wordpress, so below are some generalized suggestions.

Prepare the data first

Separating business logic from display logic will make this code much cleaner and easier to comprehend and navigate. Consider creating a function that runs all these nested loops and returns a single final array that contains all the required data already prepared.

And then, only use this array (and its nested members) to form the final HTML.

Remove unused variables

$icon and $color are nowhere to be utilized in the code. Why define them?

Avoid useless conditions

Preparing the data will help to avoid some useless nesting. If your data preparation returns an empty array for the children, you can go straight to foreach, bypassing the $has_children condition.

Use consistent control structures

While inside HTML, always use alternative syntax without curly braces.

In the end, the main foreach loop could be like below. Still monstrous but already half the size and with less nesting.

<?php foreach ($data as $key => $category): ?>
    <div class="details prl__cat hm__item--wr br" id="<?= $category->slug; ?>">
        <summary class="hm__title--main p d-flex f-c ais jcb cp w-100 zero-level-hierarchy">
            <span class="c-black-800 h5 f-400 m-0">Lorem Ipsum
                <?php if (DEBUG) : ?>
                    <a style="font-size: 1em;" tabindex="-1" href="<?= get_edit_term_link($category->term_id, 'cennik_kat'); ?>">(Edytuj)</a>
                <?php endif; ?>
            </span>
            <div class="prl__cat-btn d-flex aic jce">
                <span class="toggle-text level-zero">Rozwiń</span>
                <div class="hm__icon-wr d-flex aic jcc br">
                    <svg class="hm__icon" width="16" height="16" viewBox="0 0 16 16">
                        <use xlink:href="#arrow-down"></use>
                    </svg>
                </div>
            </div>
        </summary>
        <div class="content-wrapper prl__hm--wrapper w-100">
            <div class="content-inner prl__hm--inner">
                <div class="real-content prl__gap d-flex f-c">
                    <?php foreach ($category->posts as $row): ?>
                        <div class="details prl__item prl__gap prl__item--gr d-flex f-c w-100">
                            <summary class="hm__title p d-flex f-c ais jcb cp w-100 first-level-hierarchy">
                                <span class="c-black-800 h5 f-400 m-0">Lorem Ipsum
                                    <?php if (DEBUG) : ?>
                                        <a style="font-size: 1em;" tabindex="-1" href="<?= get_edit_post_link($post->ID); ?>">(Edytuj)</a>
                                    <?php endif; ?>
                                </span>
                                <div class="prl__cat-btn d-flex aic jce">
                                    <span class="toggle-text level-one">Rozwiń</span>
                                    <div class="hm__icon-wr d-flex aic jcc br">
                                        <svg class="hm__icon" width="16" height="16" viewBox="0 0 16 16">
                                            <use xlink:href="#arrow-down"></use>
                                        </svg>
                                    </div>
                                </div>
                            </summary>
                            <div class="content-wrapper prl__hm--wrapper">
                                <div class="content-inner prl__hm--inner">
                                    <div class="real-content prl__items-gr prl__gap--small d-flex f-c">
                                        <div class="prl__hr w-100"></div>
                                        <?php foreach ($row['children'] as $pagekey => $page): ?>
                                            <div class="details details--last prl__item prl__gap prl__item--gr d-flex f-c w-100">
                                                <summary class="hm__title p d-flex f-c ais jcb cp w-100">
                                                    <span class="c-black-800 h6 f-500 m-0 l-3">Lorem Ipsum
                                                        <?php if (DEBUG) : ?>
                                                            <a style="font-size: 1em;" tabindex="-1" href="<?= get_edit_post_link($page->ID); ?>">(Edytuj)</a>
                                                        <?php endif; ?>
                                                    </span>
                                                    <div class="prl__cat-btn d-flex aic jce">
                                                        <span class="toggle-text level-one">Rozwiń</span>
                                                        <div class="hm__icon-wr d-flex aic jcc br">
                                                            <svg class="hm__icon" width="16" height="16" viewBox="0 0 16 16">
                                                                <use xlink:href="#arrow-down"></use>
                                                            </svg>
                                                        </div>
                                                    </div>
                                                </summary>
                                                <div class="content-wrapper prl__hm--wrapper w-100">
                                                    <div class="content-inner prl__hm--inner">
                                                        <div class="real-content prl__gap d-flex f-c">
                                                            <div class="prl__hr w-100"></div>
                                                            <?php foreach ($page['group_children'] as $grandchild): ?>
                                                                <?php print_pricelist_item($grandchild->ID, true) ?>
                                                            <?php endforeach ?>
                                                        </div>
                                                    </div>
                                                </div>
                                            </div>
                                        <?php endforeach ?>
                                        <?php foreach  ($row['children'] as $page): ?>
                                            <?php print_pricelist_item($page->ID, false) ?>
                                        <?php endforeach ?>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="real-content prl__item prl__gap d-flex f-c w-100">
                            <?php print_pricelist_item($post->ID); ?>
                        </div>
                    <?php endforeach ?>
                </div>
            </div>
        </div>
    </div>
<?php endforeach; ?>
\$\endgroup\$
0
1
\$\begingroup\$

I am not familiar with WP development but I would always advise PHP developers to use a template engine like Twig.

Quick gains: separate logic from presentation, reduce the size of your files. And you can break your HTML page further in multiple files (sections) and include them as necessary. There is no need to touch PHP code when you just want to work on the layout of the pages. You may want to switch between themes too.

As suggested above, write functions that return ready to use arrays or similar structures, but do not print anything: let the templating engine take care of that. Here is an intro page that shows how you would use Twig to loop on an array to render HTML.

Right now, the code is hard to follow, in part because of the nested DIVs mixed up with PHP control structures, and because it's just too long and there is too much scrolling involved. The endwhile is 100 lines ahead from the opening while clause. I should be able to see the whole block at once, so I can see immediately what the section does (and be sure I am making edits at the right place).

Here is one portion of code that illustrates the problem (it becomes more obvious when you zoom out):

                                                            foreach ($children as $page) {
                                                                $is_group = has_term(get_pakiet_term_id(), 'cennik_kat', $page->ID);
                                                                if (!$is_group && !in_array($page->ID, $displayed_grandchildren)) {
                                                                    print_pricelist_item($page->ID, false);
                                                                }
                                                            }
                                                        }
                                                        ?>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                    <?php endif; ?>
                                    <?php if ($is_single) : ?>
                                        <div class="real-content prl__item prl__gap d-flex f-c w-100">
                                            <?php print_pricelist_item($post->ID); ?>
                                        </div>
                                    <?php endif; ?>
                            <?php
                                endwhile;
                                wp_reset_postdata();
                            endif;
                            ?>
                        </div>
                    </div>
                </div>
            </div>
        <?php endforeach; ?>
    </div>
</section>

If you need to make even a minor edit, the risk of breaking code is very high. Or the code may still work but you could end up with broken HTML or functional bugs because your control structures (if/for/while etc) have been displaced.

In addition to the main script, you have a couple functions like print_pricelist_item, that you could move to a separate file to de-clutter your script. All you have to do is an include. Just doing this reduces the number of lines by nearly a half.

Once you have broken up the code in pieces, it will become more clear and more manageable. If you let it grow, you will lose oversight and very likely introduce more bugs. There are quick gains to be had if you are willing to reorganize your project, without even increasing overall quality of the code.

\$\endgroup\$
7
  • \$\begingroup\$ Well not thanks, i'm not looking into changing whole webpage structure just for a single component. I was just looking forward for some sort of refactoring against the code, thus making it less complicated by splitting into functions etc. Not excatly what I'm looking for, general advices are also not so helpful, I'm not an junior, but also I'm not an expert in terms of PHP and WP development, but Every answer there are pretty basic, not pretty helpful, i'm looking forward for advices how to reduce this long code, and advices like 'broke up code in pieces' are not clarify anything. \$\endgroup\$ Commented Feb 5 at 6:45
  • \$\begingroup\$ @KermitTheFrog do I get it right you are looking into reducing the HTML code? Because it's HTML code which is being the longest code here. In this case you'd probably re-ask this question, providing the rendered HTML result of this code, and asking help in making this HTML less nested \$\endgroup\$ Commented Feb 5 at 7:13
  • \$\begingroup\$ I dont thin pasting there just HTML, without this whole logic will be good approach, you have every case displayed right there, so it will be clear to not just (without any context) reduce the code, as you can see before, somehow @YourCommonSense thought deleting flags and reducing the logic will be good approach XD How will you be able to reduce this bigass loop without knowledge about $is_group etc? \$\endgroup\$ Commented Feb 5 at 7:30
  • 1
    \$\begingroup\$ @KermitTheFrog it is just not clear what exactly you want to reduce. I removed all the PHP code from your template, leaving only few lines of necessary control structures. You didn't seem to like it, calling "not so helpful". Well, the only thing left to make it big is HTML. So I make it, you want to reduce that HTML. If you want to reduce HTML, then you need to post HTML. It's quite simple a logic. \$\endgroup\$ Commented Feb 5 at 8:14
  • \$\begingroup\$ Man, you are looking on a big scope that has potential to be replaced with for example recursive function, you get rid of all of the logic and called it 'useless', I think you are awared of that, like refactoring code from now on is deleting the logic? XDDD + also I've attached an screenshot for you, so you are able to see. Your code does not respecting the logic, do you what edge cases? There was a purpose for my logic attached inside the scope, I wanted to make my code nesting-friendly, to not re-declare foreach or any loop. \$\endgroup\$ Commented Feb 5 at 8:20

Your Answer

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

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