<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Josh Pollock</title>
    <description>The latest articles on Forem by Josh Pollock (@shelob9).</description>
    <link>https://forem.com/shelob9</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F63421%2F1ce6f212-f8bc-417a-b61e-92a6743899dd.jpg</url>
      <title>Forem: Josh Pollock</title>
      <link>https://forem.com/shelob9</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/shelob9"/>
    <language>en</language>
    <item>
      <title>Parsing WordPress Block Data</title>
      <dc:creator>Josh Pollock</dc:creator>
      <pubDate>Sat, 27 Feb 2021 16:31:04 +0000</pubDate>
      <link>https://forem.com/shelob9/parsing-wordpress-block-data-29am</link>
      <guid>https://forem.com/shelob9/parsing-wordpress-block-data-29am</guid>
      <description>&lt;p&gt;Before WordPress 5.0, content for posts was stored as a string. It may or may not have contained HTML, line breaks and other formatting is meaningful, also shortcodes. WordPress 5.0 introduced a block based editor. It stores content as a string of HTML, with extra annotations as well as semantic HTML markup and attributes representing the data model. That HTML is parsed server-side -- removing extra annotations and replacing dynamic content -- before outputting HTML&lt;/p&gt;

&lt;p&gt;Some developers would prefer that the block attributes, including content were stored in separate, queryable columns in the database or were presented as an object. Maybe that would have been better -- I disagree later in this post -- but it's not how it works. More importantly, you can parse the blocks into a structured object with PHP and JavaScript. That's best of both worlds, we get the interoperability of using HTML to represent HTML, and developers can work with content as an object to modify the content or how it is rendered as HTML.&lt;/p&gt;

&lt;p&gt;I recently published a helpful tool for using the block parser in React apps. It helps when you want to replace WordPress' default block parsing with your own React components. In this article, I'll get to why I like WordPress, very imperfect approach and making it easier to modify with what I'm working on. I'll also look at how it compares to markdown-based content in Jamstack-style sites. I am a big fan of both approaches and this is not about debating one versus the other. Both are better, both maybe a better choice than the other, depending on the circumstances. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Shelob9" rel="noopener noreferrer"&gt;
        Shelob9
      &lt;/a&gt; / &lt;a href="https://github.com/Shelob9/block-content" rel="noopener noreferrer"&gt;
        block-content
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Renders "raw" post content with WordPress block markup in it using React components, which you optionally provide.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Block Content Renderer&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://github.com/Shelob9/block-content/actions/workflows/main.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Shelob9/block-content/actions/workflows/main.yml/badge.svg" alt="CI"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/886152ac88edd3e7ae70862baa52fd2f2c994c37c753c2bef011d2c7fa8825af/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f407368656c6f62392f626c6f636b2d636f6e74656e743f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/886152ac88edd3e7ae70862baa52fd2f2c994c37c753c2bef011d2c7fa8825af/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f407368656c6f62392f626c6f636b2d636f6e74656e743f7374796c653d666c61742d737175617265" alt="npm"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Renders "raw" post content with WordPress block markup in it using React components you optionally provide. Uses &lt;code&gt;@wordpress/block-serialization-default-parser&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This works with the "raw" value returned by WordPress REST API for post title, content, excerpt, etc. You must request with &lt;code&gt;?context=edit&lt;/code&gt; which requires permission to edit the post.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BETA&lt;/strong&gt; Probably don't use. An experiment by &lt;a href="https://joshpress.net" rel="nofollow noopener noreferrer"&gt;Josh Pollock&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Why / Status&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;WordPress parses block-based content to HTML before displaying it in a front-end theme. This HTML is also returned by the REST API and WPGraphQL. With a JavaScript front-end, in a headless site or what not, you may want to treat the block content as an object for several reasons.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the markup -- add classes to paragraphs, change element types, etc.&lt;/li&gt;
&lt;li&gt;Sanitize content&lt;/li&gt;
&lt;li&gt;Re-order or reformat content.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;WordPress' block parser converts blocks to objects. These objects have block attributes and the inner HTML. This library will…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Shelob9/block-content" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Content Can Be An Object In A String
&lt;/h2&gt;

&lt;p&gt;First, everything is a string in the database. A JSON column is a string with special annotations for translating it into an object. Relational databases like MySQL are great for putting it all back together. And if every block was its own row in a table, we could query for all blocks of a certain type. That and similar queries would make a GraphQL API for WordPress even cooler.&lt;/p&gt;

&lt;p&gt;It is common when developing a Gatsby site to have a directory of markdown files stored in the same git repo as the code. When Gatsby generates the site, it parses the markdown in those files to an abstract syntax tree and then uses that tree to render HTML. Generally MDX is used to provide React components for each HTML element. &lt;/p&gt;

&lt;p&gt;Gatsby provides APIs to hook in while that's happening so you can add business logic like "always add this class to paragraphs" or "make a fancy blockquote markup" or "insert an ad between sections."&lt;/p&gt;

&lt;p&gt;I'm over generalizing a lot here. But the point is that minimal markup is stored with the content. The markup is generated at build-time, by treating the string of markup as an object.&lt;/p&gt;

&lt;h2&gt;
  
  
  Back To WordPress
&lt;/h2&gt;

&lt;p&gt;When content is edited by the block editor, there is a lot of HTML markup in the_content field of the database. It's semantic HTML, making heavy use of comments and data attributes. The extra annotations, the Gutenberg grammar, are removed before sending the content to the browser in most settings. &lt;/p&gt;

&lt;p&gt;The WordPress REST API returns an object for post content. It contains one or more properties. It should always return a "rendered" property. That is the same HTML as we get in the front-end. If you have permission to edit posts and append ?context=edit you will have a "raw" property in the content object.&lt;/p&gt;

&lt;p&gt;That has the unparsed content. You can do the same thing WordPress does with it: use a PHP or JavaScript parser to convert it into an array of block objects and then walk that array to generate HTML.&lt;/p&gt;

&lt;p&gt;This article covered parsing with JavaScript. &lt;a href="https://wpscholar.com/blog/add-gutenberg-blocks-to-wp-rest-api/" rel="noopener noreferrer"&gt;Micah Wood wrote a good post on using the PHP parser and expose it on REST API endpoint&lt;/a&gt;. I also recommend &lt;a href="https://jasonyingling.me/how-wordpress-parses-dynamic-block-attributes/" rel="noopener noreferrer"&gt;this explanation of how dynamic block parsing works server-side by default&lt;/a&gt;. You may also want to look at &lt;a href="https://github.com/royboy789/gutenberg-object-plugin" rel="noopener noreferrer"&gt;Roy Sivan's Gutenberg Object Plugin&lt;/a&gt; which copies block data to a separate table, and exposes it on REST API endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;The rendered HTML returned by the REST API can be rendered with React, using dangerouslySetInnerHTML:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PostContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createMarkup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;createMarkup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This is not the best solution, because you are opening yourself up to XSS attacks by letting React evaluate raw HTML like that. What if you have React components you want to use for rendering the content, for consistency with the rest of the site?&lt;/p&gt;

&lt;p&gt;In these situations, using a block parser may be helpful. For example, you can parse out links and replace them with React components, for example Gatsby's Link component in place of links.&lt;/p&gt;
&lt;h2&gt;
  
  
  Customizing Block Rendering
&lt;/h2&gt;

&lt;p&gt;As I said earlier, I'm working on a helper for working with the parser in React apps for headless WordPress sites. WordPress always returns post content with a "rendered" property, which contains the pre-parsed HTML, if you request a post with &lt;code&gt;context=edit&lt;/code&gt; query param and have permission to edit, you also get back a "raw" property. That's what we're working with here.&lt;/p&gt;

&lt;p&gt;WordPress' block parser works with that raw string, like this: &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="nx"&gt;parse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@wordpress/block-serialization-default-parser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;!-- wp:paragraph --&amp;gt;
&amp;lt;p&amp;gt;Hi Roy&amp;lt;/p&amp;gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;That returns an object full of blocks, some of which have blocks inside them. I'm working on a utility that makes it easier to use this parser to render content using components supplied by the developer. &lt;/p&gt;

&lt;p&gt;This library includes a component called &lt;code&gt;BlockContent&lt;/code&gt;, which renders raw block content, by parsing the blocks, sanitizing the content and converting it to React elements. Remember, this request must be made by a user with permission to edit the post, and with the context query param set to edit.&lt;/p&gt;

&lt;p&gt;Here's an example of a Post component that uses it:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;BlockContent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@shelob9/block-content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BlockContent&lt;/span&gt; &lt;span class="na"&gt;rawContent&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;That's cool, but it doesn't help customize what React components are used to render the block content or to add business logic to the rendering. To do that, you need to set up a provider and supply it with components.&lt;/p&gt;

&lt;p&gt;Here is an example of the components you could use. In this example, all "a" elements in post content are replaced with Gatsby's link component and all "p" elements get a different class:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//replace links with Gatsby's link component.&lt;/span&gt;
  &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:({&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;In this example, we add an additional class name to all paragraphs:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;//Add a custom class to paragraphs&lt;/span&gt;
   &lt;span class="na"&gt;p&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; custom-class`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;There is no need to supply all elements. If, for example, no component for &lt;code&gt;p&lt;/code&gt; elements are provided, a generic one is generated.&lt;/p&gt;

&lt;p&gt;These components are passed to the &lt;code&gt;ThemeProvider&lt;/code&gt; component. That provider needs to go around all elements that use &lt;code&gt;BlockContent&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@shelob9/block-content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gatsby&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your/post/component&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your/components&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;//Mock data&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;!-- wp:paragraph --&amp;gt;
&amp;lt;p&amp;gt;Hi Roy&amp;lt;/p&amp;gt;
&amp;lt;!-- /wp:paragraph --&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;rendered&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;p&amp;gt;Hi Roy&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;//Wrap everything in the theme provider&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt; &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Try It And Let Me Know What You Think
&lt;/h3&gt;

&lt;p&gt;This is a new project. If you have a chance to use it, let me know what you think, in the comments or on &lt;a href="https://twitter.com/josh412" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. I &lt;a href="https://github.com/Shelob9/block-content/pull/1" rel="noopener noreferrer"&gt;will add more control over sanitizing content and attributes next&lt;/a&gt;, but would be super happy to know what you wish this could do.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

yarn add @Shelob9/block-content

npm &lt;span class="nb"&gt;install&lt;/span&gt; @Shelob9/block-content


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Shelob9" rel="noopener noreferrer"&gt;
        Shelob9
      &lt;/a&gt; / &lt;a href="https://github.com/Shelob9/block-content" rel="noopener noreferrer"&gt;
        block-content
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Renders "raw" post content with WordPress block markup in it using React components, which you optionally provide.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Block Content Renderer&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/Shelob9/block-content/actions/workflows/main.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/Shelob9/block-content/actions/workflows/main.yml/badge.svg" alt="CI"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/886152ac88edd3e7ae70862baa52fd2f2c994c37c753c2bef011d2c7fa8825af/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f407368656c6f62392f626c6f636b2d636f6e74656e743f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/886152ac88edd3e7ae70862baa52fd2f2c994c37c753c2bef011d2c7fa8825af/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f407368656c6f62392f626c6f636b2d636f6e74656e743f7374796c653d666c61742d737175617265" alt="npm"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Renders "raw" post content with WordPress block markup in it using React components you optionally provide. Uses &lt;code&gt;@wordpress/block-serialization-default-parser&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This works with the "raw" value returned by WordPress REST API for post title, content, excerpt, etc. You must request with &lt;code&gt;?context=edit&lt;/code&gt; which requires permission to edit the post.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BETA&lt;/strong&gt; Probably don't use. An experiment by &lt;a href="https://joshpress.net" rel="nofollow noopener noreferrer"&gt;Josh Pollock&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Why / Status&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;WordPress parses block-based content to HTML before displaying it in a front-end theme. This HTML is also returned by the REST API and WPGraphQL. With a JavaScript front-end, in a headless site or what not, you may want to treat the block content as an object for several reasons.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the markup -- add classes to paragraphs, change element types, etc.&lt;/li&gt;
&lt;li&gt;Sanitize content&lt;/li&gt;
&lt;li&gt;Re-order or reformat content.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;WordPress' block parser converts blocks to objects. These objects have block attributes and the inner HTML. This library will…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Shelob9/block-content" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  I Think This Is Good
&lt;/h2&gt;

&lt;p&gt;Yes, a table structure for block data would make it easier to do MySQL query based on blocks. I love to think about an alternate reality or possible future where blocks can be used as a graph database of some sort.&lt;/p&gt;

&lt;p&gt;In the strange world we do live in, post content is a string and I think that's good. With a table based system, the content -- what site owners care about -- you would need MySQL and PHP to convert that to HTML.&lt;/p&gt;

&lt;p&gt;Gutenberg markup in HTML makes parsing optional and can be done without PHP and MySQL. There are JS and PHP clients. Also, it's a spec you could implement in Go, because you're &lt;a href="https://chriswiegman.com/" rel="noopener noreferrer"&gt;Chris Wiegman&lt;/a&gt; or whatever.&lt;/p&gt;

&lt;p&gt;That's why I think this tradeoff makes sense. But, if querying against block attributes is a requirement, then those block attributes should be saved in post meta, so queries can be done based on those meta fields. I recommend this post by &lt;a href="https://helen.blog/2021/02/updating-a-wordpress-plugin-with-a-publish-metabox-field-for-the-block-editor/" rel="noopener noreferrer"&gt;Helen Hou-Sandí wrote about working with meta fields in the block editor&lt;/a&gt; if you want to learn more about how to do that.&lt;/p&gt;

&lt;p&gt;I know this may be a contrarian opinion, but using strings of HTML is not a bad way to represent content blocks. It is way more human readable and interoperable than JSON or storing in MySQL. With the parsers, available to use, when the rendered HTML doesn't fit our need, we can customize how the rendering works, to fit our needs.&lt;/p&gt;

&lt;p&gt;Sane defaults and plenty of ways to modify core behavior. Yes, it's a little messy, but it works and is very extensible when it needs to be. That's the vibe that makes WordPress so useful, right?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Featured Image by &lt;a href="https://unsplash.com/@joeriromer?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Joeri Römer&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/rocks?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>react</category>
      <category>php</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Josh Update: 2021</title>
      <dc:creator>Josh Pollock</dc:creator>
      <pubDate>Wed, 17 Feb 2021 00:55:14 +0000</pubDate>
      <link>https://forem.com/shelob9/josh-update-2021-2p3j</link>
      <guid>https://forem.com/shelob9/josh-update-2021-2p3j</guid>
      <description>&lt;p&gt;Last week I shared some Josh news on twitter that it &lt;a href="https://twitter.com/Josh412/status/1359895455589666820"&gt;was my last week at Saturday Drive&lt;/a&gt;. To answer a common question: I work at &lt;a href="https://10up.com"&gt;10up now&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving on From Saturday Drive
&lt;/h2&gt;

&lt;p&gt;In 2019, &lt;a href="https://wptavern.com/ninja-forms-parent-company-saturday-drive-acquires-calderawp"&gt;we sold Caldera Forms to Saturday Drive&lt;/a&gt;, the developers of Ninja Forms. I joined Saturday Drive with several other members of our totally distributed team. We joined as Saturday Drive was transitioning to being mostly distributed.&lt;/p&gt;

&lt;p&gt;I'm grateful to have been a part of the time and to have contributed to Ninja Forms, Caldera Forms, SendWP as well as internal applications. It's been a great opportunity and I've learned a ton about PHP, JavaScript, React, TypeScript and more while I've been there.&lt;/p&gt;

&lt;p&gt;While I was at Saturday Drive I contributed to Ninja Forms, including new blocks, new architecture for add-ons, and I helped diagnose and fix a ton of bugs. I also worked on internal applications for Ninja Forms and oversaw a migration of NinjaForms.com in advance of Black Friday. While I wasn't able to continue the vision I had for Caldera Forms, I was able to complete Caldera Forms MailChimp 2.0, which was a complete rewrite and  rewrote all of Caldera Forms' conditional logic editor in React to fix performance issues.&lt;/p&gt;

&lt;p&gt;Ultimately, this wasn't the right fit, I know I made an impact, but it was time. Earlier this year everyone agreed it was time for me to find something new. I super appreciate the support of the Kevin, James, Jeremy and the whole Saturday Drive team during this transition.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Work At 10up Now
&lt;/h2&gt;

&lt;p&gt;Starting this week, my job title is "Lead Web Engineer" at 10up. 10up is a WordPress VIP partner, and works with top brands and some very high traffic sites. I've been working with the WordPress for awhile, but mainly on plugins and WordPress adjacent apps. While I've built sites, and dealt with scaling challenges for ecommerce sites, this is a bit different.&lt;/p&gt;

&lt;p&gt;I'm really excited about a reset. It's a WordPress job, with a lot more WordPress development than my previous plugin development job. Moving to 10up is an opportunity to work on a team and use the skills I've developed over the last few years in new ways.&lt;/p&gt;

&lt;p&gt;The process of interviewing was fairly quick and relatively low stress. I'm still onboarding, but I've been super impressed so far with the friendliness of the people and the quality of the documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Also My Hair Got Long Again
&lt;/h2&gt;

&lt;p&gt;I am super grateful that working with WordPress allows Alicia and I to stay home and stay safe during this pandemic. It has been great to spend more time together and we got a puppy. The puppy news isn't recent and if you have spoken to me or follow me on social media, you probably have seen this, but anyway here is a picture of Macy playing in the snow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tIXdk5qD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cmmrujvaly4ggq2anlfg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tIXdk5qD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cmmrujvaly4ggq2anlfg.jpg" alt="Macy Playing In The Snow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oh shoot, this section was about me, not my dog. Sorry, that will happen. Anyway, my hair has gotten pretty long. It's been longer, but still I'm happy I had this opportunity to grow it out one last time before it's gone:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UoPnBfXN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/skeni0s190i2rq40sr2s.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UoPnBfXN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/skeni0s190i2rq40sr2s.jpg" alt="Me standing in front of a door, with longer hair"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm going to keep growing it, but happy so far:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CkjurrGy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h09jvrfx7g9h9572k9p5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CkjurrGy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h09jvrfx7g9h9572k9p5.jpg" alt="Me with long hair, not as pushed behind my ears as the previous photo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here is another photo of Macy playing in the snow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T0SyFGpq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qg31980x5sr2m6us4gt6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T0SyFGpq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qg31980x5sr2m6us4gt6.jpg" alt="Our dog Macy with snow in her beard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Long Distance Hugs And High Fives
&lt;/h2&gt;

&lt;p&gt;Given everything, I'm doing pretty good right now. If you are friends, Roy or family enough that you just read a whole post about me, thank you for being awesome. I love you and hope to see you again soon. Hugs. Hi fives.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using Docker For Local Development Of Headless WordPress Sites With NextJS</title>
      <dc:creator>Josh Pollock</dc:creator>
      <pubDate>Wed, 13 Jan 2021 23:38:13 +0000</pubDate>
      <link>https://forem.com/shelob9/using-docker-for-local-development-of-headless-wordpress-apps-with-nextjs-1m85</link>
      <guid>https://forem.com/shelob9/using-docker-for-local-development-of-headless-wordpress-apps-with-nextjs-1m85</guid>
      <description>&lt;p&gt;Back in 2016, I was &lt;a href="https://sandiego.wordcamp.org/2016/session/do-wordpress-better-with-the-wordpress-rest-api/"&gt;talking about adding how the WordPress REST API&lt;/a&gt; was the first step in &lt;a href="https://pittsburgh.wordcamp.org/2016/session/the-wordpress-rest-api-and-the-evolution-of-wordpress/"&gt;evolving WordPress to more of an application platform&lt;/a&gt;. Since then the WordPress post editor has been re-written in React. In addition to the WordPress REST API, there is now a GraphQL plugin for WordPress being developed with the goal of making it a part of WordPress core one day.&lt;/p&gt;

&lt;p&gt;Back then, we were not sure how to integrate React or Vue with WordPress sites. Today, NextJS and Gatsby exist and come with WordPress presets. I am a huge fan of NextJS, which includes static site generation, making it perfect for static content sites. I also love the zero config routing for front-end pages and API routes.&lt;/p&gt;

&lt;p&gt;NextJs's WordPress preset is everything you need to start a content site, with &lt;a href="https://www.wpgraphql.com/"&gt;WPGraphQL&lt;/a&gt;. That's cool, but it could also be the start point for a WordPress-powered application. &lt;/p&gt;

&lt;p&gt;Like most WordPress projects, the first step is choosing a local development environment for the WordPress site. There are a ton of options out there. I like to use Docker beacuse there is very little setup for WordPress, thanks to the &lt;a href="https://developer.wordpress.org/block-editor/packages/packages-env/"&gt;@wordpress/wp-env&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;With a little configuration, we can automate all of the setup that is needed. This makes onboarding new developers and QA easier. In this article I am going to show you how to set that up. I will assume you are familiar with WordPress and NextJS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create The Application
&lt;/h2&gt;

&lt;p&gt;First thing, we need a Next app. The WordPress preset is a great start point. It even includes Tailwind. Get started by generating the app using create-next-app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn create next-app &lt;span class="nt"&gt;--example&lt;/span&gt; cms-wordpress cms-wordpress-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That will create the application. Now we need a WordPress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding A Local WordPress Site
&lt;/h2&gt;

&lt;p&gt;The configuration for this site is going to very minimal. We need a site with a few plugins and we need to set one constant in wp-config.php of that site. &lt;a href="https://developer.wordpress.org/block-editor/packages/packages-env/"&gt;@wordpress/wp-env&lt;/a&gt; can do all of that. &lt;/p&gt;

&lt;p&gt;The WordPress site will need to have WPGraphQL installed. I like to add the &lt;a href="https://wordpress.org/plugins/headless-mode"&gt;Headless Mode&lt;/a&gt; plugin I made with some friends awhile ago. This will redirect all unauthenticated requests to the front-end of the WordPress site will be redirected to the client. &lt;/p&gt;

&lt;p&gt;First install the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add @wordpress/env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we will need to create a &lt;code&gt;.wp-env.json&lt;/code&gt; config file for the local Docker site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"https://downloads.wordpress.org/plugin/wp-graphql.zip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"https://downloads.wordpress.org/plugin/headless-mode.zip"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4113&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"HEADLESS_MODE_CLIENT_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:3000"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The  "plugins" section is being used to download the latest version of both of those plugins from WordPress.org. The "config" section sets one PHP constant. This is the URL for the redirect. You will need to set one constant in production to match your deployed URL for the client, as &lt;a href="https://wordpress.org/plugins/headless-mode/#%0Ahow%20do%20i%20set%20it%20up%3F%0A"&gt;documented here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To make my life easier, I added a "wordpress" command to the scripts section of package.json&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next start"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"wordpress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wp-env start"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Making Them Work Toghether
&lt;/h2&gt;

&lt;p&gt;Now, lets make sure the WordPress site is working. Try starting it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn wordpress
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes as expected that will download the plugins we need and start the site. When it says its done, if you go to &lt;a href="http://localhost:4113/"&gt;the front-end of the WordPress site&lt;/a&gt; it should redirect you to port 3000, which isn't' running and that is fine. We are not there yet.&lt;/p&gt;

&lt;p&gt;If you go to &lt;a href="http://localhost:4113/wp-login.php"&gt;the WordPress login page&lt;/a&gt; you should see that like normal. If you can login with username "admin" and password "password", then everything is good.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring NextJS To Work With WordPress
&lt;/h3&gt;

&lt;p&gt;Before starting the next app, we need to tell it what URL to source content from. &lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; and add this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;WORDPRESS_API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4113/graphql&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now start the front-end&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; yarn dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you should see the front-end at &lt;a href="http://localhost:3000/"&gt;http://localhost:3000/&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding A Plugin
&lt;/h3&gt;

&lt;p&gt;If you're going to add custom post types, or &lt;a href="https://www.wpgraphql.com/functions/register_graphql_mutation/"&gt;register new mutations&lt;/a&gt; or do anything else that requires custom PHP code, you will need to add a plugin or mu-plugin.&lt;/p&gt;

&lt;p&gt;I like having a plugin for my custom code, so I can turn it on and off. I added a directory called "wordpress-plugin" and put a file in there called "wordpress-plugin". Then I updated the "plugins" section of .wp-config.json to map that directory as a plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"./wordpress-plugin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"https://downloads.wordpress.org/plugin/wp-graphql.zip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"https://downloads.wordpress.org/plugin/headless-mode.zip"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4113&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"config"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"HEADLESS_MODE_CLIENT_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:3000"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out &lt;a href="https://developer.wordpress.org/block-editor/packages/packages-env/#add-mu-plugins-and-other-mapped-directories"&gt;this part of the documentation&lt;/a&gt; for more information on other ways to load plugins and mu-plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make Web Application Now
&lt;/h2&gt;

&lt;p&gt;That's all you need to set up a NextJS app using a local WordPress site running in Docker containers. You can start developing the PHP and JavaScript for your project. What you do next is up to you. Figuring our deployment and &lt;a href="https://github.com/wp-graphql/wp-graphql-jwt-authentication"&gt;authentication&lt;/a&gt; as well as coding the app will come next.&lt;/p&gt;

&lt;p&gt;If you push the repo to Github, you can deploy the front-end with Vercel with their (super simpler importer](&lt;a href="https://vercel.com/new"&gt;https://vercel.com/new&lt;/a&gt;). Make sure to set the &lt;code&gt;WORDPRESS_API_URL&lt;/code&gt; &lt;a href="https://vercel.com/docs/environment-variables"&gt;environment variable&lt;/a&gt; to match the URL of the live WordPress site. &lt;/p&gt;

&lt;p&gt;Feel free to fork the source for &lt;a href="https://github.com/Shelob9/next-wordpress-docker"&gt;the example application&lt;/a&gt; to build your own thing. I hope you enjoy.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Featured image by &lt;a href="https://unsplash.com/@rohitsunjaynavin?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Suresh Ramamoorthy&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/blue-skies?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>nextjs</category>
      <category>docker</category>
    </item>
    <item>
      <title>How To Use WordPress As An Authentication Provider For Laravel</title>
      <dc:creator>Josh Pollock</dc:creator>
      <pubDate>Sun, 03 Jan 2021 21:02:39 +0000</pubDate>
      <link>https://forem.com/shelob9/how-to-use-wordpress-as-an-authentication-provider-for-laravel-4gd3</link>
      <guid>https://forem.com/shelob9/how-to-use-wordpress-as-an-authentication-provider-for-laravel-4gd3</guid>
      <description>&lt;p&gt;Like a lot of PHP and JavaScript developers, I started web development by learning enough PHP and then jQuery to customize a WordPress theme and then got carried away from there. Along the way, I learned what WordPress is a good solution for and what it is not a great fit for. This has led to learning Angular, Vue, React and Laravel.&lt;/p&gt;

&lt;p&gt;Overtime, more and more of my work has been building WordPress-adjacent apps in Laravel and/ or Node. This has led me to a variety of solutions for sharing data and user authentication between the two. This article shows how to allow users of an existing WordPress site to login to a Laravel site with the same username and password. I did this without data duplication -- besides the cache -- or storing passwords anywhere.&lt;/p&gt;

&lt;p&gt;If your WordPress and Laravel applications are able to access the same database, there is an alternative solution, that I will discuss later. The solution I will share in this post, uses the WordPress REST API to check credentials and issue JWT tokens for users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up The WordPress Site
&lt;/h2&gt;

&lt;p&gt;This solution requires a WordPress site that has &lt;a href="https://wordpress.org/plugins/jwt-authentication-for-wp-rest-api/"&gt;the JWT authentication plugin&lt;/a&gt;. This is a free plugin you can install through wp-admin.&lt;/p&gt;

&lt;p&gt;Once that plugin is activated, make sure to set a long, random string in the constant &lt;code&gt;JWT_AUTH_SECRET_KEY&lt;/code&gt;. Also, ensure that your server does not block the HTTP &lt;code&gt;Authorization&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;This plugin does use username and password to issue the token. They are not stored, so this process is as relatively secure as long as it is run on HTTPS. &lt;/p&gt;

&lt;p&gt;When this plugin is installed, you can make a POST request to the "/jwt-auth/v1/token" endpoint with username and password in the body to get a JWT token. That token can then be set in the Authorization header to authorize requests to the WordPress REST API. If we supply that header when making a GET request to "/wp/v2/users/me" we will get details of the current user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wptavern.com/wordpress-5-6-to-introduce-application-passwords-for-rest-api-authentication"&gt;WordPress 5.6 will add an "application password"&lt;/a&gt; feature. This is a basic authentication system that uses a different password then the one for login. In the future, that might be a better solution then JWT tokens.&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://laravel.com/docs/8.x/authentication#adding-custom-user-providers"&gt;followed instructions&lt;/a&gt; on how to create a customer user provider. It takes the supplied username and password and attempts to get a JWT token from the WordPress site. If that works, a User model is created -- in memory and cache. The password is not saved. A standard Laravel session is created. That session will be maintained as long as the user remains in the cache or the user logs out.&lt;/p&gt;

&lt;p&gt;In this post, I will show you how it works. I also put the source for the app I built to test this &lt;a href="https://github.com/Shelob9/laravel-wordpress-user-provider"&gt;on the Githubs&lt;/a&gt;. Feel free to fork it. It's a basic Laravel 8 site with &lt;a href="https://github.com/laravel/breeze"&gt;Breeze&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating An API Client
&lt;/h2&gt;

&lt;p&gt;The first step in the Laravel app will be to create an API client. This client, which will make use of Laravel's HTTP facade will need to be able to make requests with and without the authentication token.&lt;/p&gt;

&lt;p&gt;This client will need methods for GET and POST requests, that optionally add the header. I also added a method for exchanging username and password for the token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http\Clients&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cd"&gt;/**
 * API client for WordPress REST API
 *
 * Must have the JWT auth plugin
 * https://wordpress.org/plugins/jwt-auth/
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WordPressApiClient&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$apiUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$apiUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;?string&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$apiUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;//In production require TLS on both ends.&lt;/span&gt;
        &lt;span class="c1"&gt;//Else do not verify.&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'verify'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'production'&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ENV'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;WordPressApiClient&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;hasToken&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;is_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;apiUrl&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$endpoint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;){&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;withToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;withOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nv"&gt;$query&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;apiUrl&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$endpoint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;){&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;withToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;withOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nv"&gt;$body&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;


    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/jwt-auth/v1/token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'username'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating A User Provider
&lt;/h2&gt;

&lt;p&gt;So far we have an HTTP client that could be used for a user provider. It could also be used to get or edit posts or other data from the WordPress site. For now, we'll look at the user provider.&lt;/p&gt;

&lt;p&gt;The documentation for &lt;a href="https://laravel.com/docs/8.x/authentication#adding-custom-user-providers"&gt;creating a custom user provider&lt;/a&gt; is pretty complete, and worth reading through.&lt;/p&gt;

&lt;p&gt;The most important method here is &lt;code&gt;retrieveById()&lt;/code&gt;. Returning a user model here sets the current logged in user. I wanted to avoid having to check the token against the WordPress REST API on every request.&lt;/p&gt;

&lt;p&gt;To avoid this, I created a factory for User models that caches the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;userFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAuthIdentifier&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
           &lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
           &lt;span class="mi"&gt;9000&lt;/span&gt;
       &lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the method that finds users by id, which uses that factory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;retrieveById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$identifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//Has user in cache?&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$identifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;//Yes? Make a user model and return it&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="p"&gt;){&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;forceFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;//No user? Do a login.&lt;/span&gt;
        &lt;span class="c1"&gt;//Could trigger a redirect to wp-logint that comes back with a JWT...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function, may not return a user model. This happens when there is no no logged in user. IF that happens, thes enext two methods will be called, in the order you see here. If the token check throws an exception, which the API client will do if it's invalid, that will trigger a login error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;retrieveByCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;wordpressClient&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;userFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validateCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Authenticatable&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;retrieveById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I did modify the User model to simplify it, and make it work for my needs. You don't have to use the User model, you can use any model. That might be useful if you wanted to have users in the Laravel database and users in the WordPress database. Here is my user model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Eloquent\Factories\HasFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Foundation\Auth\User&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;Authenticatable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Notifications\Notifiable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Authenticatable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;HasFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Notifiable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * The attributes that are mass assignable.
     *
     * @var array
     */&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$fillable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'token'&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also deleted the user migration. In my test site I used sqlite for the session and cache table. If I deployed this with Vapor, I would use DynamoDB and/ or Redis instead. That means that the Laravel application may not need a MySQL database, which would be great for scaling and cost-control.&lt;/p&gt;

&lt;p&gt;Here is the full user provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Providers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\DTO\UserResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Http\Clients\WordPressApiClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Contracts\Auth\Authenticatable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Contracts\Auth\UserProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Arr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Auth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WordPressUserProvider&lt;/span&gt;  &lt;span class="kd"&gt;implements&lt;/span&gt;  &lt;span class="nc"&gt;UserProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;WordPressApiClient&lt;/span&gt; &lt;span class="nv"&gt;$wordpressClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;wordpressClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WordPressApiClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WP_API_URL'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$identifier&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;__CLASS__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$identifier&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;retrieveById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$identifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//Has user in cache?&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$identifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;//Yes? Make a user model and return it&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="p"&gt;){&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;forceFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;//If nothing is returned here, login redirect will be triggered&lt;/span&gt;
        &lt;span class="c1"&gt;//That is normal for unauthorized users.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;retrieveByToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//Try to find by id&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;retrieveById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$identifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;//Not found? Exchange token for user details again.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="p"&gt;){&lt;/span&gt;
            &lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;
                &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;wordpressClient&lt;/span&gt;
                &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/wp-json/wp/v2/me'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;
                &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;userFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updateRememberToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Authenticatable&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//Not actually needed&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @param array $credentials
     * @return User|Authenticatable|null
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;retrieveByCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;wordpressClient&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;userFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;validateCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Authenticatable&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//Recheck token?&lt;/span&gt;
        &lt;span class="c1"&gt;//Auth::setUser($user);&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;
           &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;retrieveById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
           &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;userFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;Arr&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;only&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;,[&lt;/span&gt;
                &lt;span class="s1"&gt;'token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'ID'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'user_email'&lt;/span&gt;
            &lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toModel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAuthIdentifier&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
           &lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
           &lt;span class="mi"&gt;9000&lt;/span&gt;
       &lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;userFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;forceFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
              &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user_email'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
              &lt;span class="s1"&gt;'token'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'token'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getAuthIdentifier&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="mi"&gt;9000&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring Everything
&lt;/h2&gt;

&lt;p&gt;That's basically it. The last step is to wire things up. These steps are &lt;a href="https://laravel.com/docs/8.x/authentication#adding-custom-user-providers"&gt;straight out of the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First in the &lt;code&gt;AuthProvider&lt;/code&gt; service provider, register the user provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt; &lt;span class="nc"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wordpress'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WordPressUserProvider&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that is done, you can tell Laravel to use that provider by modifying &lt;code&gt;config/auth.php&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'providers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'users'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'driver'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'wordpress'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the user provider, I used the env varibale &lt;code&gt;WP_API_URL&lt;/code&gt; for the URL of the site's REST API. You would put something like &lt;code&gt;WP_API_URL=https://hiroy.club/wp-json&lt;/code&gt; in your .env file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As an alternative to the approach I showed in this post, you may want to &lt;a href="https://github.com/ahsankhatri/wordpress-auth-driver-laravel"&gt;look at this package&lt;/a&gt;. That package requires access to WordPress' MySQL database. That's not required by the approach I showed here.&lt;/p&gt;

&lt;p&gt;My organization's goal is to decouple our big WordPress site from our other internal applications, so co-location didn't make sense. Also, I want to use managed WordPress hosting providers for WordPress and Vapor for Laravel. Mixing them on the same server feels like a regression.&lt;/p&gt;

&lt;p&gt;I hope you found this useful. Please feel free to copy the example application that &lt;a href="https://github.com/Shelob9/laravel-wordpress-user-provider"&gt;is on Github&lt;/a&gt;. I am curious to see if anyone finds this useful and if you have better solutions. I know one thing I'm considering is using a redirect to the WordPress login screen.&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>I Love Writing JavaScript, But Livewire Is A Great Way To Avoid Writing JavaScript For Stupid Reasons</title>
      <dc:creator>Josh Pollock</dc:creator>
      <pubDate>Fri, 01 Jan 2021 00:38:41 +0000</pubDate>
      <link>https://forem.com/shelob9/i-love-writing-javascript-but-livewire-is-a-great-way-to-avoid-writing-javascript-for-stupid-reasons-53fo</link>
      <guid>https://forem.com/shelob9/i-love-writing-javascript-but-livewire-is-a-great-way-to-avoid-writing-javascript-for-stupid-reasons-53fo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I suppose it is tempting, if the only tool you have is a React, to treat everything as if it were JSX.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wiktionary.org/wiki/if_all_you_have_is_a_hammer,_everything_looks_like_a_nail"&gt;Roy Sivan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, I really love writing JavaScript. React plus TypeScript is some of my favorite code to write. One of the things I've done a lot of is build drag and drop user interface builders, React is amazing for that.&lt;/p&gt;

&lt;p&gt;A lot of the development I do right now for my job is internal applications to help development and support of &lt;a href="https://ninjaforms.com"&gt;Ninja Forms&lt;/a&gt;. I am mainly working with Laravel. The complexity in these projects is scaling databases and processing a ton of data. On the other hand, the UI requirements are very basic -- mainly HTML tables and some graphs.&lt;/p&gt;

&lt;p&gt;Initially, I was using Laravel as a headless API and for scheduled task processing. The user interface was a decoupled NextJS front-end. Starting a project with Next and Tailwind is a great way for me to get started. Also, it was totally overkill, and I had to figure out CORS and authentication. Sure, that is worth it in some cases, but I was building a UI for like 3 people to use.&lt;/p&gt;

&lt;p&gt;So this was a perfect opportunity to try out &lt;a href="https://laravel-livewire.com/"&gt;LiveWire&lt;/a&gt;. I was able to build dynamic components that switch between tables and graphs as well as handle pagination and live search. All with out page re-loads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Applications Are Difficult
&lt;/h2&gt;

&lt;p&gt;One of the problems I run into with my job, is I'm good at figuring things out. In general this is a good thing, but it also means I can get stuck in a rabbit hole making something stupid work.&lt;/p&gt;

&lt;p&gt;Livewire got me out of the "everything has to be an app" mindset. AJAX for pagination? That improves UX when searching. A full page refresh between screens that are for totally different data? Chill, it's fine.&lt;/p&gt;

&lt;p&gt;Like, do you ever think about the amount of work we do to show people a loading spinner while we reset the entire state of the web page, just to avoid reloading the web page? Chill.&lt;/p&gt;

&lt;p&gt;With Livewire I can create an SPA-like interface. For something basic, that we may or may not ever expand on, not having a lot of code, I think is a benefit.&lt;/p&gt;

&lt;p&gt;For this app, I created one &lt;a href="https://laravel-livewire.com/docs/2.x/making-components"&gt;Livewire component&lt;/a&gt; per page. This was a little awkward, as it meant in addition to the PHP class for the component, I had a blade file with the layout for the page, with a livewire component tag and I had a blade file for the component.&lt;/p&gt;

&lt;p&gt;The Laravel router returns a view that calls a blade file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'layouts.app'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nf"&gt;section&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'content'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"flex items-center"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"md:w-10/12 sm:w-full md:mx-auto"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

            &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"text-sm border border-t-8 rounded text-green-700 border-green-600 bg-green-100 px-3 py-4 mb-4"&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"alert"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;endif&lt;/span&gt;

            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"flex flex-col break-words bg-white border border-2 rounded shadow-md"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"font-semibold bg-gray-200 text-gray-700 py-3 px-6 mb-0"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="nc"&gt;Title&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;
                        &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"md:float-right"&lt;/span&gt;
                        &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nc"&gt;Back&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"w-full p-6"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"text-gray-700"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;livewire&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;endsection&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is too many files for me, but it does mean that only part of the page is a Livewire component. What's neat about it is that the rest of the page is just a normal blade file. There could be other Livewire components. Or other JavaScript can be used for other parts of the page.&lt;/p&gt;

&lt;p&gt;This blade file has to "wire" bindings, that's where the magic happens. The first on the input is &lt;code&gt;wire:model="url"&lt;/code&gt;. This binds the value of the &lt;a href="https://laravel-livewire.com/docs/2.x/properties"&gt;property&lt;/a&gt; of the component PHP class to this input. I just needed to add a public property called URL to make it work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http\Livewire&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Team&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Auth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Livewire\Component&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewSite&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'livewire.new-site'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;//Don't worry about this yet.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used a &lt;a href="https://laravel-news.com/php7-typed-properties"&gt;typed property&lt;/a&gt; to force the value to string. That can cause issues with using the value server-side, for example, in the create callback of this method. If the input is never changed, the value of &lt;code&gt;$this-&amp;gt;url&lt;/code&gt; will be null, which is not a string, and therefore there is an error.&lt;/p&gt;

&lt;p&gt;My solution is to set it the property to an empty string using the &lt;code&gt;mount()&lt;/code&gt; method of the class. That method, gets called before render, which renders the blade template on the server and sets up whatever magic keeps the client in sync with the server.&lt;/p&gt;

&lt;p&gt;If the variable does not need to be dynamically addressed in the client, you can also pass it to the view directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'livewire.new-site'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'site'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'https://hiroy.club'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I really like that there isn't a special view function for Livewire. It's the same old &lt;a href="https://laravel.com/docs/8.x/helpers#method-view"&gt;&lt;code&gt;view()&lt;/code&gt;&lt;/a&gt;. There is some special syntax in the Livewire blade templates, but for the most part, it is just &lt;a href="https://laravel.com/docs/8.x/blade"&gt;blade&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another use for the &lt;code&gt;mount()&lt;/code&gt; method I mentioned before, is to get URL parameters. For example, if this component was for the route &lt;code&gt;/sites/{id}&lt;/code&gt; I could get the site ID from the &lt;code&gt;mount()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$siteId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;siteId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anyway, back to the component for creating a site. We were discussing bindings and magic. The second binding is &lt;code&gt;wire:click="create"&lt;/code&gt;. This binds the click event of the button to the PHP class method &lt;code&gt;create&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That method has the value of the input, set in the property &lt;code&gt;$url&lt;/code&gt;, thanks to the first binding. I was able to use that to create the site, and associate it with the current user's team, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cd"&gt;/** @var Team $team */&lt;/span&gt;
        &lt;span class="nv"&gt;$team&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCurrentTeam&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$site&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$team&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sites&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/sites/%s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$site&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Full Page Livewire
&lt;/h2&gt;

&lt;p&gt;As I mentioned earlier, in the first project I used Livewire on, I did not use full page components. This approach allows for composing the page out of HTML, one or more Livewire components, and could include other frameworks.&lt;/p&gt;

&lt;p&gt;That project started as a Laravel 7 app, and was upgraded to version 8, and I never got &lt;a href="https://laravel-livewire.com/docs/2.x/rendering-components#page-components"&gt;full page components&lt;/a&gt; to work. When I started a second project, using &lt;a href="https://jetstream.laravel.com/1.x/stacks/livewire.html"&gt;Jetstream&lt;/a&gt; they worked as expected.&lt;/p&gt;

&lt;p&gt;That way there is only one blade file per page. Binding the component to the route, in web.php, is like binding an invokable controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'/sites/{id}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;App\Http\Livewire\Site&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an example of a full page component blade file, which would work with the same PHP class I showed in the previous section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;x-slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"font-semibold text-xl text-gray-800 leading-tight"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        {{ __('New Site') }}
    &lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/x-slot&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"py-12"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"max-w-7xl mx-auto sm:px-6 lg:px-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-white overflow-hidden shadow-xl sm:rounded-lg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    URL
                &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;wire:model=&lt;/span&gt;&lt;span class="s"&gt;"url"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;wire:click=&lt;/span&gt;&lt;span class="s"&gt;"create"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first part is the equivalent of a &lt;a href="https://vuejs.org/v2/guide/components-slots.html"&gt;VueJS slot&lt;/a&gt;. I like how Jetstream breaks the page up into sections like this. I will likely add a sidebar and footer slot next.&lt;/p&gt;

&lt;h2&gt;
  
  
  LiveWire FTW!
&lt;/h2&gt;

&lt;p&gt;Not every website needs to be a complete single page web application. Working with Livewire has reminded me that a traditional page navigation isn't such a bad thing. That page refresh prepares the user for a change, and it wipes out the unrelated state.&lt;/p&gt;

&lt;p&gt;Livewire, especially with Jetstream is a great tool for building Laravel UI quickly. That makes it a great fast prototyping tool that should force you to ask the question -- do I need to replace this with a full SPA? Probably not, Livewire is very good.&lt;/p&gt;

&lt;p&gt;This post is a brief intro to Livewire and why I think it is good. That said, I think I covered most of the important things you need to know. The &lt;a href="https://laravel-livewire.com/"&gt;docuemntation&lt;/a&gt; is quite good. If you &lt;a href="https://github.com/sponsors/calebporzio"&gt;sponsor the creator of the framework, Caleb Porzio on Github&lt;/a&gt;, you will get access to screencasts about using Livewire.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>javascript</category>
      <category>php</category>
    </item>
    <item>
      <title>Unit Testing Classes That Call Functions From WordPress Core</title>
      <dc:creator>Josh Pollock</dc:creator>
      <pubDate>Mon, 30 Nov 2020 15:59:04 +0000</pubDate>
      <link>https://forem.com/shelob9/unit-testing-classes-that-call-functions-form-wordpress-core-3h5p</link>
      <guid>https://forem.com/shelob9/unit-testing-classes-that-call-functions-form-wordpress-core-3h5p</guid>
      <description>&lt;p&gt;In general, I make the distinction between unit and integration testing based on dependencies. A unit test should test one "unit" of code only, just one class or function, with no external dependencies -- WordPress or MySQL.&lt;/p&gt;

&lt;p&gt;Unit testing with mocks can be very useful. It can also create false confidence beacuse it assures your code works when the dependencies work as you &lt;strong&gt;assume&lt;/strong&gt; they will. So don't forget to use integration tests.&lt;/p&gt;

&lt;p&gt;One way to make your code more testable is to inject dependent functionality into a class. Then you can mock those classes with a different class implementing the same interface, or use a mocking library such as &lt;a href="http://docs.mockery.io/en/latest/"&gt;Mockery&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That's not always possible, or not practical, if the functionality you need to mock out is a function from WordPress core. In those cases, &lt;a href="https://freek.dev/385-monkey-patching-in-php-with-patchwork"&gt;monkey patching&lt;/a&gt; is a better solution. "Monkey patching" is the process of swapping the definition of a plugin at runtime, this case during testing. &lt;/p&gt;

&lt;p&gt;Instead of testing with the actual function -- that would be an integration test -- we replace it. When we use a monkey patching library or a mocking library we can ensure that the code being tested calls the dependency, calls it with the right value and does the right things with the return values.&lt;/p&gt;

&lt;p&gt;There is a WordPress-specific tool called &lt;a href="https://brain-wp.github.io/BrainMonkey/docs/wordpress-why-bother.html"&gt;Brain Monkey&lt;/a&gt;, that can do this for us. In this post, I will show you how to do that. Both Mockery and Brain Monkey will be setup if you choose to add unit tests, when creating a WordPress plugin with &lt;a href="https://pluginmachine.com/blog/doc/php-testing/"&gt;Plugin Machine&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you need a good way to try these tests out, that's good place to start. Alternatively, you can &lt;a href="https://github.com/Shelob9/wordpress-plugin/blob/main/tests/Unit/EnvironmentTest.php"&gt;copy my example tests&lt;/a&gt; into your plugin.&lt;/p&gt;

&lt;p&gt;You just need to &lt;a href="https://brain-wp.github.io/BrainMonkey/docs/installation.html"&gt;install Brain Monkey&lt;/a&gt; first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require brain/monkey:2.&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Unit Testing That WordPress Hook Callbacks
&lt;/h2&gt;

&lt;p&gt;I often get asked how to test a class that calls &lt;code&gt;add_action()&lt;/code&gt; and &lt;code&gt;add_filter()&lt;/code&gt; in the constructor and then has callback functions in the same class. The best advice I can give you is don't do that.&lt;/p&gt;

&lt;p&gt;Instead, write classes, or even better functions for your callbacks that are totally de-coupled from the WordPress plugins API.&lt;/p&gt;

&lt;p&gt;For example, imagine a plugin that adds a call to action to every post. If I create a class that &lt;strong&gt;can&lt;/strong&gt; be used as a callback for the &lt;code&gt;the_content&lt;/code&gt; filter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ContentFilters&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt;  &lt;span class="nv"&gt;$cta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$cta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt;  &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$content&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;p&amp;gt;%s&amp;lt;/p&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cta&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a super basic example, but if the callback function for an action or filter does something, you should be able to test it by passing the arguments to the function and testing the side effects or return values. If there are a lot of side effects, that's probably better suited to an integration test.&lt;/p&gt;

&lt;p&gt;Because this is a class with one responsibility, I can test it's business logic without worrying about the integration with the WordPress plugins API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ContentFiltersTest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testAddCta&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="nv"&gt;$adder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ContentFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hi Roy'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertSame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Before&amp;lt;p&amp;gt;Hi Roy&amp;lt;/p&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$adder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Before'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Unit Testing WordPress Functions Called By Hooks
&lt;/h3&gt;

&lt;p&gt;In general structuring your code for easy testing, for example decoupling hooks from hook callbacks is a good practice. So that's an advantage of testing, it makes path of least resistance -- code that's easy to test -- the good path.&lt;/p&gt;

&lt;p&gt;But what about a function that sets up enqueuing scripts? That's a good example of something I test, just to prevent big mistakes that break everything. Here is an example of that kind of function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nf"&gt;add_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_enqueue_scripts'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'callback_for_registering_scripts'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;callback_for_registering_scripts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;wp_register_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;wp_localize_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can use Brain Monkey's &lt;a href="https://brain-wp.github.io/BrainMonkey/docs/functions-expect.html"&gt;&lt;code&gt;expect()&lt;/code&gt;&lt;/a&gt; function to ensure the functions in my callback get called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testRegisterAndEnqueue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;callback_for_registering_scripts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;Brain\Monkey\Functions\expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_register_script'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;Brain\Monkey\Functions\expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_localize_script'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could also use expect to make sure that &lt;code&gt;add_action()&lt;/code&gt; is called. But that would be a different test, which you could write with expect. There is also &lt;a href="https://github.com/10up/wp_mock"&gt;a specialized library for mocking WordPress hooks&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unit Testing Interactions With WordPress Core Functions
&lt;/h2&gt;

&lt;p&gt;So far, we've just tested that functions are called. That's nice, but often times we have logic following a core function call. That's what we need to test: our business logic. For example, let's say you have a class that creates a post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Storage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$title&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nv"&gt;$id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wp_insert_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'post_title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$title&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;is_numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;)){&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Could not create'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you were to unit test this, without monkey-patching &lt;code&gt;wp_insert_post()&lt;/code&gt;, you'd get an error. Our goal isn't to test &lt;code&gt;wp_insert_post&lt;/code&gt;. Our goal is to test the unique business logic of the plugin. In this case, that is the conditional and whether or not it throws an exception. &lt;/p&gt;

&lt;p&gt;We can do that with the &lt;a href="https://brain-wp.github.io/BrainMonkey/docs/functions-expect.html"&gt;&lt;code&gt;when()&lt;/code&gt; function&lt;/a&gt;. When we call &lt;code&gt;when()&lt;/code&gt;, we can chose what to return. I will test once where it returns an integer, which is what happens when a post is saved. I will test again and this time have it return an object. That test expects an exception to be thrown. &lt;/p&gt;

&lt;p&gt;These two tests cover what happens if things go right and what happens if they go wrong. As long as I've made accurate assumptions about what right and wrong are, this is good.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Brain\Monkey\Functions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestWhatever&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;YourTestCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testInsert&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="c1"&gt;//A fake wp_insert_post() that always returns 1&lt;/span&gt;
        &lt;span class="nf"&gt;Functions\when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_insert_post'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;justReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertIsNumeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello Royvan'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testInsertThrows&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="nf"&gt;Functions\when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp_insert_post'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;justReturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;stdClass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;expectException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Make Errors!'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  These Tests May Not Be Enough
&lt;/h2&gt;

&lt;p&gt;In this post, I've shown how to fake WordPress core functions during unit testing. Don't forget that these tests rely on assumptions. Make sure to confirm your assumptions with integration or acceptance tests. &lt;/p&gt;

&lt;p&gt;If you want to get more comfortable with this type of testing, I would suggest making a new plugin from &lt;a href="https://shelob9.github.io/wordpress-plugin/"&gt;my template&lt;/a&gt;, adding a new class that calls functions you may commonly use, like &lt;code&gt;get_post()&lt;/code&gt; or&lt;code&gt;update_option()&lt;/code&gt; and write unit tests for them. As you do that, keep in mind what assumptions you're making. If any of them make you uncomfortable, write integration tests instead :)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image: &lt;span&gt;Photo by &lt;a href="https://unsplash.com/@vorosbenisop?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Benjamin Voros&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/sky-moon?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>wordpress</category>
      <category>testing</category>
      <category>tdd</category>
    </item>
    <item>
      <title>I Made An App With Userbase And Have Written A Blog Post About It</title>
      <dc:creator>Josh Pollock</dc:creator>
      <pubDate>Sun, 22 Nov 2020 02:02:16 +0000</pubDate>
      <link>https://forem.com/shelob9/i-made-an-app-with-userbase-and-have-written-a-blog-post-about-it-jd8</link>
      <guid>https://forem.com/shelob9/i-made-an-app-with-userbase-and-have-written-a-blog-post-about-it-jd8</guid>
      <description>&lt;p&gt;This blog post is about &lt;a href="https://userbase.com/" rel="noopener noreferrer"&gt;Userbase&lt;/a&gt;. It's not a tutorial. I made one app with Userbase, these are some initial reactions. I'm impressed with how quickly I was able to get going once I figured out the very small API.&lt;/p&gt;

&lt;p&gt;The documentation for Userbase is very complete. The docs include an &lt;a href="https://userbase.com/docs/quickstart/" rel="noopener noreferrer"&gt;excellent tutorial&lt;/a&gt; that explains how it works by showing how to build a user login screen and some other things. &lt;/p&gt;

&lt;p&gt;I recommend reading through that and then making a &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;NextJS&lt;/a&gt;, with Tailwind and Userbase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn create next-app &lt;span class="nt"&gt;--example&lt;/span&gt; hi-roy next-userbase-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file-based routing in Next makes it my favorite React framework for building apps quickly. Not having to think through the router is one less thing to worry about when trying out a new thing. &lt;/p&gt;

&lt;p&gt;The starter I used included &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt;, which made me happy. I can make things look sort of good with Tailwind. &lt;/p&gt;

&lt;p&gt;Anyway, this post is about Userbase. I was curious if it fit that pattern of easy to set up, work with like the other tools I mentioned.&lt;/p&gt;

&lt;h2&gt;
  
  
  No Setup Database, End To End Encrypted? Nice.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://userbase.com/" rel="noopener noreferrer"&gt;Userbase&lt;/a&gt; is a product I'd been meaning to try for a bit. I built &lt;a href="https://imaginaryshapes.com/" rel="noopener noreferrer"&gt;an app&lt;/a&gt; earlier this year to try out &lt;a href="https://docs.blockstack.org/build-an-app" rel="noopener noreferrer"&gt;blockstack.js&lt;/a&gt;, which also offers end-to-end decryption and a no set up database. &lt;/p&gt;

&lt;p&gt;Some things that are super neato about both Usersbase and Blockstack are that you get a database backend with basically no set up. The database is very easy to get started with and is encrypted. This is great for privacy-first applications.&lt;/p&gt;

&lt;p&gt;That's a cool benefit.&lt;/p&gt;

&lt;p&gt;Userbase is more centralized than Blockstack. There is a paid service, which runs on AWS, or you can run your own instance, on AWS. Blockstack uses a blockchain and various cloud providers, and has a decentralized file system, as well as a wallet-based authentication. Having to set up a crypto wallet to login to an app isn't ideal, so I was interested in trying Userbase.&lt;/p&gt;

&lt;p&gt;While signing up for a Userbase is very typical. You provide your email address and a password. The big catch with Userbase is there is no way to do a password reset. To be honest, that does me a concern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blue Boxes
&lt;/h2&gt;

&lt;p&gt;I love graph paper. When I got stuck going to school, beacuse I was a child, one of my favorite things was the blue boxes on the graph paper. It's simple, and you can use it to make graphs or patterns. I also like to use graph paper for writing sometimes.&lt;/p&gt;

&lt;p&gt;Anyway, it's November 2020 and the sun is getting dark earlier now, and I've been trying to channel my anxiety with JavaScript, and I saw this Visa tweet:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1329093575649370113-432" src="https://platform.twitter.com/embed/Tweet.html?id=1329093575649370113"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1329093575649370113-432');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1329093575649370113&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;This was a nice way to stop doom scrolling, so I built the graph paper generator after work the other day. I like the idea of putting notes or links to tweets in boxes, so this felt like a good way to test out Userbase as a data store for this type of app.&lt;/p&gt;

&lt;p&gt;I'm starting to enjoy these puzzles to make a surface that is X by Y and has different things in each box. I used two loops, one for rows and one for adding boxes to the row. &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Here is the code for the generator&lt;/a&gt;. This isn't a tutorial, but it's probably helpful to know what the data looks like. Here are the TypeScript interfaces for the graph object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IBox&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;boxId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;rowId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;link&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;open&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IBoxes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;IBox&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IRow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IBoxes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;rowId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IRows&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;IRow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IGraph&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;graphId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IRows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was my original intent to store the entire object for a graph, with an array of rows, each one with an array of boxes in one "item" in the Userbase database.&lt;/p&gt;

&lt;p&gt;I got a helpful error telling me that the max size of any item in the database is 10kb. It does say that &lt;a href="https://userbase.com/docs/sdk/insert-item/" rel="noopener noreferrer"&gt;in the documentation&lt;/a&gt;. I missed that, I was tired and it's been a week, but I think we'll be OK and that the data in productivity and wellness apps should be private.&lt;/p&gt;

&lt;p&gt;The solution that worked for me was to create one database, identified by the graph's ID, per graph. All of the records in the database are boxes. The boxes know what row they are in, and there order in the row, so the graph can be re-assembled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Saving Boxes
&lt;/h3&gt;

&lt;p&gt;I kept the database name in a memo-ized value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let databaseName = useMemo(() =&amp;gt; `graph-${graphId}`, [graphId]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I was able to create a function for saving individual boxes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saveBox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IBox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;boxId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userbase&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="nx"&gt;databaseName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;itemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boxId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;boxId&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Given that I never created that database with a CLI or back-end or waited for it to provision and the code is that little, I'm super duper impressed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Boxes
&lt;/h3&gt;

&lt;p&gt;Originally I had a similar function to create one box. Then I looped through each row and created each box. I ended up getting rate-limited doing that. Which felt fair, &lt;a href="https://userbase.com/docs/sdk/put-transaction/" rel="noopener noreferrer"&gt;also userbase supports bulk operations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You are able to create, update or delete up to ten records at a time. I broke the boxes up into chunks of ten when they are created.&lt;/p&gt;

&lt;h2&gt;
  
  
  Userbase Is Very Cool
&lt;/h2&gt;

&lt;p&gt;I am not going to go on about how I built the application. You can &lt;a href="https://github.com/Shelob9/blue-boxes/" rel="noopener noreferrer"&gt;read the source&lt;/a&gt; here. &lt;/p&gt;

&lt;p&gt;Clearly, I think Userbase is very neato. There is a lot of promise here and they are working on SQL. I will keep my eye on Userbase. &lt;/p&gt;

&lt;p&gt;The big concern is that if a user looses there password, they can't reset it. That's a concern. Still, this is a great way to build an app quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Oh Yah, The Blue Boxes
&lt;/h2&gt;

&lt;p&gt;I do not identify as someone who can make a user interface, web blog or web application look good. I enjoy building user interface for other reasons, and assume someone with different skills will make it look right.&lt;/p&gt;

&lt;p&gt;So, I was pretty proud of myself that I made the blue boxes. The graph is an HTML table. The table, table body, table rows and table cells have the class &lt;code&gt;graph&lt;/code&gt;. The table cells, the blocks, also have the class &lt;code&gt;graph-box&lt;/code&gt;. This little bit of Tailwind CSS made it look right:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.graph&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;border-2&lt;/span&gt; &lt;span class="err"&gt;border-blue-500&lt;/span&gt; &lt;span class="err"&gt;border-opacity-50;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.graph-box&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an image of the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuwv1tpqm3x83yrjuodw9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuwv1tpqm3x83yrjuodw9.png" alt="Screenshot of application UI, showing the blue boxes of the graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Graphs Are Fun
&lt;/h2&gt;

&lt;p&gt;This is the end of the blog post. If you have not yet, check out &lt;a href="https://userbase.com/" rel="noopener noreferrer"&gt;Userbase&lt;/a&gt;. This is a very promising framework. I used it for user authentication and data storage. There is also built-in file uploads, data sharing and payments.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://github.com/shelob9/blue-boxes" rel="noopener noreferrer"&gt;check out the source code for the app I built&lt;/a&gt; or &lt;a href="https://blue-boxes.vercel.app/" rel="noopener noreferrer"&gt;try the app&lt;/a&gt;. The graph paper interface is fun, I'll probably build something else with it, feel free to put it to use in your own thing.&lt;/p&gt;

&lt;p&gt;Have fun. The blog post is now over.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover Photo by &lt;a href="https://unsplash.com/@whileimout?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Gabriel Santiago&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/moon-light?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>react</category>
      <category>blockchain</category>
      <category>userbase</category>
    </item>
    <item>
      <title>How To Use Docker For WordPress Development</title>
      <dc:creator>Josh Pollock</dc:creator>
      <pubDate>Thu, 07 May 2020 22:04:27 +0000</pubDate>
      <link>https://forem.com/shelob9/how-to-use-docker-for-wordpress-development-28c6</link>
      <guid>https://forem.com/shelob9/how-to-use-docker-for-wordpress-development-28c6</guid>
      <description>&lt;p&gt;When developing WordPress sites, apps, plugins and themes, it's always a good idea to work with a local test environment. When I started WordPressing seriously in 2014, the options were XAMP -- DesktopServer, MAMP, WAMP, etc. and Vagrant -- VVV, Homestead, etc. Later on Docker-based solutions appeared. For example, Local by Flywheel.&lt;/p&gt;

&lt;p&gt;This post is an introduction to using Docker for local WordPress development. It's not going to teach anything about using Docker in production or make you a Docker expert. My goal is to understand what Docker does and how to quickly get a local development envioronment up and running with the tools for this provided by the WordPress open source project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Docker
&lt;/h2&gt;

&lt;p&gt;XAMP like solutions install and configure PHP, MySQL and a web server -- apache, nGnix, etc -- &lt;strong&gt;on your computer&lt;/strong&gt; to work as WordPress site. On the other hand, Docker creates and runs a complete web server stack -- including the operating system and all tooling -- in a completley isolated "container" that runs the same whether your computer is Linux, Windows, or Mac.&lt;/p&gt;

&lt;p&gt;For teams of any size, the "works on my machine" problem will always be present. Using Docker allows us to explictily say "this is what the machine we develop with is." With Docker, everyone can choose there own computer, or possibly a cloud-hosted IDE and not worry about getting all of the parts of a web server set up the same way as there team mates every time.&lt;/p&gt;

&lt;p&gt;Docker is used to "containerized" our applications. A container is one application, totally encapsulated into one immutable entity. We simplify the creation of containers with pre-defiend templates called images. For example, if we want to use PHP in our app, we could use our Dockerfile to install PHP, and extensions and configure it. Or we may use a PHP image, or a PHP-fpm 7.4 image. &lt;/p&gt;

&lt;p&gt;This is a &lt;a href="https://deliciousbrains.com/vagrant-docker-wordpress-development/"&gt;good article comparing Docker and Vagrant&lt;/a&gt; from Delicious Brains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Introduction To Docker
&lt;/h2&gt;

&lt;p&gt;If you've ever followed a tutorial on setting up a server, you may recall beign given a set of instructions to follow, in the correct order to install all of the software via the command line. This always makes me nervous -- I'm worried about skipping a step or remembering to do it the same way every time.&lt;/p&gt;

&lt;p&gt;Docker files normally start with a "FROM" directive, which tells us what "image" -- a template defined in a different Dockerfile -- to start from. This is like starting with a fresh VPS with Ubuntu installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start with Ubuntu installed&lt;/span&gt;
FROM ubuntu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Dockerfile describes exactly how to make the server. The commands are similar. If we had a VPS, we might SSH in, and use apt-get to install some software, such as git. We use the RUN directive to do the same thing in our Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start with Ubuntu installed&lt;/span&gt;
FROM ubuntu

&lt;span class="c"&gt;# install git&lt;/span&gt;
RUN apt-get update &lt;span class="se"&gt;\&lt;/span&gt;
    apt-get upgrade &lt;span class="se"&gt;\&lt;/span&gt;
    apt-get &lt;span class="nb"&gt;install &lt;/span&gt;git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A Few Key Docker Concepts
&lt;/h3&gt;

&lt;p&gt;Docker is a technology that gets a lot of attention, beacuse it can do so much. It's a large ecosystem, I want to start with a few concepts that you should know. The goal here is to show what's important, and what can be ignored.&lt;/p&gt;

&lt;p&gt;Docker is a software that manages containers. Container is a general term, not specific to Docker. Containers are not run by the operating system directly, instead they use the operating system's kernal directly and run there own OS. This makes containers, effectively different computers running on the same processor.&lt;/p&gt;

&lt;p&gt;Docker, provides and API and CLI for creating and running containers both in production and on your computer, via &lt;a href="https://www.docker.com/products/docker-desktop"&gt;a desktop app&lt;/a&gt;. You should install the Docker desktop app for Mac, Windows or Linux.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Intermodal_container"&gt;Intermodal shipping containers&lt;/a&gt; come in a variety of standard sizes. They can be stacked in warehouses, on ships, placed on trains, or pulled behind a truck. What is in the container doesn't matter. A ship is built to carry X containers of size Y, what's inside those containers doesn't matter, as long as the dimensions, max weight and door specifications match.&lt;/p&gt;

&lt;p&gt;You can think of Docker, the software as the crane that moves the containers around. We can do this from the command line, but that doesn't scale well past a single container. We need php, MySQL, etc for our apps. The docker ecosystem has various container orchestration tools that you can think of as telling the crane how to assemble stacks of containers.&lt;/p&gt;

&lt;p&gt;There are several solutions we could use for orchestration. The simplest is docker-compose, which is optimized for local development. While docker-compose could be used in production, generally something like Kubernetes is a better choice in production.&lt;/p&gt;

&lt;p&gt;With docker-compose, we can define a collection of services that work toghether into one container, &lt;a href="https://docs.docker.com/compose/compose-file/"&gt;with a single yaml file&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Options For WordPress Local Development With Docker
&lt;/h2&gt;

&lt;p&gt;Until recently, to use docker-compose with WordPress, we needed to write and maintain a docker-compose.yml file. The documentation includes a great guide, with examples of &lt;a href="https://docs.docker.com/compose/wordpress/"&gt;how to write a docker-compose file for WordPress&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;WordPress now has two node packages that use docker-compose, and remove the need to write that file. These packages do what I have spent a lot of time doing for my projects -- scripting installation, package.json commands, etc. But I don't have to maintain it and the open source project has done a better job than I ever did.&lt;/p&gt;

&lt;p&gt;The rest of this post focuses on those options. Using your own docker-compose file is totally fine as well. I have also used &lt;a href="https://joshpress.net/create-a-wordpres-site-with-lando/"&gt;Lando&lt;/a&gt; and &lt;a href="https://localwp.com/"&gt;Local By Flywheel&lt;/a&gt;, both of which work well. &lt;/p&gt;

&lt;h3&gt;
  
  
  Options For Docker-based Local Development From WordPress Core
&lt;/h3&gt;

&lt;p&gt;The WordPress open-source project has published two tools to simplify docker-based local development. Both of them use nodejs to abstract away most of the complexity of using docker-compose for WordPress development.&lt;/p&gt;

&lt;p&gt;The first option &lt;a href="https://www.npmjs.com/package/@wordpress/env"&gt;@wordpress/env&lt;/a&gt; is the simplest option. It is useful for spinning up a quick test site with, some options. These options include installing plugins and switching WordPress versions.&lt;/p&gt;

&lt;p&gt;The other option is the env sub command of &lt;a href="https://www.npmjs.com/package/@wordpress/scripts#env"&gt;@wordpress/scripts&lt;/a&gt;. This command is similar to @wordpress/env. It can also has a phpunit test runner that runs WordPress integration tests, making it more useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using @wordpress-scripts for Local WordPress Development With Docker
&lt;/h2&gt;

&lt;p&gt;You may wish to review &lt;a href="https://git.saturdaydrive.io/_/engineering-ops/cactus"&gt;this sample plugin&lt;/a&gt; I created while working through this section.&lt;/p&gt;

&lt;p&gt;This section assumes you are already using @wordpress/scripts in your plugin for compiling JavaScript and CSS. If not install it first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add @wordpress/scripts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update Your package.json
&lt;/h3&gt;

&lt;p&gt;WordPress env requires you add a "wp-env" key to your package.json and define at least two values. These required options are plugin name and directory. Assuming your plugin's name is Catus&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"wp-env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"plugin-dir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cactus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"plugin-name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cactus"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need to add an "env" or similar command to your plugin's scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wp-scripts env"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using The Env Command
&lt;/h3&gt;

&lt;p&gt;To start and install the local docker-based development environemnt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn &lt;span class="nb"&gt;env &lt;/span&gt;start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first time this runs, the environment will be installed and started. In the furture it will re-start. You should see WordPress in your browser when you go to &lt;a href="http://localhost:8889"&gt;http://localhost:8889&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To stop this environment, go to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn &lt;span class="nb"&gt;env &lt;/span&gt;stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Featured image by &lt;a href="https://unsplash.com/@dorographie?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Dorothea OLDANI on Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>Developing WordPress Blocks Without WordPress</title>
      <dc:creator>Josh Pollock</dc:creator>
      <pubDate>Tue, 03 Mar 2020 22:23:42 +0000</pubDate>
      <link>https://forem.com/shelob9/developing-wordpress-blocks-without-wordpress-3lo6</link>
      <guid>https://forem.com/shelob9/developing-wordpress-blocks-without-wordpress-3lo6</guid>
      <description>&lt;p&gt;Now that WordPress has React-powered editor with code decoupled from WordPress, we do not need WordPress as a dependency when developing block plugins. This can speed up block development. In this article, I will show you how to use &lt;a href="https://codesandbox.io/"&gt;Codesandbox&lt;/a&gt; to develop blocks.&lt;/p&gt;

&lt;p&gt;The proposed block &lt;a href="https://make.wordpress.org/design/2019/06/07/block-library-mockups-prototype/"&gt;block library&lt;/a&gt; should allow for WordPress plugins with only JavaScript, CSS and a block.json file. The structure of this template is only 1 PHP file -- the assets.php file generated by &lt;a href="https://www.npmjs.com/package/@wordpress/scripts"&gt;wordpress-scripts&lt;/a&gt; -- away from being a complete plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Steps To Start Block Development
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/wordpress-block-u8k01"&gt;I created a CodeSandbox template to start block development&lt;/a&gt;. You can use this template to start developing your blocks. I started by cut and pasting &lt;a href="https://github.com/WordPress/gutenberg/tree/master/storybook/stories/playground"&gt;the code in the "playground" story of the Gutenberg repo's Storybook.&lt;/a&gt;. This minimal implimentation of Gutenberg has the setting sidebar and the main editor area. You can insert the block from this sandbox and the core blocks. The sidebar is on the top, I didn't add extra CSS for that. It's a little odd at first, but I think it will help me focus on settings and block seperately. I might change that.&lt;/p&gt;

&lt;p&gt;This is now my two step process for starting development of a block:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click the "New Sandbox" option and then choose to create from a template.&lt;/li&gt;
&lt;li&gt;Search templates for "WordPress Block" and choose my template.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LEMXNngH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/oqzbpj8i8arn3thfm2ou.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LEMXNngH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/oqzbpj8i8arn3thfm2ou.jpg" alt="The Codesanbox template search, with results for this template"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can then open the &lt;code&gt;src/block&lt;/code&gt; directory and start working on your block there. I added an block.json file, which sets block attributes, index.js that collects the object of settings you would pass to &lt;code&gt;registerBlockType&lt;/code&gt; in your plugin and a componet for the Edit callback, as well as it's tests. &lt;/p&gt;

&lt;p&gt;The block is registered for you in the Editor component.&lt;/p&gt;

&lt;p&gt;In the Editor component, I commented out this code, beacuse it can make things run very slow if you add a lot of blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;previewHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;previewHtml&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What that does is, every time blocks are updated, it converts them to HTML and echos it. That's cool and useful for testing. Also you could build a live preview with it. Have fun.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then What
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/docs/git"&gt;Codesandbox projects can be linked to a git repository&lt;/a&gt;. At some point you should start making commits. Once a block is ready, you can add a main plugin file and use the build command of &lt;a href="https://www.npmjs.com/package/@wordpress/scripts"&gt;wordpress-scripts&lt;/a&gt;. You have a WordPress plugin now!&lt;/p&gt;

&lt;h2&gt;
  
  
  Have Fun
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Follow Me On &lt;a href="https://dev.to/shelob9/"&gt;DEV&lt;/a&gt; and &lt;a href="https://twitter.com/Josh412"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Use this button to start your own blocks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/wordpress-block-u8k01?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pzpI10jk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://codesandbox.io/static/img/play-codesandbox.svg" alt="Edit WordPress Block"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>Typed Properties In PHP Are Neato</title>
      <dc:creator>Josh Pollock</dc:creator>
      <pubDate>Sat, 01 Feb 2020 23:17:15 +0000</pubDate>
      <link>https://forem.com/shelob9/typed-properties-in-php-are-neato-2aoh</link>
      <guid>https://forem.com/shelob9/typed-properties-in-php-are-neato-2aoh</guid>
      <description>&lt;p&gt;Until recently, for a PHP and JavaScript developer, avoiding bugs caused by variables being the wrong type was difficult, beacuse the langues are not strongly-typed. Thanks to PHP7 and TypeScript, we can opt-in to more type safety, which I like.&lt;/p&gt;

&lt;p&gt;PHP7 added scalar type hinting to function parameters, as well as return types. This makes the getter and setter pattern very useful. By protecting a property and a protected property, we can prevent a property from being set to the wrong type -- from outside of the object's scope. This pattern looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/** @var string */&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getFirstName&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setFirstName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$firstName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's cool, and while it's a lot less boilerplate than it would be in PHP5, it's still a pretty complex way to implement a string property on an object. This class has two public methods that need test coverage, which adds up. &lt;/p&gt;

&lt;p&gt;Having a property that acts public, but also is constrained by type, is nice. I think the popularity of Laravel backs up this opinion. And yes, &lt;a href="https://torquemag.io/2016/05/wordpress-developers-need-know-php-magic-methods/"&gt;I can use magic methods&lt;/a&gt; to make something like a Laravel model.&lt;/p&gt;

&lt;p&gt;Also, &lt;a href="https://laravel-news.com/php7-typed-properties"&gt;PHP 7.4 has typed properties&lt;/a&gt; and they are neato.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactoring PHP With Typed Properties
&lt;/h2&gt;

&lt;p&gt;So, I can now rewrite that class, with no methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to being less code, I don't need to write a test for this. However this class gets used in integration tests by other classes, my IDE and &lt;a href="https://github.com/phpstan/phpstan"&gt;a static analysis tool&lt;/a&gt; all prevent it from being used wrong.&lt;/p&gt;

&lt;p&gt;In the first example, nothing stopped me from setting &lt;code&gt;$firstName&lt;/code&gt; to an array from another method of this class. Now the program will not let me make that mistake. Machines are better at remembering stuff than me, so that's great.&lt;/p&gt;

&lt;h2&gt;
  
  
  But Wait. There Is A Catch!
&lt;/h2&gt;

&lt;p&gt;One catch though, Ok, technically, it's a potential error that you can catch...&lt;/p&gt;

&lt;p&gt;You can not read a property before it is set for the first time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;//This works&lt;/span&gt;
&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Roy'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;//This throws an error&lt;/span&gt;
&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This problem is also present in the older method. In both cases, a default value of the property could be set. Or I could just not use the property before setting. Depends on how the program is run. I don't like the possibility of the code being used wrong, beacuse lots of people I don't know run code I write.&lt;/p&gt;

&lt;p&gt;One solution is to hardcode the default value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Roy'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That keeps the code minimal, which is nice. I do think writing the class so that the property HAS to be set on initialization is a good option as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$firstName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>php</category>
    </item>
    <item>
      <title>How To Immutably Update An Array In TypeScript</title>
      <dc:creator>Josh Pollock</dc:creator>
      <pubDate>Fri, 31 Jan 2020 22:58:15 +0000</pubDate>
      <link>https://forem.com/shelob9/how-to-immutably-update-an-array-in-typescript-54nm</link>
      <guid>https://forem.com/shelob9/how-to-immutably-update-an-array-in-typescript-54nm</guid>
      <description>&lt;p&gt;Using an array to collect objects is useful. But, it presents some challenges with searching and updating the data. In React, updating an object in an array will not cause re-renders, adding more complication.&lt;/p&gt;

&lt;p&gt;This article does not aim to provide a great explanation of what immutability means in this context. This recent post from CSS tricks &lt;a href="https://css-tricks.com/understanding-immutability-in-javascript/"&gt;explains the concept of immutability in JavaScript&lt;/a&gt; quite well.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;An array is an object, and objects are React's state management tracks objects by reference.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;One&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Three&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Add Item&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Item&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make this work, we need a new array, not an updated array. Instead of updating the array -- a mutation -- we need to return a new array.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;I want to avoid clever abstractions on top of array searches and updates that I have. Sorting and searching large collections of objects can become a performance issue. Avoiding an abstraction or dependency helps a bit. So, basically I keep cutting and pasting the same code, so I figured if I put it on dev.to, I could find it via internet search. I'm glad that you may find it useful as well.&lt;/p&gt;

&lt;p&gt;These examples work with a collection of objects that use this type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use whatever type you want. The searches are based off of the id property. &lt;/p&gt;

&lt;h3&gt;
  
  
  Immutably Adding Or Adding An Item To An Array
&lt;/h3&gt;

&lt;p&gt;This function makes use of &lt;code&gt;Array.findIndex()&lt;/code&gt; to locate the index of the field being updated in the collection. If it's not present, the item is added to the array. If the item does is found, the existing items in the array are sliced into two -- the items before and the items after -- with the updated item placed in between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addOrUpdateField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;//Not found, add on end.&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;//found, so return:&lt;/span&gt;
  &lt;span class="c1"&gt;//Clone of items before item being update.&lt;/span&gt;
  &lt;span class="c1"&gt;//updated item&lt;/span&gt;
  &lt;span class="c1"&gt;//Clone of items after item being updated.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that instead of &lt;code&gt;Array.push()&lt;/code&gt;, I'm returning a new array, with the existing items spread in. I can prove that this is returning a different object, with this test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Adds fields immutably&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intitalFields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;addOrUpdateField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;addOrUpdateField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Three&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;intitalFields&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intitalFields&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is important to me that adding and removing items maintains order, which is why I used &lt;code&gt;Array.slice()&lt;/code&gt;. These tests prove adding and removing works, and maintains the order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Removes field, maintaining order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intitalFields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;addOrUpdateField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Three&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;One&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intitalFields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;removeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;intitalFields&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Adds a field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;addOrUpdateField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Three&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Adds a second field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intitalFields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;addOrUpdateField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intitalFields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;addOrUpdateField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Three&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;intitalFields&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Immutably Removing An Item From An Array
&lt;/h3&gt;

&lt;p&gt;Ok, one more thing while I'm here, though this could be it's own post: immutably removing item.&lt;/p&gt;

&lt;p&gt;This function also relies on &lt;code&gt;Array.findIndex()&lt;/code&gt;. If no item is found, the field collection is returned unmodified. If it is found, I use &lt;code&gt;Array.slice()&lt;/code&gt; to cut the array in two again: items before and items after. This time only those two pieces are returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;removeField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;fieldId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fieldId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;//Not found, return same reference.&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;//Return clone of items before and clone of items after.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can prove that fields are removed, and order is maintained, with this test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Removes field, maintaining order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intitalFields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;addOrUpdateField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Three&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;One&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intitalFields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;removeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;intitalFields&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;BTW I'm using the &lt;code&gt;addOrUpdateField&lt;/code&gt; function, which does make this an integration test, not a unit test. Also, I don't care. I like this kind of functional programming with arrays.&lt;/p&gt;

&lt;p&gt;I care that it works the way I want it to. So I care that it updates immutably when used how I'm actually going to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Removes field immutably&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intitalFields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;addOrUpdateField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Three&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;One&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;removeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;intitalFields&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intitalFields&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Testing Node's Event Emitter</title>
      <dc:creator>Josh Pollock</dc:creator>
      <pubDate>Fri, 24 Jan 2020 21:41:23 +0000</pubDate>
      <link>https://forem.com/shelob9/testing-event-emitter-38b5</link>
      <guid>https://forem.com/shelob9/testing-event-emitter-38b5</guid>
      <description>&lt;p&gt;Today at work, I implemented a basic event system using &lt;a href="https://nodejs.org/api/events.html#events_class_eventemitter"&gt;Node's EventEmitter&lt;/a&gt; class. I am developing a state management system to aid in migrating a part of &lt;a href="http://calderaforms.com/"&gt;a WordPress plugin&lt;/a&gt;'s admin interface from jQuery and Handlebars to React. &lt;/p&gt;

&lt;p&gt;Our immediate goal is to fix some performance issues related to storing state in the DOM. I'm developing this API decoupled from the main Git repo for the plugin so I can work faster. This repo has unit tests, and the main git repo for the plugin will have acceptance tests proving that the system works in the plugin.&lt;/p&gt;

&lt;p&gt;One type of test I'd never written before was to cover the events emitted with that event emitter. My logic is that if &lt;a href="https://testing-library.com/docs/guiding-principles"&gt;we're supposed to test our application like it is used&lt;/a&gt;, then we should test developer APIs like they are used. Use the comments to tell me any better approaches you may have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing The Event Emitter
&lt;/h2&gt;

&lt;p&gt;The plugin this is for is a form builder plugin. The code being tested manages the state of the forms in the form editor. In the test code below,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Emits remove field event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Mock the event callback to see that:&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// This is a factory function&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fieldState&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;//Bind jest's mock function to the event.&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;feildEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;removeField&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fieldId&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fieldId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;//Do the thing that triggers the event&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;//Did callback get called once?  &lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeCalledTimes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;//Did callback get called with the right data?&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBeCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I'm doing here is, instantiating my state management system, pretty much how it will actually get used, and then doing the thing that triggers the event and making sure the callback gets the right data. &lt;/p&gt;

&lt;h2&gt;
  
  
  It Could Be More Isolated
&lt;/h2&gt;

&lt;p&gt;So, yes, this is more of an integration test than a unit test. I could have invoked the event directly. But, I didn't add that event emitter so I'd have something cute to write about on the internet. I needed to do something with the data the callback passes.&lt;/p&gt;

&lt;p&gt;The test I wrote looks more like the actual code that is bound to the event I'm testing, than any unit test would look like. I don't care that an instance of &lt;code&gt;EventEmitter&lt;/code&gt; emits events, I care that it emits the right event, with the right data at the right time.&lt;/p&gt;

&lt;p&gt;I added the event emitter beacuse updating the field list wasn't updating the generated list of "magic tags" -- merge tags based on field values and other settings. So, really the most important tests here were showing that adding and removing fields change the total list of magic tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Adds field magic tag when adding a field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Start with 1 field&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cfEditorState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;intialFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="c1"&gt;//...&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// Has one magic tag&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAllMagicTags&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;//Add a field&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

     &lt;span class="c1"&gt;// Has two magic tags&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAllMagicTags&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test covers that the event was fired, with the right data, and had the right effect. I have tests with less scope than that, which were helpful to have on the journey to getting this test to pass. But, this one final test makes sure everything works together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typing Events
&lt;/h2&gt;

&lt;p&gt;This passed in my tests, but made compilation fail. The error I had was I was not extending the base event emitter. So when I exported the events object, TypeScript's compiler was not happy. A little googling later and I learned you can't export an internal in TypeScript. &lt;/p&gt;

&lt;p&gt;And like, whatever, I found a solution on &lt;a href="https://github.com/Morglod/tsee"&gt;Github called &lt;code&gt;tsee&lt;/code&gt;&lt;/a&gt; and it let's me type my events. That's really cool. &lt;/p&gt;

&lt;p&gt;The one thing I don't like about event-based architecture is if the code emitting the event changes, the callback can break. This can happen accidently, beacuse it's not always obvious what is bound to an event. Typeing the events should help.&lt;/p&gt;

&lt;p&gt;The one thing I don't like about event-based architecture is if the code emitting the event changes, the callback can break. This can happen accidently, beacuse it's not always obvious what is bound to an event. Typeing the events should help.&lt;/p&gt;

&lt;p&gt;Before I made the change I had:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;import EventEmitter from &lt;span class="s1"&gt;'events'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I updated that to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;import &lt;span class="o"&gt;{&lt;/span&gt; EventEmitter &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s1"&gt;'tsee'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I was able to type the event emitter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;const fieldEvents &lt;span class="o"&gt;=&lt;/span&gt; new EventEmitter&amp;lt;&lt;span class="o"&gt;{&lt;/span&gt;
    addField: &lt;span class="o"&gt;(&lt;/span&gt;fieldId: fieldId&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; void&lt;span class="p"&gt;;&lt;/span&gt;
    updateField: &lt;span class="o"&gt;(&lt;/span&gt;args: &lt;span class="o"&gt;{&lt;/span&gt; fieldId: fieldId&lt;span class="p"&gt;;&lt;/span&gt; beforeUpdate: field &lt;span class="o"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; void&lt;span class="p"&gt;;&lt;/span&gt;
    removeField: &lt;span class="o"&gt;(&lt;/span&gt;field: field&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; void&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&amp;gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A Fun Event
&lt;/h2&gt;

&lt;p&gt;Event-based architecture is useful. Instead of piling more responsiblities into a function or file, sometimes it's better to open it to modification from the outside world. But, it also means the function has side effects. &lt;/p&gt;

&lt;p&gt;Testing side effects is always difficult, in this post, I've shown one way to test that the data supplied to the callbacks is consistent. In this post you also learned how to use TypeScript to make it harder to make the kinds of mistakes this test prevents. Adding tests, and typing the events does not mean that they can't be consumed incorrectly, but it's a step in the right direction.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Featured image by &lt;a href="https://unsplash.com/@arizonanthony?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Anthony Cantin&lt;/a&gt; on &lt;a href="/s/photos/sky?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>node</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
