Как избежать распространенных ошибок при создании тем WordPress

Дата публикации:Март 10, 2018

Если вы задумываетесь о создании платных или бесплатных тем WordPress, вы обязательно столкнетесь с массой разных ошибок и сложностей. В данной статье я приведу их и покажу, как их избежать.

Не изобретайте колесо

Будьте осторожны, когда вы стремитесь что-то улучшить – особенно в том случае, когда вы создаете функцию, выполняющую примерно то же самое, что и другая, уже существующая функция. Чем больше «красивого» кода вы добавите, тем сложнее вам будет его поддерживать в дальнейшем. Количество нажатий на клавиши не является показателем вашей производительности как разработчика. Лучше потратить больше времени на обдумывание кода, чем на его написание.

Я много раз допускал подобную ошибку, полагая, что придерживаюсь принципа DRY (Don’t repeat yourself).

К примеру, я создал функцию под названием get_portfolio_part($name, $slug). Можете догадаться, что она делала? Да, она была оболочкой, позволяющей избежать многократного написания get_template_part(“portfolio/$name”, $slug); Я называю это «постепенным изобретением колеса». Функция делает почти то же самое, что и оригинал, при этом усложняя кодовую базу.

Не делайте так! Со временем будет сложно понять, как работает эта функция. Даже если вы считаете, что она простая и понятная – это все равно приводит к дополнительному анализу еще одной функции либо к угадыванию того, откуда эта функция получает файлы.

Я помню аргумент, который возник у меня в голову при создании этой функции get_portfolio_part() – как быть, если я решу переместить каталог портфолио в будущем? Мне достаточно будет сделать «уродливый поиск и замену».

Знаете, сколько раз я менял название каталога за все эти годы? Ноль. Это ведет нас к ошибке номер 2.

Не пытайтесь предсказать будущее

Люди очень плохо предсказывают будущее. И мы, как разработчики, постоянно пытаемся это делать.

Представьте, что вы сделали опцию по выводу социальных иконок где-либо в записи. Давайте отложим обсуждения о том, является ли это территорией плагинов или нет. Представим, что вы решили это сделать. Наша гипотетическая функция будет иметь приблизительно следующий вид:


function has_social_icon($icon) {

    $icons = get_post_meta(get_the_ID(), 'post_social_icons', true);

    // do what has to be done with $icons
    
    return true;    
}

Однако затем меня начинает тяготить мысль: «Как быть, если в будущем я захочу использовать эту функцию где-либо за пределами цикла?» В итоге я делаю рефакторинг, который выглядит так:


function has_social_icon($icon, $post_id = 0) {

    if( ! $post_id ) {
        $post_id = get_the_ID();
    }

    $icons = get_post_meta($post_id, 'post_social_icons', true);

    // do what has to be done with $icons

    return true;    
}


Отлично! Теперь мы получили абсолютно ненужное раздувание кода во имя несуществующего будущего. Это простой пример, как это происходит. Чем сложнее вещи, тем легче попасться на такой крючок.

Не делайте так! Рефакторинг кода должен проводиться только исходя из реальных потребностей, а не гипотетических сценариев, которые могут так и не случиться.

Бездумная оптимизация – корень всех зол

Я не думал об этом до недавнего времени. Очень сложно изменить своим старым привычкам. Я по-прежнему нередко ловлю себя на мысли, что оптимизирую код, который я не должен был оптимизировать.

Делали ли вы когда-либо так?


<?php
$post_id = get_the_ID(); // look 'ma - I'm reusing ID, saving 1 function call!
$thumb = get_the_post_thumbnail( $post_id, 'large'); // look 'ma - I'm saving another function call! Yay!
?>

<div id="post-<?php echo $post_id ?>"

<?php if( $thumb ): ?>
    <div class="thumbnail">
        <?php echo $thumb ?>
    </div>
<?php endif; ?>

</div>

Присваивание значения переменной, поскольку вы используете ее дважды, позволит вам сохранить 0.000002 мс (что полностью бесполезно). Но вообще, если этот запрос будет кэширован, что произойдет в большинстве случаев, вы сохраните 0 мс.

Вот гораздо более простой способ написать то же самое в WordPress:

<div id="post-<?php the_ID() ?>"

<?php if( has_post_thumbnail() ): ?>
    <div class="thumbnail">
        <?php the_post_thumbnail('large') ?>
    </div>
<?php endif; ?>

</div>

Да, здесь у нас два дополнительных вызова, но производительность кода никак не страдает. Это не значит, что вы не должны оптимизировать свой код вообще. Подходите к этому с умом. Если вы сохраняете запросы к базе данных или используете ресурсоемкие функции в цикле – естественно, вам нужно заботиться об оптимизации кода. Но делайте это разумно. Не нужно закидывать все в переменную, чтобы сохранить вызов функции. Кстати, раз уж заговорили о переменных…

Избегайте переменных в шаблонах

Когда вы закончите с лишней оптимизацией, вы заметите, что у вас стало гораздо меньше переменных в шаблонах. Я рекомендую вам развить эту идею и вообще отказаться от переменных в шаблонах.

Хотя без некоторых логических условий не обойтись, вы по-прежнему можете улучшить читабельность ваших шаблонов, удалив из них как можно больше всего лишнего.

Простой пример:

<?php
$logo_url = false;
$thumbnail_url = wp_get_attachment_image_src( get_theme_mod( 'hypthetical_theme_logo' ), 'full' );
if( $thumbnail_url ) {
    $logo_url = $thumbnail_url[0];    
}
?>
<?php if( $logo_url ): ?>
    <a href="<?php echo esc_url( home_url() ); ?>" title="<?php bloginfo( 'name' ); ?>" class="custom-logo">
        <img src="<?php echo $logo_url; ?>" />
    </a>
<?php endif; ?>

В целом код может показаться не таким уж и страшным, но когда он оказывается в header.php, он начинает выглядеть «грязновато», особенно если он обернут в несколько div с отступами.

Почему мы должны заботиться в шаблоне о том, как возвращается логотип? Шаблоны просто должны выводить контент, а не получать и анализировать содержимое.

Вместо определения двух переменных почему бы просто не вынести все это в функции? Тогда код получил бы следующий вид:

<?php if( hypotheme_has_logo() ): ?>
    <a href="<?php echo esc_url( home_url() ); ?>" title="<?php bloginfo( 'name' ); ?>" class="custom-logo">
        <img src="<?php hypotheme_the_logo_url() ?>" />
    </a>
<?php endif; ?>

Теперь код гораздо проще читать, в нем нет лишнего беспорядка, и если кто-то захочет узнать, откуда берется логотип, он всегда может проинспектировать функцию. Логика отделена о представления.

Еще пример:

<?php
/**
 * page.php
 */
?>
<?php get_header(); ?>

<?php
$hypotheme_sidebar      = hypotheme_get_option( 'hypotheme_sidebar' );
$hypotheme_sidebar_size = hypotheme_get_option( 'hypotheme_sidebar_size' );
?>

<?php while ( have_posts() ) : the_post(); ?>

    <div class="row two-columns">

    <?php if ( $hypotheme_sidebar == 1 ): ?>
        <div class="main-column <?php if ( $hypotheme_sidebar_size == 0 ) { ?> col-md-6 <?php } else { ?> col-md-7 <?php } ?>">
    <?php else: ?>
        <div class="main-column col-md-12">
    <?php endif; ?>

    <div id="page-<?php the_ID(); ?>" <?php post_class( 'entry-page' ); ?>>
        <h1 class="entry-title"><?php the_title(); ?></h1>
        <div class="entry-content"><?php the_content(); ?></div>
    </div>

    <?php if ( comments_open() ) : ?>
        <?php comments_template(); ?>
    <?php endif; ?>

    </div>

    <?php if ( $hypotheme_sidebar == 1 ) {
        get_sidebar();
    } ?>

    </div>

<?php endwhile; ?>
<?php get_footer(); ?>

Посмотрите на эти переменные. Сами по себе они вполне адекватны – они делают то, что должны делать.

Однако этот шаблон, вероятно, далеко не единственный шаблон с сайдбаром в данной теме. Значит, эти переменные, скорее всего, присутствуют во всех шаблонах, в которых имеется сайдбар.

Логика смешивается с представлением, а также повторяется по всем шаблонам (page.php, single.php, index.php). Слишком много повторений кода, которых можно было бы избежать:


<?php
/**
 * page.php
 */
?>
<?php get_header(); ?>

<?php while ( have_posts() ) : the_post(); ?>

    <div class="row two-columns">

        <div class="main-column <?php echo hypotheme_container_width_class() ?>">
            <div id="page-<?php the_ID(); ?>" <?php post_class( 'entry-page' ); ?>>
                <h1 class="entry-title"><?php the_title(); ?></h1>
                <div class="entry-content"><?php the_content(); ?></div>
            </div>

            <?php if ( comments_open() ) : ?>
                <?php comments_template(); ?>
            <?php endif; ?>

        </div>

        <?php get_sidebar(); ?>

    </div>

<?php endwhile; ?>
<?php get_footer(); ?>

Данный код гораздо проще прочитать и понять. Функции позволяют сделать код более удобочитаемым и расширяемым, если их использовать в сочетании с хуками WordPress и подключаемыми функциями.

Всегда следите за разработкой WordPress

WordPress постоянно развивается, как и все остальное в сети. Будьте всегда в курсе лучших практик и применяйте их в своей разработке.

К примеру, я видел темы на WordPress.org в этом году, которые все еще используют wp_print_styles вместо wp_enqueue_scripts, хотя wp_print_styles устарел еще в версии WordPress 3.3.

Если вы создаете темы WordPress для других пользователей, то обязательно изучайте лучшие практики и периодически проверяйте код, чтобы узнать, действительно ли тот подход, который вы применяете, является самым эффективным.

Используйте родные WordPress функции, когда это возможно

Важно использовать родные WordPress функции тогда, когда это возможно, чтобы другие могли связываться с вашей WordPress-темой – вне зависимости от того, через плагин или через дочернюю тему.

Если вы знаете о последних трендах WordPress, то вы можете обнаружить, что код в ошибке под номером 4 может быть легко заменен на родную WordPress функцию – начиная с версии 4.5, WordPress поддерживает «из коробки» функциональность произвольного логотипа.

<?php if( has_custom_logo() ): ?>
    <a href="<?php echo esc_url( home_url() ); ?>" title="<?php bloginfo( 'name' ); ?>" class="custom-logo">
        <img src="<?php the_custom_logo() ?>" />
    </a>
<?php endif; ?>

Другой пример.

При разработке красивой навигации по записям я решил прибегнуть к функции get_next_post и вставил что-то подобное в свою тему:

<?php
$next_post = get_next_post();
if (!get_next_post()): ?>
  <a href="<?php echo esc_url( get_permalink( $next_post->ID ) ); ?>"><?php echo esc_attr( $next_post->post_title ); ?></a>
<?php endif; ?>

То, что мне нужно, думал я.

Что здесь не так? Несколько вещей.

Во-первых, не обращайтесь к свойствам объекта напрямую, если только вы не уверены, что они есть. Вместо этого вы можете использовать функцию get_the_title(). В итоге вы правильно получите заголовок, добавите «Private/Protected» и примените фильтр the_title:


// do this
echo get_the_title( $next_post )

// instead of this:
echo $next_post->post_title

Во-вторых, в WordPress есть функция next_post_link, и вы можете заменить все, что приведено выше, с помощью простого вызова:

<?php next_post_link() ?>

Опять же, следование лучшим практикам и изучение последних трендов позволяют заметно улучшить темы.

Не пишите свои собственные фреймворки

Когда я пишу код, я хочу, чтобы он отвечал принципам DRY, был чистым, реюзабельным и производительным. Я думаю, что мы все хотим того же самого.

Когда все эти амбиции сочетаются с бездумной оптимизацией, желанием предсказать будущее, игнорированием встроенных функций WordPress, а также экономией на нажатии клавиш, тогда появляется и желание сделать «фреймворк от себя для себя».

Как говорится, «благими намерениями вымощена дорога в ад». За свои пять лет разработки тем я создавал для себя «мощный фреймворк» как минимум дважды, и переписывал эти фреймворки бесчисленное количество раз. Не делайте так! Сэкономьте свое время!

Вот несколько проблем и побочных эффектов от создания фреймворка для себя:

Проблемы с обслуживанием

Первая проблема заключается в том, что создание «фреймворка» — это простое добавление дополнительной кодовой базы, которую нужно поддерживать. Если фреймворк находится в вашей директории /inc/me-framework, вам нужно обновлять все темы, использующие его, при обновлении самого фреймворка.

Если вы не станете обновлять темы, то у вас появятся дополнительные проблемы.

По мере вашего роста как разработчика ваши фреймворки будут тоже дорабатываться и улучшаться. Зачастую это приводит к несовместимости со старыми темами. Как быть, если вы найдете критическую ошибку в старых версиях фреймворка? Вам придется либо переписывать части всех тем, которые вы создали, либо выпускать специальный форк с исправлением бага. И опять же: появляется еще больше кода для поддержки и обслуживания.

Территория плагинов

Если вы добавляете «дополнительный функционал» к теме, то, скорее всего, вам нужно будет вынести его в плагин WordPress. Темы должны иметь красивую разметку и приятный стиль. Если вам нужно использовать PHP классы, то в таком случае вы, вероятно, ступите на территорию плагинов.

Создайте плагин, сделайте его легко настраиваемым и стилизуйте его в своей теме. Вы сможете избежать конструирования нового фреймворка и внесете свой вклад в open source сообщество!

Растущая сложность

Когда вы создаете фреймворк для себя, вы делаете свою тему более сложной в работе. Пользователям, изучающим код вашей темы, понадобится также исследовать и ваш фреймворк, который, скорее всего, будет плохо документирован или вообще не документирован.

Источник: https://www.smashingmagazine.com

Поделиться

2 комментария

  1. Спасибо, годная статья, узнал много нового.

  2. Макс says:

    Класс! ниче не понял поскольку только начинаю изучать код — но статья просто заворожила, я чувствую огромную мудрость и опыт автора (и это не лесть).

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.