Ранее мы изучали сохранение данных редактора блоков WordPress (Gutenberg) в атрибутах блоков и в метаданных записей, но знаете ли вы, что вы можете сохранять и извлекать из таблицы параметров WordPress, импортируя apiиз файлов @wordpress/api.
В этом руководстве мы рассмотрим, что вы обычно пишете на PHP как update_optionи get_option.
Чтобы реализовать это, нам нужно воспользоваться жизненным циклом React, поэтому мы рассмотрим создание компонента React путем импорта Componentиз @wordpress/element.
Предпосылки
- Быть знакомым с созданием плагинов для WordPress Gutenberg
- Будьте знакомы с динамическими блоками и рендерингом на стороне сервера
- Иметь представление о том, как создавать метабоксы в Гутенберге .
Это последнее требование полезно для пользовательского интерфейса, который мы собираемся использовать в этом руководстве, однако в реальных приложениях вполне вероятно, что вы будете использовать этот метод на боковой панели или странице параметров.
Зарегистрируйте параметры в PHP
Прежде чем мы сможем использовать параметр в JavaScript, мы должны убедиться, что мы зарегистрировали его в PHP с помощью register_settingи что для show_in_restаргумента установлено значение true.
Следуя руководству по динамическим блокам, откройте корневой PHP-файл плагина (в данном случае wholesome-plugin.php) и добавьте следующий код в конец этого файла после всех остальных функций:
function wholesomecode_wholesome_plugin_register_settings() {
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_text',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
}
add_action( 'init', 'wholesomecode_wholesome_plugin_register_settings' );
Этот код регистрирует метаполе, вызываемое wholesomecode_wholesome_plugin_block_textдля wholesomecode_wholesome_plugin_settingsгруппы опций. Это также гарантирует, что REST API может получить доступ к этому метаполю со show_in_restзначением true.
Создайте компонент
Откройте /src/edit.jsфайл, мы собираемся несколько изменить структуру этого файла, чтобы мы могли вывести наш файл Component.
Вырежьте и вставьте весь этот блок кода в /src/edit.jsфайл, мы рассмотрим, что он делает через мгновение:
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import {
Panel,
PanelBody,
TextControl,
} from '@wordpress/components';
import { Component } from '@wordpress/element';
import './editor.scss';
class OptionsExample extends Component {
constructor() {
super( ...arguments );
this.state = { exampleText: '' };
}
render() {
const { exampleText } = this.state;
return (<Panel>
<PanelBody
title={ __( 'Example Meta Box', 'wholesome-plugin') }
icon="admin-plugins"
>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => this.setState( { exampleText }) }
value={ exampleText }
/>
</PanelBody>
</Panel>) }
}
export default function Edit( props) {
return (<div { ...useBlockProps() }>
<OptionsExample { ...props }/>
</div>
);
}
Вы можете узнать, что пользовательский интерфейс, который мы внедрили, точно такой же, как в руководстве Gutenberg Meta Box, где мы использовали метаатрибуты записей. Вы также можете заметить, что мы пока не получаем и не устанавливаем информацию с помощью параметров, а вместо этого просто используем компонент state.
Использование состояния
Причина, по которой мы создали пользовательский компонент, а затем передали его в нашу Editфункцию, заключается в том, что мы можем использовать преимущества состояния. Мы сделали это, потому что:
- Мы создадим функцию для загрузки параметров из API, и нам нужно сохранить ее в состоянии, чтобы наши компоненты могли ее прочитать.
- Мы не хотим, чтобы API обновлял параметры, как только текст изменится в нашем текстовом поле, поэтому нам нужна функция для сохранения состояния в параметрах через API после сохранения сообщения.
Использовать состояние довольно просто. В конструкторе мы инициализируем состояние следующим образом:
this.state = { exampleText: '' };
И в компоненте мы обращаемся к нему аналогично тому, как мы обращались к атрибутам в предыдущем руководстве:
const { exampleText } = this.state;
Разница в том, что в нашем onChangeметоде вместо использования setAttributesмы используем this.setState.
Получение опций из API
В верхней части импорта документа apiиз @wordpress/api:
import api from '@wordpress/api';
Добавьте новое свойство туда, где инициализируется состояние isAPILoaded. Нам это понадобится, чтобы убедиться, что мы не пытаемся получить доступ к API или визуализировать компонент до загрузки API.
this.state = {
exampleText: '',
isAPILoaded: false,
};
Теперь внутри созданного нами Компонента добавьте блок кода в конструктор с именем componentDidMount. Это метод жизненного цикла React, который вызывается после добавления компонента в DOM.
В этот блок кода добавьте следующее:
componentDidMount() {
api.loadPromise.then(() => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
isAPILoaded: true,
} );
} );
}
} );
}
Здесь мы получаем доступ к опции, которую мы зарегистрировали ранее с помощью register_settingфункции.
Этот блок кода делает следующее:
- Получает настройки из API настроек WordPress Guttenberg.
- Получает
isAPILoadedот государства - Если API не был загружен, он извлекает настройки из API в
response - Затем мы устанавливаем состояние для обновления состояния с параметром, к которому мы хотим получить доступ, и устанавливаем для
isAPILoadedсостояния значение true .
Остановить рендеринг блока без настроек
Мы не хотим, чтобы наш блок отображался до заполнения настроек. Чтобы позаботиться об этом, мы можем импортировать PlaceHolder и Spinner из $wordpress/components:
import {
Panel,
PanelBody,
Placeholder,
Spinner,
TextControl,
} from '@wordpress/components';
Затем в методе рендеринга компонента убедитесь, что вы получаете isAPILoadedиз состояния, и выведите, Placeholderа Spinnerесли нет:
const {
exampleText,
isAPILoaded,
} = this.state;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
Таким образом, если параметры не загружены, мы получаем хороший заполнитель, пока компонент не загрузится:
Заполнитель и счетчик
Подключение к Гутенбергу при сохранении
Теперь, когда мы читаем параметры из таблицы параметров, нам нужен способ сохранить эти параметры при их изменении. Для этого мы идем в subscribeхранилище данных WordPress Gutenberg, которое укажет, когда что-то изменилось.
Используя это, мы создадим прослушиватель, когда сообщение будет сохранено, и сохраним наши настройки, когда это произойдет.
Для этого импортируйте subscribeи selectиз @wordpress/data.
import { select, subscribe } from '@wordpress/data';
Затем в верхней части componentDidMountблока кода напишите следующее:
subscribe(() => {
const { exampleText } = this.state;
const isSavingPost = select('core/editor').isSavingPost();
const isAutosavingPost = select('core/editor').isAutosavingPost();
if (isAutosavingPost) {
return;
}
if (! isSavingPost) {
return;
}
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
} );
settings.save();
});
Код делает следующее:
- Проверяет, сохраняется ли пост
- Проверьте, является ли сохранение автоматическим
- Если сообщение сохраняется и не является автосохранением, вставьте новые настройки в API настроек.
- Запустите сохранение API настроек.
Небольшой хак
Мы могли бы оставить наш код таким, но поскольку мы помещаем наши настройки в блок, а не в боковую панель или другой компонент редактора, если мы изменим одну из опций и ничего больше в редакторе, кнопка «сохранить» не сработает. стать активным.
Это потому, что мы не используем setAttributesничего для изменения фактического кода блока.
Мы можем обойти это, либо просто отредактировав другую часть поста, либо добавив небольшой хак в TextControlкод:
onChange={ (exampleText) => { this.setState( { exampleText } ); setAttributes( { exampleText }) } }
Не забудьте поместить эту строку кода в начало метода рендеринга для извлечения setAttributesиз props(поскольку мы используем компонент, доступ к свойствам которого немного отличается от this.
const { setAttributes } = this.props;
Теперь, когда мы изменим наш атрибут, «фальшивый» атрибут изменится, заставив редактора подумать, что теперь мы можем сохранить пост.
Это немного хакерски, но для этого варианта использования это делает то, что нам нужно.
Весь Editкод
Вот весь код, который вам нужен для Editметода:
я `импорт {} из ‘ @wordpress /i18n’; импортировать API из ‘ @wordpress /api’; импортировать {useBlockProps} из ‘ @wordpress /block-editor’; импортировать { Panel, PanelBody, Placeholder, Spinner, TextControl, } из ‘ @wordpress /components’; импортировать {выбрать, подписаться} из ‘ @wordpress /data’; импортировать {Компонент} из ‘ @wordpress /element’;
импортировать ‘./editor.scss’;
class OptionsExample extends Component { конструктор () { супер (…аргументы);
this.state = {
exampleText: '',
isAPILoaded: false,
};
}
componentDidMount() {
subscribe( () => {
const { exampleText } = this.state;
const isSavingPost = select('core/editor').isSavingPost();
const isAutosavingPost = select('core/editor').isAutosavingPost();
if (isAutosavingPost) {
return;
}
if (! isSavingPost) {
return;
}
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
} );
settings.save();
});
api.loadPromise.then( () => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
isAPILoaded: true,
} );
} );
}
} );
}
render() {
const {
exampleText,
isAPILoaded,
} = this.state;
const { setAttributes } = this.props;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
return (<Panel>
<PanelBody
title={ __( 'Example Meta Box', 'wholesomecode') }
icon="admin-plugins"
>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => { this.setState( { exampleText } ); setAttributes( { exampleText }) } }
value={ exampleText }
/>
</PanelBody>
</Panel>) }
}
функция экспорта по умолчанию Edit(реквизиты) { return (
); }
### Remove the Attributes
Option up `src/index.js` and remove the attributes block that we placed there in the previous guides. We are not storing any attributes, the data will be pushed into and retrieved from the options table.
Render the Output
Because we have saved our attribute as settings in the WordPress options table, we could output this anywhere in WordPress using `get_option`:
get_option(‘здоровый код_здорового_плагина_блок_текста’, ");
Continuing from the [Dynamic Block guide](https://wholesomecode.ltd/guides/php-render-block-wordpress-gutenberg/), let's see how we can access this attribute on the server side in PHP.
With this in mind, let’s update our `register_block_type` to output the option:
register_block_type(‘полезный код/полезный-плагин’, массив(‘editor_script’ => ‘полезный код-полезный-плагин-блок-редактор’, ‘editor_style’ => ‘полезный код-полезный-плагин-блок-редактор’, ‘render_callback’ = > function($attributes, $content) { $example_text = get_option(‘wholesomecode_wholesome_plugin_example_text’); return "
$example_text
"; }, ‘стиль’ => ‘полезный код-здоровый-плагин-блок’,) );
Note that we no longer need to register the `attributes` here, because we are only accessing the post meta field via the `get_post_meta` function.
5.
Using the Block
--------------------
Putting it all together, let’s see the block in action:
:
импорт {} из ‘ @wordpress /i18n’; импортировать API из ‘ @wordpress /api’; импортировать {useBlockProps} из ‘ @wordpress /block-editor’; импортировать { Panel, PanelBody, PanelRow, Placeholder, SelectControl, Spinner, TextControl, ToggleControl, } из ‘ @wordpress /components’; импортировать {выбрать, подписаться} из ‘ @wordpress /data’; импортировать {Компонент} из ‘ @wordpress /element’;
импортировать ‘./editor.scss’;
class OptionsExample extends Component { конструктор () { супер (…аргументы);
this.state = {
exampleSelect: '',
exampleText: '',
exampleText2: '',
exampleText3: '',
exampleToggle: false,
isAPILoaded: false,
};
}
componentDidMount() {
subscribe( () => {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
} = this.state;
const isSavingPost = select('core/editor').isSavingPost();
const isAutosavingPost = select('core/editor').isAutosavingPost();
if (isAutosavingPost) {
return;
}
if (! isSavingPost) {
return;
}
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_select' ]: exampleSelect,
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
[ 'wholesomecode_wholesome_plugin_example_text_2' ]: exampleText2,
[ 'wholesomecode_wholesome_plugin_example_text_3' ]: exampleText3,
[ 'wholesomecode_wholesome_plugin_example_toggle' ]: exampleToggle,
} );
settings.save();
});
api.loadPromise.then( () => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleSelect: response[ 'wholesomecode_wholesome_plugin_example_select' ],
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
exampleText2: response[ 'wholesomecode_wholesome_plugin_example_text_2' ],
exampleText3: response[ 'wholesomecode_wholesome_plugin_example_text_3' ],
exampleToggle: Boolean( response[ 'wholesomecode_wholesome_plugin_example_toggle' ] ),
isAPILoaded: true,
} );
} );
}
} );
}
render() {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
isAPILoaded,
} = this.state;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
return (<Panel>
<PanelBody
title={ __( 'Example Meta Box', 'wholesome-plugin') }
icon="admin-plugins"
>
<SelectControl
help={ __( 'An example dropdown field.', 'wholesome-plugin') }
label={ __( 'Example Select', 'wholesome-plugin') }
onChange={ (exampleSelect) => this.setState( { exampleSelect }) }
options={ [
{
label: __( 'Please Select...', 'wholesome-plugin' ),
value: '',
},
{
label: __( 'Option 1', 'wholesome-plugin' ),
value: 'option-1',
},
{
label: __( 'Option 2', 'wholesome-plugin' ),
value: 'option-2',
},
] }
value={ exampleSelect }
/>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => this.setState( { exampleText }) }
value={ exampleText }
/>
<PanelRow>
<TextControl
help={ __( 'Use PanelRow to place controls inline.', 'wholesome-plugin') }
label={ __( 'Example Text 2', 'wholesome-plugin') }
onChange={ (exampleText2) => this.setState( { exampleText2 }) }
value={ exampleText2 }
/>
<TextControl
help={ __( 'This control is inline.', 'wholesome-plugin') }
label={ __( 'Example Text 3', 'wholesome-plugin') }
onChange={ (exampleText3) => this.setState( { exampleText3 }) }
value={ exampleText3 }
/>
</PanelRow>
<ToggleControl
checked={ exampleToggle }
help={ __( 'An example toggle.', 'wholesome-plugin') }
label={ __( 'Example Toggle', 'wholesome-plugin') }
onChange={ (exampleToggle) => this.setState( { exampleToggle }) }
/>
</PanelBody>
</Panel>) }
}
функция экспорта по умолчанию Edit(реквизиты) { return (
); } `
Вот результат:
Дополнительные опции
- Посмотрите на создание вложенных дочерних блоков с
InnerBlocksкомпонентом - Взгляните на использование метаполей сообщений в блоках Гутенберга.
- Взгляните на создание пользовательских мета-боксов в Гутенберге .