Create a custom HivePress extension
In this tutorial, we'll create a custom HivePress extension that allows users to:
Follow or unfollow any vendor
View listings from the followed vendors on a single page
Get emails about new listings from the followed vendors
Unfollow all vendors with one click
While this extension is pretty simple, it covers the main aspects of the HivePress framework, so by the end of this tutorial, you should be able to create your own extension for HivePress.
Before we begin, please make sure that you have a working WordPress installation and at least basic WordPress development skills. The result of this tutorial is available on GitHub, so you can browse the complete source code or download the extension to test it locally.
Create the main file
First, create a directory in the wp-content/plugins
WordPress subdirectory. The directory name will be used as the extension identifier, so make sure it's unique enough to avoid conflicts with other HivePress extensions (use lowercase letters, numbers, and hyphens only).
For this tutorial, we'll name it "foo-followers", where "foo" is a unique prefix (e.g. your company name or abbreviation), and the "followers" part describes the extension purpose.
Next, create a PHP file with the same name inside the extension directory. This is the main file that is loaded by WordPress automatically when the extension is active.
As you can see, this file contains the extension details, such as:
Plugin Name & Description - the extension name and short description of its purpose;
Author & Author URI - your or your company name with an optional website URL;
Text Domain - used for translating the extension, matches the main file name.
If you plan to distribute your extension, please name it "Something for HivePress" rather than "HivePress Something" to avoid trademark violation.
Also, there’s a simple code that prevents direct file access by URL so that it can be loaded by WordPress only. We’ll add the same code to every file created in this tutorial:
The only thing the main file does is register the extension directory via the hivepress/v1/extensions
hook to allow HivePress to detect and load all the other extension files automatically:
If you plan to submit your extension to the WordPress.org repository, make sure to follow its guidelines and add the readme.txt
along with the license file.
After you create the main file, go to WordPress > Plugins and activate the extension:
Create a component
In HivePress, components are PHP classes used to group actions, filters, and helper functions. Let’s create an empty component and add the extension-specific functions to it later.
Create a class-followers.php
file in the includes/components
extension subdirectory. Notice that it has the class-
prefix, and its name matches the extension name. It must be unique enough to avoid conflicts with other HivePress components (use lowercase letters, numbers, and hyphens only).
The PHP class name must be based on the file name, but with underscores instead of hyphens and no lowercase restriction (e.g. Foo_Bar
class for the class-foo-bar.php
file).
If your extension is simple enough, you can fully implement it within a component and skip other steps, but we recommend using the HivePress framework where possible.
Create a model
In HivePress, models are PHP classes that represent the WordPress database entities such as posts, comments, or terms. Using models makes working with the database much easier.
Let’s create a Follow
model for storing the follower ID along with the followed vendor ID. Create a class-follow.php
file in the includes/models
extension subdirectory. The file and PHP class naming conventions are the same as for components.
Notice that the model class is based on the Comment
class. This means that the model objects will be stored as WordPress comments of a custom type. We implement the model this way because comments are linked to both users and posts in the WordPress database schema. Since vendors are implemented as posts of a custom type, we can use this model to store both the follower (user) and the followed vendor IDs.
The Follow
model contains 2 fields:
user - mapped to the
user_id
database field, used for storing the follower ID;vendor - mapped to the
comment_post_ID
database field, used for storing the followed vendor ID.
Also, if the model is based on the Comment
class, WordPress will show the model objects in the comment feeds. To keep the model objects hidden, create a comment-types.php
file in the includes/configs
extension subdirectory:
The code above contains a configuration that makes the follow
comment type registered by the model we created private.
Create a template
In HivePress, templates are defined as PHP classes that contain arrays of blocks. With blocks, it’s easy to re-use and customize specific layout parts without affecting the whole template.
Let’s create a template for a page that displays all listings from the followed vendors. Create a class-listings-feed-page.php
file in the includes/templates
extension subdirectory. The file and PHP class naming conventions are the same as for components.
It’s good practice to follow the {entity}-{context}-{layout}
pattern for naming templates (e.g. Listing_Edit_Page
, Vendor_View_Block
).
As you can see, the template class is based on the User_Account_Page
class. This means that the template inherits the user account page layout and adds custom blocks to it.
The template we created adds 2 blocks to the page_content
area:
listings - displays listings for the current page;
listing_pagination - displays the page links for navigation.
The block names must be unique within the template scope. Each block is defined as an array containing the block type and extra parameters used to render the block.
Create a controller
Now, we need to define custom URLs that will render the template we created and perform specific actions, such as following or unfollowing a vendor. In HivePress, this can be done using controllers – PHP classes that define URL routes and implement actions corresponding to them.
Create a class-followers.php
file in the includes/controllers
extension subdirectory. The file and PHP class naming conventions are the same as for components.
Add the template route
Let's define a new URL route in the controller constructor:
If you add new or change any of the existing URL routes, don't forget to refresh permalinks in the Settings > Permalinks section.
The route we defined uses these parameters:
title - used for the page title and menu label;
base - used for inheriting another route
path
;path - the relative route URL path;
redirect - points to the URL redirect function;
action - points to the route action function;
paginated - flag required for paginated URLs.
Based on the parameter values, the listings_feed_page
route has the /account/feed
URL, renders a page with the “Feed” title and supports pagination.
Next, let's implement the redirect and action functions for this route below the constructor:
When the route URL is visited, the redirect function is called first. In our case, it checks if the current user is logged in and has any followed vendor IDs. It returns the corresponding redirect URL or false
if all checks are passed.
If there was no redirect, the action function is called next. As you can see, it creates a query for listings published by the followed vendors, sets it as the main page query, and finally renders the template we created earlier. Notice that this function returns the rendered HTML instead of outputting it with echo
.
Now, if you refresh permalinks in Settings > Permalinks and try to visit the /account/feed
URL, you will be redirected because you haven’t followed any vendors yet.
Update the component
We already used a code that checks if the current user follows any vendors by checking the vendor_follow_ids
value in the request context, but there’s no function that sets this value in context yet. Add this code to the component constructor:
The code above hooks a custom filtering function to the request context values. Let’s implement this function in the component below the constructor:
This function checks if the followed vendor IDs are cached for the current user, and if not, it queries the Follow
model objects by user ID and fills an array of vendor IDs, then caches this array. Finally, it sets an array of vendor IDs in the vendor_follow_ids
context, this allows us to get it anywhere in the code this way:
Also, the listing feed page we created doesn’t have any links on the front-end yet, so let’s add it to the user account menu. Add this code to the component constructor:
The code above hooks a custom filtering function to the user account menu parameters. Next, implement this function in the component below the constructor:
As you can see, it adds a custom menu item linked to the listings_feed_page
route we created previously. You can adjust the _order
parameter value to change the menu item position. The menu item will appear only if the current user follows any vendors.
Add REST API routes
Let’s also create URL routes that allow users to follow or unfollow vendors. Since these routes don’t render anything and are used for performing actions only, we will define them as REST API routes. These routes don’t require the title
, redirect
, and paginated
parameters, but other parameters are needed instead:
method - restricts the accepted HTTP method (e.g.
GET
,POST
);rest - flag required for REST API routes.
It’s good practice to follow the {entity}-{context}-{type}
pattern for naming routes (e.g. listing_view_page
, vendor_update_action
).
The code above defines 2 REST API routes, both accept requests via the POST
method. The first route will follow or unfollow a vendor on every subsequent request, while the second one will unfollow all vendors at once. Next, implement the action functions for these routes:
The follow_vendor
function checks if the current user is logged in, gets a Vendor
object by ID, and queries the Follow
objects by the user and vendor IDs.
Then, if any Follow
objects are found, they are deleted. If not, a new Follow
object is created and saved in the database. This way, every subsequent call of this function will follow or unfollow a vendor, creating or deleting a Follow
object.
The unfollow_vendors
function checks if the current user is logged in, then queries the Follow
objects by the user ID and deletes them, thus unfollowing all vendors.
Now we have all the URL routes and actions according to the extension requirements. You can view the complete controller source code on GitHub for reference.
Create a block
We created REST API routes, but there are no links or forms on the front-end that send requests to these routes yet. Let’s add a toggle link that sends a request to the vendor_follow_action
on click and add it somewhere on the vendor page. We need to create a new block type for this.
In HivePress, block types are defined as PHP classes with properties and methods that determine the behavior and rendering of the block.
Create a class-follow-toggle.php
file in the includes/blocks
extension subdirectory. The file and PHP class naming conventions are the same as for components.
Notice that the block class is based on the Toggle
class – this is an existing block type available in HivePress, so all the properties and methods are inherited from it.
The Follow_Toggle
block type we created defines 2 states for the toggle, setting the Font Awesome icon name and a label for each state.
Before the block is rendered, it fetches the Vendor
object from the current template context and sets the toggle url
to the vendor_follow_action
route we created previously. It also enables the active
flag if the vendor ID is among the followed vendor IDs.
This way, the toggle will show the “Follow” or “Unfollow” label on every subsequent click and send an AJAX request to the vendor_follow_action
route URL. Also, it will show the “Unfollow” label by default if the vendor is already followed.
It’s good practice to re-use the existing HivePress block types and avoid creating new ones if possible. In this case, we had to create a new block type because it implements a custom logic (the toggle state depends on the followed vendor IDs).
Update the component
Next, let’s add this block to the vendor templates. Add this code to the component constructor:
The code above hooks a custom filtering function to the vendor template parameters. Now, implement this function in the component below the constructor:
The function above filters the template parameters and adds a new block using the block type we created earlier. Let’s check if the toggle link is added on the front-end:
Now you can try clicking on the Follow toggle and check if the vendor listings appear on the listing feed page, and unfollow a vendor to check if listings disappear.
Create a form
We still have one action left that is not used anywhere, the one that unfollows all vendors at once. So let’s create a form for it and add a button to show the form on click.
Create a class-vendors-unfollow.php
file in the includes/forms
extension subdirectory. The file and PHP class naming conventions are the same as for components.
The form we’ve created defines these parameters:
description - text displayed before the form;
action - URL for sending requests on submission;
method - HTTP method for sending requests (e.g.
POST
,GET
);redirect - flag to refresh or redirect the page;
button - the submit button parameters.
This form doesn’t contain fields, but you can define an array of fields in the fields
parameter and the form will render them, sending the entered values with the request.
Update the template
Next, let’s add new blocks to the Listings_Feed_Page
template we created earlier:
As you can see, there’s a part
block that loads a specific HTML file and a modal
block that contains the form we’ve just created. The part block points to a non-existing file, so we need to create a vendors-unfollow-link.php
file in the templates/vendor/follow
extension subdirectory:
It’s good practice to follow the {entity}/{context}/{layout}
directory structure for template parts, this way you can easily find the template where the part is used.
Now, let’s check the listing feed page. It should have the “Unfollow” button that opens a modal window on click. The modal window contains a form that sends a request to the vendors_unfollow_action
route URL, thus unfollowing all vendors at once.
Create an email
Finally, let’s add an email notification sent to users about new listings from the followed vendors.
Create a class-listing-feed.php
file in the includes/emails
extension subdirectory. The file and PHP class naming conventions are the same as for components.
The email we’ve just created defines these parameters:
subject - the email subject;
body - the email message with placeholders.
Update the component
Next, let’s add a function that sends an email to the vendor followers if there’s a newly published listing. Add this code to the component constructor:
The code above hooks a custom function to the listing status change action. Now, implement this function in the component below the constructor:
The function above checks if the listing got the "published" status, gets all the Follow
objects by vendor ID and sends an email to each follower, providing the email address and tokens to be replaced in the email text.
Now, if you follow a vendor and this vendor publishes a new listing, you will get an email notification that contains the listing title and URL.
Create a POT file
You probably noticed that we wrapped all the texts in the code with the translation functions. We also need to generate a POT file for translation to work properly.
Create a new languages
extension subdirectory and install the Loco Translate plugin. Go to Loco Translate > Plugins > Followers for HivePress and click Create template, then proceed.
That's it. Now website owners can translate or change any of the extension texts via Loco Translate or POEdit without editing the source code directly.
Keep developing
Congratulations! You’ve just developed a fully-functional HivePress extension. Even though there's a lot more to the HivePress framework than what you’ve seen so far, you’re now ready to start developing your own HivePress extensions.
For example, you can develop a custom HivePress extension for a client, share it on the WordPress.org repository or even sell it on the CodeCanyon marketplace.
If you have any questions about the HivePress framework, please check the available docs and feel free to join the HivePress developer community.
Last updated