WP DEV Section 26: Plugin: Featured Professor


WordPress

Updated Mar 14th, 2022

Starting Our Featured Professor Plugin

New Block Type. Preview: Feature or highlight a professor, Pulls current values from the teachers CPT. Learning how to create two-way relationship between two pieces of content.

Download starter.zip file associated with the lesson, extract and move into plugins folder and then open in VS Code.

<?php 

/*
  Plugin Name: The Best F*cking Plugin Yet
  Version: 1.0
  Author: Your Name Here
  Author URI: https://www.your-url.com/
*/

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if URL accessed directly

class FeaturedProfessor {
  function __construct() {
    add_action('init', [$this, 'onInit'])
  }

  function onInit() {
    wp_register_script('featuredProfessorScript', plugin_dir_url(__FILE__) . "", c)
    wp_register_style(a, b, c)

    register_block_type(a, b)
  }

  function renderCallback($attributes) {
    return '<p>We replace this content</p>'
  }

} // end class definition

$featuredProfessor = new FeaturedProfessor()

We don’t get into the scss file here

The index.js file is where we import the scss file and register the block type on the JS side of things

import "./index.scss"

wp.blocks.registerBlockType("ourplugin/featured-professor", {
  title: "",
  description: "",
  icon: "",
  category: "",
  edit: EditCOmponent,
  save: function() {
    return null
  }
})


// what you see in the admin area
function EditComponent(props) {
  return (
    <div className="featured-professor-wrapper">
      <div className="professor-select-container">
        We will have a select dropdown here
      </div>
      <div>
        The HTML preview of the selected professor will appear here
      </div>
     </div>
  )
}

Cd into the plugin directory, “npm init -y, npm install @wordpress/scripts.” Set up “package.json” file to add run build and start scripts.

"build": "wp-scripts build",
"start": "wp-scripts start"

Run start command and in the WP admin activate the plugin.

Build select dropdown element in index.js and in the JSX.

<select>
  <option value="1">Fake option 1</option>
</select>

Pots id value should be stored as attritbute for the block type in the “registerBlockType” function in index.js file.

attributes: {
  profId: {type: "string"}
},

An now in the JSx on the opening “select” we can add and onChange

<select onChange={e => props.setAttributes({profId: e.target.value})}>

Task: Once you select, if you refresh the page it pulls in the data.

// selected={true}

Loading a List of Professors

Task: Use client side JS to load a list of professors

We could send request to API endpoint, this would work but there is a new tool/way to load data. Test it out by typing into the console.

wp.data.select.("core").getEntityRecords("postType", "professor", {per_page: -1})

If we hit enter it looks like nothing happened. Hit enter again. WP already has the tools we need to load data without sending off our own HTTP request.

WP has even created React solution, spoiling us.

In the src/index.js file:

import {useSelect} from "@wordpress/core/data"

//down in Edit Component above return line

const allProfs = useSelect(select => {
  return select.("core").getEntityRecords("postType", "professor", {per_page: -1})
})

console.log(allProfs)

In the editor screen we want it to show “loading…” until content is fetched.

if(allProfs === undefined) {
  return <p>Loading...</p>
}

Create a mpa for the select option to loop through array of professor

{allProfs.map(prof => {
  return (
    <option value={prof.id} selected={props.attributes.profId == prof.id}>{prof.title.rendered}</option>
  )
})}

Save and test out. Selecting a choice, updating and refreshing should show your option selected/

Displaying Professor Info

Display content on front-end.

In featured-professor.php file, hollow iut renderCallback function

function renderCallback($attributes) {
  if ($attributes['profId']) {
    wp_enqueue_style('featuredProfessorStyle');
    return '<div class="professor-callout"</div>' 
  } else {
    return NULL;
  }
}

Code organization timeout to save the php file from becoming swamped with code. Want HTML template to live in it’s own separate file.

function renderCallback($attributes) {
  if ($attributes['profId']) {
    wp_enqueue_style('featuredProfessorStyle');
    return generateProfessorHTML($attributes['profId']) 
  } else {
    return NULL;
  }
}

Now we can create the function in a new file called “inc/generfateProfessorHTML.php”

<?php

function generateProfessorHTML() {}

IN the featured-professor.php file import the file PHP style

require_once plugin_dir_path(__FILE__) . "inc/generateProfessorHTML.php";

Now contents of new file will be available. And go back there to perform query:

<?php

function generateProfessorHTML($id) {
  $profPost = new WP_Query(array(
    '' => '',
    '' => ''
  ));

  while($profPost->have_posts()) {
    $profPost->the_post();
    ob_start(); ?>
    <div class="professorCallout">
      <div class="" style="background-image: url(<?php the_post_thumbnail_url('professorPortrait') ?>)"></div>
      <div class="professor-callout__text">
        <h5><?php the_title(); ?></h5>
        <p><?php echo wp_trim_words(get_the_content(), 30);?></p>

        <?php
        $relatedPrograms = get_field('related_programs')
        if($relatedPrograms) { ?>

          <p><?php the_title(); ?> Teaches:
          <?php foreach($realtedPrograms as $key => program) {
            echo get_the_title($program);
            if ($key != array_key_last($relatedPrograms) && count($relatedPrograms > 1)) {
             echo ', ';
            }
          } ?>.
          </p>
        <?php }
        ?>

        <p><a href="<?php the_permalink() ?>">Learn More About <?php the_title() ?></a></p>

      </div>
    </div>
    <?php
    wp_resest_postdata();
    return ob_get_clean();
  }

}

Note ob is output buffer

Save and checkout.

To display the photo we will use as background-image instead of inline img element.

Professor Preview in Editor (Part 1)

Set up HTML preview for professor within the edit screen using the same template from the previous lesson.

No less than 100 different ways to setup here.

don’t want to spell out template in both php and JSX. We do want server side.

Could look into frontity although you need to build your entire site around frontity. We will use php here.

We will create our own new totally custom Rest API Endpoint

Professor Preview in Editor (Part 2)

In featured-professor.php

add_action

new function profHTML

Pretty Cool

Need endpoint to be for 1 professor

In src/index.js import {useState, useEffect} from ‘react’ and apiFetch from “@wordpress/api-fetch.”

thepreview, setThePreview state

useEffect for props.attributes.profId changes

no axios, or fetch we can just use built in wp apifetch. returns promise.

use Async/Await inside useEffect with trick

built-in baseUrL the backticks for URL to hollow out

request updates state

render results in JSX using dangerouslySetUInnerHTML={{__html: thePreview}}

security talk, what if titled had malicious code. is and isnt a big deal. Admin user should not be malicious so we don’t bother escaping. although you could escape to be extra safe.

echo wp_esc_html(get_the_title()) or use echo wp_strip_all_tags

Control Post Meta With Block Type

Set up metadata.

Stiring only ID can be a one-way street

see post metadata in db

meta_key is name, meta_value is value

leverage useEffect existing to update meta

function updateTheMeta()

need to zoom out to think about block editor screen as a whole

wp.data.select(“core/block-editor”).getBlocks().filter()

returns array with block types but we just need id numbers so tag on a map function as well.

don’t store duplicate meta filter function. /brad says this code wasn’t intuitive and had to read multiple times.

.filter((x, index, arr) {
  return arr.indexOf(x) == index
)}

Need to use php to register new custom meta name, in f-prof.php find onInit function and add register_meta function

Set single to false. Setting to true can serialize data which slows down search.

Meta saved to DB!

two details:

First is deleting instance of block needs to be deleted from post meta. component unmounting. Create secind useEffct in index.js that has empty dependency array. Return a cleanup function to call update the meta.

Second: Inserting new block you want a blank instead of first option by wrapping in if statement

if () {}

Add Related Posts to Professor Detail Page

to the constructor function add_filter that runs new custom function addRelatedPosts.

Translations / Localization (For JavaScript)