Använda alternativ för att lagra data i WordPress Block Editor (Gutenberg)
Vi har tidigare utforskat lagring av WordPress-blockredigeringsdata (Gutenberg) i blockattribut och i postmeta, men visste du att du kan lagra och hämta från i WordPress-alternativtabellen genom att importera apifrån @wordpress/api.
I den här guiden tar vi en titt på vad du klassiskt skulle skriva i PHP som update_optionoch get_option.
För att implementera detta måste vi dra nytta av Reacts livscykel, så vi kommer att titta på att skapa en React-komponent genom att importera Componentfrån @wordpress/element.
Förutsättningar
- Var bekant med att skapa plugins för WordPress Gutenberg
- Var bekant med dynamiska block och rendering på serversidan
- Ha förståelse för hur du kan skapa metaboxar i Gutenberg
Det sista kravet är användbart för det användargränssnitt som vi kommer att använda i den här guiden, men i verkliga applikationer är det troligt att du skulle använda den här metoden i en sidofält eller alternativsida.
Registrera alternativen i PHP
Innan vi kan använda ett alternativ i JavaScript måste vi se till att vi har registrerat det i PHP med hjälp av register_settingoch att show_in_restargumentet har satts till sant.
Följ upp guiden för Dynamic Block, öppna root-PHP-filen för plugin-programmet (i det här fallet wholesome-plugin.php) och lägg till följande kod längst ner i filen efter alla andra funktioner:
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' );
Denna kod registrerar ett metafält som kallas wholesomecode_wholesome_plugin_block_textför wholesomecode_wholesome_plugin_settingsalternativgruppen. Det säkerställer också att REST API kan komma åt detta metafält med show_in_restvärdet satt till sant.
Skapa komponenten
Öppna /src/edit.jsfilen, vi kommer att ändra strukturen på den här filen något så att vi kan mata ut vår Component.
Klipp ut och klistra in hela detta kodblock i /src/edit.jsfilen, vi kommer att täcka vad det gör på ett ögonblick:
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>
);
}
Du kanske känner igen att användargränssnittet vi har infört är exakt samma från Gutenberg Meta Box -guiden, där vi använde post-metaattribut. Du kanske också märker att vi inte får eller ställer in information med alternativ ännu, utan istället använder vi bara komponenten state.
Använder staten
Anledningen till att vi har skapat en anpassad komponent och sedan överfört den till vår Editfunktion är så att vi kan dra fördel av staten. Vi har gjort detta för att:
- Vi kommer att skapa en funktion för att ladda alternativen från API:t, och vi måste lagra detta i tillstånd så att våra komponenter kan läsa det
- Vi vill inte att API uppdaterar alternativen så fort texten ändras i vår textruta, så vi behöver en funktion för att spara tillståndet i alternativen via API när inlägget har sparats
Att använda tillstånd är ganska enkelt. I konstruktorn initierar vi tillståndet så här:
this.state = { exampleText: '' };
Och i komponenten kommer vi åt den på samma sätt som vi har fått åtkomst till attribut i den tidigare guiden:
const { exampleText } = this.state;
Skillnaden är att på vår onChangemetod använder setAttributesvi istället för att använda this.setState.
Få alternativ från API
Överst i dokumentet importera apifrån @wordpress/api:
import api from '@wordpress/api';
Lägg till en ny egenskap där tillståndet initieras av isAPILoaded. Vi kommer att behöva detta för att se till att vi inte försöker komma åt API:et eller rendera komponenten innan API:et har laddats.
this.state = {
exampleText: '',
isAPILoaded: false,
};
Nu i komponenten vi skapade, lägg till ett kodblock under konstruktorn som heter componentDidMount. Detta är en React-livscykelmetod, som anropas efter att en komponent har lagts till i DOM.
Lägg till följande i kodblocket:
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,
} );
} );
}
} );
}
Här kommer vi åt alternativet vi registrerade tidigare med register_settingfunktionen.
Detta kodblock gör följande:
- Hämtar inställningarna från WordPress Guttenberg Settings API.
- Får
isAPILoadedfrån saten - Om API:et inte har laddats hämtar det inställningarna från API:et i en
response - Vi ställer sedan in tillståndet för att uppdatera tillståndet med alternativet vi vill komma åt och ställer in
isAPILoadedtillståndet till sant
Stoppa blockåtergivningen utan inställningar
Vi vill inte att vårt block ska renderas innan inställningarna har fyllts i. För att ta hand om detta kan vi importera en platshållare och en spinnare från $wordpress/components:
import {
Panel,
PanelBody,
Placeholder,
Spinner,
TextControl,
} from '@wordpress/components';
Sedan i komponentrenderingsmetoden, se till att du hämtar isAPILoadedfrån tillståndet och matar ut Placeholderoch Spinnerom det inte har:
const {
exampleText,
isAPILoaded,
} = this.state;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
På detta sätt, om alternativen inte har laddats, får vi en bra platshållare tills komponenten laddas:
Platshållare och Spinner
Hakar på Gutenberg på Spara
Nu när vi läser alternativ från alternativtabellen behöver vi ett sätt att spara dessa alternativ när vi ändrar dem. För att göra detta går vi subscribetill WordPress Gutenbergs datalager, som kommer att indikera när något har förändrats.
Med detta skapar vi en lyssnare för när inlägget sparas och sparar våra inställningar när det händer.
För att göra detta importera subscribeoch selectfrån @wordpress/data.
import { select, subscribe } from '@wordpress/data';
Skriv sedan följande överst i componentDidMountkodblocket:
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();
});
Koden gör följande:
- Kontrollerar om inlägget sparas
- Kontrollera om lagringen är en automatisk lagring
- Om inlägget sparas och det inte är en autospara, tryck in de nya inställningarna i Settings API
- Utlösa en lagring av Settings API.
Ett litet hack
Vi skulle kunna lämna vår kod så här, men eftersom vi lägger in våra inställningar i ett block, och inte i en sidofält eller annan redigeringskomponent, om vi ändrar ett av alternativen, och inget annat i redigeraren, fungerar inte "spara"-knappen bli aktiv.
Detta beror på att vi inte använder setAttributeseller något för att ändra den faktiska koden för blocket.
Vi kan komma runt detta genom att antingen, bara redigera en annan del av inlägget, eller genom att lägga till ett litet hack i TextControlkoden:
onChange={ (exampleText) => { this.setState( { exampleText } ); setAttributes( { exampleText }) } }
Kom ihåg att sätta denna kodrad överst i renderingsmetoden för att extrahera setAttributesfrån props(eftersom vi använder en komponent kommer vi åt rekvisita något annorlunda med this.
const { setAttributes } = this.props;
Nu när vi ändrar vårt attribut kommer ett "falskt" attribut att ändras, vilket gör att redaktören tror att vi nu kan spara inlägget.
Det är lite hackigt, men för det här användningsfallet gör det vad vi behöver.
Hela Editkoden
Här är all kod du behöver för Editmetoden:
i ` import {} från ’ @wordpress /i18n’; importera api från ’ @wordpress /api’; importera { useBlockProps } från ’ @wordpress /block-editor’; importera { Panel, PanelBody, Placeholder, Spinner, TextControl, } från ’ @wordpress /components’; importera { välj, prenumerera } från ’ @wordpress /data’; importera { Komponent } från ’ @wordpress /element’;
importera ’./editor.scss’;
class OptionsExample utökar Component { constructor() { super( …arguments );
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>) }
}
export standardfunktion Redigera( rekvisita) { 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( ’wholesomecode_wholesome_plugin_block_text’, " );
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( ’wholesomecode/wholesome-plugin’, array( ’editor_script’ => ’wholesomecode-wholesome-plugin-block-editor’, ’editor_style’ => ’wholesomecode-wholesome-plugin-block-editor’, ’render_callback’ = > function( $attributes, $content) { $example_text = get_option( ’wholesomecode_wholesome_plugin_example_text’); return "
$example_text
"; }, ’style’ => ’hälsokod-hälsosam-plugin-block’,) );
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:
:
importera {} från ’ @wordpress /i18n’; importera api från ’ @wordpress /api’; importera { useBlockProps } från ’ @wordpress /block-editor’; importera { Panel, PanelBody, PanelRow, Platshållare, SelectControl, Spinner, TextControl, ToggleControl, } från ’ @wordpress /components’; importera { välj, prenumerera } från ’ @wordpress /data’; importera { Komponent } från ’ @wordpress /element’;
importera ’./editor.scss’;
class OptionsExample utökar Component { constructor() { super( …arguments );
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>) }
}
export standardfunktion Redigera( rekvisita) { return (
); } `
Här är resultatet:
Extra alternativ
- Ta en titt på att skapa kapslade underordnade block med
InnerBlockskomponenten - Ta en titt på hur du använder post-metafält i Gutenberg-block
- Ta en titt på att skapa anpassade metaboxar i Gutenberg