Category: Uncategorized

  • END OF WEEK 4

    • Caching in WordPress
    • Select media modal using wp.media

    Caching in WordPress

    If there is query in our code that is taking long to load and hampering the performance of our site, we can incorporate caching.

    We will cache the results instead of firing the query every time which will make our site load faster.

    In our project, to render shortcodes of movie and person according to the attributes we are using meta queries and tax queries.

    There are ways to optimize this queries which will not be covering here, we are focusing on using caching only.

    WordPress provides following methods in class WP_Object_Cache {} for caching,

    We can use output buffer in php, to get the output being put on the site like the movie details and then store that output in our cache

    // ...your code to get posts and filter where meta person = actor
    ob_start() ?>
    <p>the_title()</p>
    <?php $contents = ob_get_contents(): ?>
    

    Then we can get the contents in the buffer and save them to our cache using wp_cache_set()

    &lt;?php
    wp_cache_set("_rt-person-actor",$contents);
    ?&gt;
    

    After this, before running the query we can check if we have it in the cache and return that only

    &lt;?php
    if(wp_cache_get('_rt-person-actor')){
      echo wp_cache_get('_rt-person-actor');
      return; // don't perform queries below
    }
    
    // ...your code to get posts
    

    This will not work though, why ? the wp cache functions store non persistent cache, means once the request is over cache will be removed

    What use of this is then?, we can use persistent cache tool like redis, for that you can use redis cache object plugin


    Select media modal using wp.media

    I create this generic function for mediametaboxes to launch different modal according to which button is clicked

    /*
    This function will be used by meta boxes to display wp.media modal.
     */
    function createMediaFrame( // eslint-disable-line no-unused-vars
    	title,
    	allowMultiple,
    	mediaType,
    	hiddenInputID,
    	callback = null
    ) {
    	// Create a new media frame.
    	const frame = wp.media({
    		title,
    		multiple: allowMultiple,
    		library: { type: mediaType }, // Show only the specified media type in the media library.
    	});
    
    	// Callback when media is selected.
    	frame.on('select', function () {
    		const attachments = frame
    			.state()
    			.get('selection')
    			.map(function (attachment) {
    				return attachment.id;
    			});
    
    		// Update the hidden input with the selected attachment IDs (for form submission).
    		const hiddenInput = document.getElementById(hiddenInputID);
    		hiddenInput.value = attachments.join(',');
    
    		// Execute the custom callback function if provided.
    		if (typeof callback === 'function') {
    			callback(attachments);
    		}
    
    		frame.close();
    	});
    
    	// Open the media frame.
    	frame.open();
    }
    
    

    Can be used as

    createMediaFrame(
    'Select Images',// name for the modal
     true, //multiselect
    'image', //type of media
    'rt-media-meta-img-ids');
    
    
  • DAY 19

    Adding Custom Roles
    • add_role() is used to add a custom role
    • to add custom role with same capability as admin but not allow to add edit update delete post we can:
      • get all capabilties for admin
      • from that remove capability for add edit update delete posts
      • create a role and give the modified capability to them.
    
      function add_custom_role(){
    	$capabilities = get_role['administrator']->capabilities;
    	unset($capabilities['publish_posts']);	  
    	unset($capabilities['edit_posts']);	  
    	unset($capabilities['delete_posts']);	  
    	add_role('cannot_manager', 'Cannot Manager', $capabilities);
    	}
    
    
    Adding Custom Capability
    • we can filter hook into the default capabilities and append our custom capability to it
    
    function simple_role_caps() {
    	// Gets the simple_role role object.
    	$role = get_role( 'simple_role' );
    
    	// Add a new capability.
    	$role->add_cap( 'access_admin_menu', true );
    }
    
    // Add simple_role capabilities, priority must be after the initial role definition.
    add_action( 'init', 'simple_role_caps', 11 );
    
    
    • then on admin menu page check :
    
    if (! current_user_can( 'access_admin_menu' ) ) {
    	die("you Don't have access to this page");
    }
    
    map_meta_cap() function
    • It is used to map meta capabilties to primitive capability
    • eg. edit_posts is a primitive capability, we want to add capability like edit_rt_movie which is a meta capabiltiy
    map_meta_cap filter:
    • when map_meta_cap() is called for capability check, it returns the required capabilties.
    • This is where the map_meta_cap filter comes into action, we can use it to set our custom capability.
    
    function custom_map_meta_cap($caps, $cap){
    if('edit_rt_movie' === $cap){
    $caps['edit_post'] = true;
    }
    return $caps;
    }
    add_filter('map_meta_cap', 'custom_map_meta_cap',10,2);
    
  • Day 18

    2–3 minutes

    Generating pot file using WP CLI

    • from local environment, open shell
    • go to your plugin directory and type this
    • type
    $ wp i18n make-pot . languages/my-plugin.pot
    

    Generating machine objects file from po file

    $ wp i18n make-mo movie-library-hi_IN.po languages
    

    Different functions used for i18n

    • __()
      • put the string inside __() to make it translatable
      • eg : __('Add new Movie','movie-library');
    • _e()
      • if the string should echo to the browser then use
      __('Add new Movie','movie-library');
      
    • _n()
      • give both singular and plular form of the string
      _n( 'We deleted %d spam message.', // singular
          'We deleted %d spam messages.', //plural
          $count, 
         'my-text-domain'  )
      
    • _x()
      • Sometimes same word may mean diffrent according to in which context it is used for that this is used.
       _x( 'Attachment', 'A files attached to mail', 'my-text-domain' );
      
    • _ex()
      • Use when we want to combine both _e() and _x()

    array_diff()

    How can we set the info selected by user and remove the user that is unselected in metabox?

    We are only getting the selected ids and not what is unselected, for that we can get previously selected terms, compare them with currently selected and remove the ones that are not present in current.

    For this, I used PHP array_diff() function,

    Compares array against one or more other arrays and returns the values in array that are not present in any of the other arrays.

    First we get the selected ids and create terms

    $all_people = array_merge( $directors, $producers, $writers, $actors );
    $terms = [];
    // Create array of terms to be added now.
    		foreach ( $all_people as $person_id ) {
    			$person      = get_post( $person_id );
    			$term        = $person->post_name . '-' . $person_id;
    			$term_exists = term_exists( $term, '_rt_movie-person' );
    			// Create the term if it does not exists.
    			if ( ! $term_exists ) {
    				wp_insert_term( $term, '_rt-movie-person', [ 'slug' => $term ] );
    			}
    			$terms[] = $term;
    		} // end foreach
    
    

    Then we get the terms selected before

    // Get previous terms.
    		$old_terms = wp_get_post_terms( $post_id, '_rt-movie-person', [ 'fields' => 'slug' ] );
    
    

    Compare the both using array_diff() and get terms which are not in the current but were there in previous

    // Get unselected Terms.
    		$unselected_terms = array_diff( $old_terms, $terms );
    

    Finally remove the relationship between terms and out post

    		wp_remove_object_terms( $post_id, $unselected_terms, '_rt-movie-person' );
    
    

    This way, the term relationship will get deleted when unselected in the metabox


    WPDB – standard way to interact with WordPress database


    wpdb is a class provided by WordPress that has functions to handle database queries without having to write raw SQL.

    • It is used so we can query the wordpress database without having to write raw SQL queries.
    • Create an abstraction layer over the database.
    • Common methods
      1. insert() : insert data into a table
      2. prepare() : prepares sql query to avoid sql injection
      3. update() : to update data in table
      4. delete() : to delete data
  • day 17

    • Namespaces in php
    • PSR-4 autoloader
    • Nonces
    • Shortcodes

    Namespaces in php


    Namespaces in php were introduced with version 5.3, they provide a great way to organize our code and also avoid naming conflicts

    example

    <?php
    namespace main\controller
    
    class Media{
    
    }
    ?>
    
    <?php
    namespace main\view
    
    class Media{
    
    }
    ?>
    

    Today or tommorow in project, we will run out of unique names to give to our classes and functions, so this is a great alternative instead of just prefixing everything.

    PSR-4 Autoloader

    PSR-4 autoloader is a standard that we can use to create our autoloader in the project. It uses nested namespaces and follows a specific directory structure.

    We have to then import the autoloader only once in the main plugin file and we can autoload all our files. As for namespaces we have to use the full class name to access the classes.

    spl_autoload_register(function ($class) {
    
        // project-specific namespace prefix
        $prefix = 'movie_library\\includes';
    
        // base directory for the namespace prefix
        $base_dir = __DIR__;
    
        // does the class use the namespace prefix?
        $len = strlen($prefix);
        if (strncmp($prefix, $class, $len) !== 0) {
            // no, move to the next registered autoloader
            return;
        }
    
        // get the relative class name
        $relative_class = substr($class, $len);
    
        // replace the namespace prefix with the base directory, replace namespace
        // separators with directory separators in the relative class name, append
        // with .php
        $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
    
        // if the file exists, require it
        if (file_exists($file)) {
            require $file;
        }
    });
    

    Nonce

    Number used once, nonce is a hash value or some random digits, alphabets that we use to verify requests received are from legit sources and to protect our site from CSRF

    To create nonce we can use wordpress inbuild function wp_create_nonce(). example :

    $nonce = wp_create_nonce('csrf_token');
    
    

    Then in our backend code, when we receive any request we can validate this nonce using wp_verifiy_nonce() function.

    function save(){
    if(!(isset($_GET['csrf_token'] && 
    wp_verifiy_nonce($_GET['csrf_token], 'csrf_token) ){
    return;
    }
    
    }
    

    ShortCode API


    Shortcodes in wordpress are used to render some content on the front end dynamically. they are writter as [directors_list]

    The wordpress finds this shorcode if it is registered or not and call the function connected to it which will return some content.

    This content will then be displayed at the place of the shortcode.

    to register a shortcode we have to use:

    function register_shortcodes(){
       add_shortcode('recent-posts', 'directors_list_function');
    }
    

    Then we also have to define the callback which is going to give the content

    function directors_list_function() {
       //logic to get all directors
       return $directors;
    }
    

  • Monday

    • Reflecting on mistakes
    • Custom array serialization
    • Metadata API

    Reflecting on Mistakes


    I created one file as main plugin file and all other classes inside includes directory.

    movie-library/
    ├── movie-library.php
    └── includes/
        ├── movie.php
        └── person.php
    
    

    So I put everything related to movie like CPT, Taxonomies and metaboxes in the movie.php and for person.php did the same.

    This made the files hugs like about 1000 lines, which made it really hard to maintain all of that, so to fix it and make it more maintainable for me I spent some time to refactor everything and make the directory structure a little bit different.

    movie-library/
    ├── movie-library.php
    └── includes/
        ├── utils/
        ├── metaboxes/
        │   └── MovieMetaboxes.php
        ├── post-types/
        │   ├── Movie.php
        │   └── Person.php
        └── taxonomy/
            ├── MovieTaxonomy.php
            └── PersonTaxonomy.php
    

    This made the whole structure little bit more maintainable.

    Custom array serialization


    The custom meta boxes allow users to select multiple options at a time, they are sent as an array and need to be stored in the database.

    When I looke at the default serialization in WordPress, it is not much readable it looks like this

    a:2:{i:1;a:0:{}s:12:"_multiwidget";i:1;}
    

    Frankly this looks ugly, even though we will be getting the unserialized version of this.

    I wanted to store the array in string like format but also be able to retreive them into the array for that I used below format

    $arr = array(10,20,30);
    $serialized = '[10,20,30]';
    

    Then to retrive this string array, we have to

    • First remove the [ and ] from the string
    • Then Split the string by "," to get the array
    • After converting the array elements into integer values to display the selected we use strict equality.
    /**
    	 * Deserialize Array
    	 *
    	 * @param  mixed $string String retrieved from db.
    	 * @return array
    	 */
    	public static function deserialize_array( $string ) : array {
    
    		$string = trim( $directors, '[]' );
    		$arr    = explode( ',', $directors );
    		$arr    = array_map( 'intval', $directors );
    		return $arr;
    
    	}
    

    Metadata API


    Metadata API is simple and standard way to access metadata of an object in wordpress. Mostly we will use it to store metadata related to our Custom Post Types.

    • add_metadata()
    • delete_metedata()
    • update_metadata()
    • get_metadata()

    Metadata API is really useful to retrieve the metadata, also in our custom metaboxes we use function of this API to create new meta key and values pairs or update them when user creates or updated a movie post.

  • End of week 3

    • Publisher Subscriber Model
    • Singleton Pattern
    • Use yoda condition checks, you must

    Publisher Subscriber Model


    The WordPress hooks loosely follow this architecture, When it used the do_action() function, it is publishing an event to which anyone can subscribe using add_action().
    The hooks system in WordPress is the base of the whole architecture as it allows us to hook into the WordPress events and extend them, the filters even allow us to modify the data which is really cool

    Singleton Pattern


    We are using OOP to code our plugin, the other way is to do it in functional way. Functional programming may seem convinient at start but as the code base becomes larger and larger it is difficult to maintain all those functions and finding unique names for them.

    The main plugin file we used singleton pattern as it will be used only once and no need to create its instances again and again.

    It makes sure that when we are taking instance of the main class, we are getting the same instance every time.

    I will make sure that to use this only once at the entry point of plugin, as overusing the singleton pattern is not a good idea

    <?php
    if ( ! defined( 'ABSPATH' ) ) {
    	exit; // Exit if accessed directly.
    }
    
    require_once 'includes/Movie.php';
    require_once 'includes/Person.php';
    
    /**
     * Plugin Name: Movie Library Plugin
     * Description: Plugin for movie library
     * Plugin URI: tr.rt.gw
     * Author: Pratik
     * Text Domain: movie-library
     */
    class MovieLibraryPlugin {
    	private static $instance = null;
    
    	/**
    	 * Private Constructor to implement Singleton pattern
    	 */
    	private function __construct() {
    		register_activation_hook( __FILE__, array( __CLASS__, 'activate' ) );
    		register_deactivation_hook( __FILE__, array( __CLASS__, 'deactivate' ) );
    
    		/**
    		 * Register the custom post types and taxonomies under rt-movie
    		 */
    		$movie = new Movie();
    		$movie->register();
    
    		/**
    		 * Register the custom post types and taxonomies under Person
    		 */
    		$person = new Person();
    		$person->register();
    
    	}
    
    	/**
    	 * Get the instance of the class
    	 *
    	 * @return MovieLibraryPlugin
    	 */
    	public static function get_instance(): MovieLibraryPlugin {
    		if ( null === self::$instance ) {
    			self::$instance = new self();
    		}
    
    		return self::$instance;
    	}
    
    	/**
    	 * Do tasks when activating the plugin
    	 *
    	 * @return void
    	 */
    	public static function activate() {
    		// Functionality to activate the plugin goes here.
    		flush_rewrite_rules();
    	}
    
    	/**
    	 * Do tasks when deactivating the plugin
    	 *
    	 * @return void
    	 */
    	public static function deactivate() {
    		// Functionality to deactivate the plugin goes here.
    	}
    
    
    	public static function uninstall() {
    		// Clean up the plugin
    	}
    }
    
    MovieLibraryPlugin::get_instance();
    
    

    Use yoda condition checks, you must


    The Yoda conditions programming style (or “Yoda notation”) inverts the variable and constant, literal, or function in an equality conditional expression.

    This how we typically write conditions :

    public static function get_instance(): MovieLibraryPlugin {
    		if ( self::$instance === null ) {
    			self::$instance = new self();
    		}
    
    		return self::$instance;
    	}
    
    

    Using Yoda conditions

    public static function get_instance(): MovieLibraryPlugin {
    		if ( null === self::$instance ) {
    			self::$instance = new self();
    		}
    
    		return self::$instance;
    	}
    
    

    This was really helpful for me as I sometimes write my if condition like

    if ( $message = null)
    

    this will assign null to the $message instead of using comparison

  • Internationalization

    • internationalization
    • WordPress Flow
    • Translations wrapper functions
    • gettext hook

    Internationalization is the process of making our plugins/themes ready to be translated into other languages.

    POT, PO and MO files

    POT (Portable Object Template ) file

    • POT file is used as starting point for translations
    • It contains all the original strings that need to be translated in other languages

    PO (Portable Object) file

    • The PO file contains the actual translations from the template files

    MO (Machine Object) file

    • MO file is generated by compiling the PO File.
    • It is optimised to be used in actual translations

    WordPress Flow


    I got through the wordpress core files to learn how exactly the files are run one by one, like how the wordpress flow works to better understand the actions and filters.

    1. Index.php file is entry point when an user visits the WordPress blog
    2. After index.php wp-blog-header.php is loaded. This file loads the wp-load.php file to load the wordpress
    3. Then it moves to loading template-loader.php
    4. the template loader redirects to the template and emits template-redirect action

    __() , _e() functions in i18n


    In order to make a string translatable in your application you have to just wrap the original string in a __() function:

    __( 'Hello, dear user!', 'movie-library' );
    

    If your code should echo the string to the browser, use the _e() function instead:

    _e( 'Watch from here', 'movie-library' );
    

    gettext hook


    The gettext filter is used to translate the strings in our plugin

    function my_text_strings( $translated_text, $text, $domain ) {
    	switch ( $translated_text ) {
    		case 'Salman' :
    			$translated_text = __( 'Bhai!', 'movie-library' );
    			break;
    	}
    	return $translated_text;
    }
    add_filter( 'gettext', 'my_text_strings', 20, 3 );
    
    
    
  • Custom post types


    In WordPress, we have inbuilt post types like post, pages, attachments, revisions and changesets.

    All of these post types are stored in the table posts. Wordpress allows us to create our own post types and also to extend its functionality like we can create a post type for movies etc.

    To create a new custom post type we use register_post_type() function

    function mlb_custom_post_type() {
    	register_post_type('Movie',
    	  array(
    		'labels' => array(
    			'name'=> __('Movies','textdomain'),
    	'singular_name' => __('Movie','textdomain'),
    			),
    	);
    }
    add_action('init', 'mlb_custom_post_type');
    
    

    This will register a new post type Movie which will be created at init hook.

    Taxonomies

    What are taxonomies in WordPress, it is just a way for classification of things like we use tags and categories.

    We can aslo create our own custom taxonomies like for our movies we can categorize them in different genres

    function mlb_register_taxonomy_genre()
    {
        $labels = array(
            'name'=> _x( 'Genres', 'taxonomy general name' ),
    		 'singular_name'=> _x( 'Genre', 'taxonomy singular name' ));
    
    $args = array(
    		 'hierarchical'   => false
    		 'labels'         => $labels,
    		 'show_ui'        => true,
    		 'rewrite'        => [ 'slug' => 'genre' ],
    	 );
    
    

    Creating custom post types and taxonomies is the WordPress way of extending its functionality however we want without ever having to touch its core files.

  • Basic plugin development

    • Hooks
    • actions and filters

    Hooks in wordpress

    in wordpress, when some point in the execution is going on a hook is emitted like when the user created a new post or when the user logs out.

    We can write code that will be attached to these hooks and executed when hooks are called.

    There are two types of hooks in wordpress

    1. Action hook
    2. Filter hook

    Action Hooks

    Action hook let’s us do something when the hook gets called.

    For example we can remove all data of our plugin when the uninstall action is called.

    Action hooks are defined using

    do_action( 'action_name', [optional_arguments] );
    

    We can then execute some code by attaching a function to this action hook

    add_action('action_name' , 'function_name');
    
    function function_name()
    {
        //do stuff
    }
    

    Filter Hooks

    Filter hooks are used to modify the data. So we can pass the data to our custom filter, modify it and then return the modified data.

    To apply a filter,

    $message = apply_filter('message' , $message)
    

    And then we can attach our custome function to filter the message

    add_filter('message' , 'filter_message');
    
    function filter_message( $message )
    {
        return $message . "modified";
    }
    

    The filter hooks will always get some data sent to them and will have to return some data. The actions may or may not get data and only need to do some tasks.

  • Coding standards

    • Rules vs Standards
    • PHP code sniffer
    • Plugins : the first step

    Rules vs Standards


    When we are writing code, we are making something that will be used by other people. As we go on writing code, it becomes a huge mess if everyone just wrote whatever they want however they want as long as it works.

    To stop this from happening and make code readable and manageable we follow some rules while writing code. Rules may be like using tabs instead of spaces for indentation or using camel case for naming functions etc.

    There are many rules we can follow, but if in a large group of people, everyone is following those rules it becomes a standard. The standard that is followed within an organization or by a small group of developers or a huge community of open source devs.

    When writing PHP code, to enforce the standards we use PHP code sniffer

    PHP Code Sniffer

    We can set custom standards to be followed and the PHP code sniffer detects in our code where we have violated them. PHPCS can detect standards violations in CSS and JS also but in WordPress we use it only for PHP as there are other tools for those languages too.

    We can install multiple standards like for WP VIP, for WP core etc


    Plugins : the first step

    After learning how to use WordPress, we are going to learn how to extend its functionality using plugins.

    one huge rule for WordPress : Never touch the core

    There can be incredible things to be done without ever having to touch the core of WordPress. We can extend the functionality of our WordPress using plugins.

    How to create a plugin ?

    • Go into wp-contents/plugins
    • You can create plugins even using just one php file
    • create a php file : plugin-name.php
    • To make this file interpreted as a plugin we have to put a header doc in the php
    /*
     * Plugin Name:       Test
     * Plugin URI:        https://example.com/plugins/the-basics/
     * Description:       Testing if this works
     * Version:           1.0.0
     * Requires at least: 6.2
     * Requires PHP:      7.2
     * Author:            Pratik Londhe
     */