Оптимизация плагина ACF
Безусловно, плагин Advanced Custom Fields (ACF) является лидером среди плагинов для добавления произвольных полей в WordPress.
У него тысячи контрибьюторов, которые находят и фиксят ошибки, занимаются оптимизацией производительности, но всегда есть, что подкрутить.
Суть данной оптимизации заключается в переносе инициализации полей из базы данных в РНР, скрытии GUI плагина, удалению ненужных записей в базе данных и отключении плагина во фронтенде.
Экспорт полей в РНР
Для начала экспортируем имеющиеся поля в виде PHP кода. Для чего переходим по пути Группы полей → Инструменты, отмечаем чекбокс Выбрать все и нажимаем кнопку Генерировать РНР.
Копируем полученный код и вставляем его в файл functions.php
вашей активной темы, используя рекомендуемый хук acf/init
:
add_action( 'acf/init', function() {
acf_add_local_field_group( array( ... ) );
} );
Отключение GUI в ACF
Так как все поля у нас теперь инициализируются через РНР, то управлять ими через админку нам более не требуется, поэтому отключим данный интерфейс при помощи хука acf/settings/show_admin
внутри файла functions.php
вашей активной темы:
add_filter( 'acf/settings/show_admin', '__return_false' );
Теперь и админка WordPress и сам сайт будут делать ровно в два раза меньше запросов в базу данных для получения полей.
ACF Builder
После экспорта полей в PHP и отключения ненужного нам более GUI можно подтянуть к себе в проект удобную обёртку ACF Builder, которая использует текучий интферфейс (fluent interface) для создания конфигурации полей прямо из кода.
Устанавливается она либо через composer:
composer require stoutlogic/acf-builder
Либо простым подключением файла autoload.php в ваш проект. Сначала нужно склонировать себе проект:
git clone git@github.com:StoutLogic/acf-builder.git
И после этого подключать:
require_once __DIR__ . '/acf-builder/autoload.php';
Использовать билдер достаточно просто:
use StoutLogic\AcfBuilder\FieldsBuilder;
$banner = new StoutLogic\AcfBuilder\FieldsBuilder('banner');
$banner
->addText('title')
->addWysiwyg('content')
->addImage('background_image')
->setLocation('post_type', '==', 'page')
->or('post_type', '==', 'post');
add_action('acf/init', function() use ($banner) {
acf_add_local_field_group($banner->build());
});
Более подробно смотрите в документации к ACF Builder.
Удаление лишних записей в базе данных
Внутренняя архитектура плагина ACF такова, что для хранения одного поля у одного поста используется две метазаписи в базе данных. Одна содержит информацию о самом поле, вторая — значение этого поля.
После переноса всех полей в само приложение информация о поле в базе данных нам больше не нужна, поэтому мы может ее удалить.
Перед началом работы с базой данных — сделайте её полный бекап.
Бекап делаем при помощи WP-CLI:
wp db export
После создания бекапа, открываем консоль MySQL, куда будем вводить SQL запросы:
wp db cli
Удаление полей у записей
Ищем поля, которые начинаяются на _field_
и удаляем их:
DELETE FROM wp_postmeta WHERE meta_key like '\_field\_%' LIMIT 100000;
Удаление полей у пользователей
Ищем поля, которые начинаяются на _field_
и удаляем их:
DELETE FROM wp_usermeta WHERE meta_key like '\_field\_%' LIMIT 100000;
Отключение плагина ACF во фронтенде
Перед отключением плагина убедитесь, что у вас нет сложных составных полей с повторителями, хотя и это ограничение можно обойти.
Копируем следующий код и вставляем его в файл _disable-acf-on-frontend.php
(подчеркивание в начале нужно, чтобы файл подключился в самом начале загрузки плагинов WordPress) в папке wp-content/mu-plugins
:
/**
* Удаляем плагин ACF во фронтенде
*
* @param array $plugins Массив всех плагинов
* @return array
*/
function mihdan_disable_acf_on_frontend( $plugins ) {
if ( is_admin() || wp_is_json_request() || wp_is_jsonp_request() ) {
return $plugins;
}
foreach( $plugins as $key => $plugin ) {
if ( 'advanced-custom-fields-pro/acf.php' === $plugin ) {
unset( $plugins[ $key ] );
}
}
return $plugins;
}
add_filter( 'option_active_plugins', 'mihdan_disable_acf_on_frontend' );
Теперь, чтобы вывести поля нужно использовать стандартные функции ядра get_post_meta
, get_term_meta
, get_user_meta
вместо функции get_filed
из набора ACF.
Чтиво
Комментарии приветствуются.
Отключение на фронте что конкретно дает в плане производительности?
Не тратится память на инициализацию плагина и его переменных (8-30 мб), нет множества запросов в базу.
Чтобы увидеть разницу — возьми сайт с плагином и сделай нагрузочный тест на 30к запросов, например.
Потом «выключи» плагин на фронте и повтори действия — разница будет заметна не вооруженным глазом.
Тут надо просто его глубоко попрофилировать через xdebug и сразу увидишь сколько памяти и на что тратит плагин на фронте.
Спасибо! Ваша статья по поводу «попрофилировать через xdebug и сразу увидишь сколько памяти и на что тратит плагин на фронте.» была бы очень всем полезна.
Спасибо, я учту ваши пожелания в будущих постах.
В коде ошибки
wp_is_json_request наверное wp_is_json_request()?
$i навероное $key?
Или это защита от копипасты? ))
Спасибо, исправил
Вариант с отключением плагина во фронте не срабатывает. Функция плагина остаются доступными и возвращают значения при обращении.
В примере сниппет для плагина ACF PRO
Да, я это заметил. Но моя ошибка была в другом. Я вставил фильтр в functions.php темы, но не как mu plugins. Значение active_plugins менялось после того, как плагины уже были прогружены. Спасибо!
Не за что, пользуйтесь!
В первом пункте вы написали:
используя рекомендуемый хук
acf/init
:объясните пожалуйста что это значит??? Если вы имели ввиду то, что нужно поставить сгенеррированный из ACF PHP код заместо «…» то происходит крах сайта.
Почему нельзя просто скопировать то, что сгенеррируется из ACF сразу в
functions.php
?ACF при экспорте полей в РНР не добавляет туда хук, а без него вы словите фатал, если ваш код запустится раньше инициализации самого плагина ACF (когда вы пишите свой плагин) или плагин ACF у вас будет деактивирован, например, если делаете отладку в WordPress
Спасибо за ответ!
Это действительно классно, когда разработчик отвечает, особенно если ответ в адекватный срок (не через год).
Приведите пожалуйста пример того, как нужно поставить PHP код из ACF используя хук acf/init — я не совсем понимаю то, как это нужно сделать.
Заранее спасибо. Добавил ваш сайт в закладки — отличная инфа!
Например, так:
Ещё пару вопросов:
1) А для чего удалять в базе — они же не будут использоваться. Просто что бы уменьшить размер базы?
2) «add_filter( ‘acf/settings/show_admin’, ‘__return_false’ );» вставляем после php кода от ACF или без разницы? Просто не происходит отключение интерфейса. Как слева стоит плагин, так всё и осталось.
3) Отключение плагина ACF во фронтенде — правильно ли я понимаю, что после отключения придётся дописывать код, что бы вывести поля? Т.е. Уже ранее работающие поля перестанут работать? Что даёт данный шаг и на сколько высока его необходимость?
1. Размер базы уменьшаяется ровно в два раза, это актуально на больших нагруженных сайтах. В простых блогах — это не актуально.
2. Этот код надо вставить в любое место
functions.php
или вашего плагина.3. Не тратится память на инициализацию плагина и его переменных (8-30 мб), нет множества запросов в базу. Чтобы увидеть разницу — возьмите сайт с плагином и сделайте нагрузочный тест на 30к запросов, например. Потом «выключите» плагин на фронте и повторите действия — разница будет заметна не вооруженным глазом. Тут надо просто его глубоко попрофилировать через xdebug и сразу увидидите сколько памяти и на что тратит плагин на фронте. Метод
the_field()
перестанет работать, но вместо него можно использовать стандартныеget_post_meta()
иget_term_meta()
.Можно пример вывода поля через get_post_meta() и get_term_meta()?
Вот допустим этих полей
<?php if($artikyl=get_field(«artikyl» )){echo $artikyl;} ?>
<?php if($foto_6=get_field(«foto_6»)){echo ‘<img src=»‘.$foto_6.'»/>’;}?>
В статье же есть примеры.
имеет ли смысл проводить данную оптимизацию для сайтов с количеством полей ~20 и посещением ~1000 в сутки?
Для таких показателей, скорее всего, и не нужно, но я бы рекомендовал сделать — хуже не будет.
хорошо. вот еще один вопрос, очень хотелось бы узнать, какой вариант делает меньше всего запросов к базе данных в цикле WP_Query
1 вариант:
2 вариант:
Само собой, вариант с get_post_meta быстрее.
Привет, отличная статья. А что вы посоветуете делать с полями типа Репитер? как это обойти грамотно?
Да также получать через get_post_meta, get_option.
в MU плагине еще можно добавить проверку на крон wp_doing_cron()
?
Не понял зачем переносить поля в php если можно скинуть их в json, бред же, тем более через пару обновлений структура может поменяться и фиг потом что сделаешь с этими полями
Функционала с JSON тогда еще не было ) И он был пару лет очень кривой и косой