Posted on • Originally published atglpzzz.dev on
Integrating Yii3 packages into WordPress
I was recently assigned with the task of integrating several extensive forms into a WordPress website. These forms comprised numerous fields, intricate validation rules, dynamic fields (one to many relationships) and even interdependencies, where employing PHP inheritance could mitigate code duplication.
Upon initial exploration, it became evident that the conventional approach for handling forms in WordPress typically involves either installing a plugin or manually embedding markup using the editor or custom page templates. Subsequently, one largely relies on the plugin’s functionality to manage form submissions or resorts to custom coding.
Given that part of my task entailed logging data, interfacing with API endpoints, sending emails, and more, I opted to develop the functionality myself, rather than verifying if existing plugins supported these requirements.
Furthermore, considering the current landscape (as of March 2024) where most Yii 3 packages are deemed production-ready according to official sources, and being a long-time user of the Yii framework, I deemed it an opportune moment to explore and acquaint myself with these updates.
Source code available
You can explore the entire project and review the code by accessing it onGithub.
Additionally, you can deploy it effortlessly using Docker by simply executingdocker-compose up
from the project’s root directory. Check theDockerfile
for the WordPress setup and content generation which is done automatically.
Goal
My objective was to render and manage forms within a WordPress framework utilizing Yii3 packages. For demonstration purposes, I chose to implement a basic Rating Form, where the focus is solely on validating the data without executing further actions.
Approach
To proceed, let’s start with a minimalistic classic theme as an example. I created a WordPress page named “The Rating Form” within the dashboard. Then, a file namedpage-the-rating-form.php
is to be created within the theme’s root folder to display this specific page.
This designated file serves as the blueprint for defining our form’s markup.
Adding Yii3 Packages to the Project:
To harness Yii3’s functionalities, we’ll incorporate the following packages:
- yiisoft/form-model: For defining a model class representing the form’s fields and facilitating field rendering.
- yiisoft/form: To handle markup-related tasks.
- yiisoft/validator: For input validation.
To begin, let’s initialize a Composer project in the root of our theme by executingcomposer init
. This process will generate a composer.json file. Subsequently, we’ll proceed to include the Yii3 packages in our project.
composer require yiisoft/form-model:dev-master yiisoft/validator yiisoft/form:dev-master
and instruct the theme to load the composer autoload by adding the following line to thefunctions.php
file:
require __DIR__. '/vendor/autoload.php';
Create the form model
Following the execution of thecomposer init
command, asrc
directory has been created in the root directory of the theme. We will now proceed to add our form model class within this directory.
Anticipating the expansion of the project, it’s imperative to maintain organization. Thus, we shall create the directorysrc/Forms
and place theRatingForm
class inside it.
<?phpnamespace Glpzzz\Yii3press\Forms;use Yiisoft\FormModel\FormModel;class RatingForm extends FormModel{ private ?string $name = null; private ?string $email = null; private ?int $rating = null; private ?string $comment = null; private string $action = 'the_rating_form'; public function getPropertyLabels(): array { return [ 'name' => 'Name', 'email' => 'Email', 'rating' => 'Rating', 'comment' => 'Comment', ]; }}
Beyond the requisite fields for our rating use case, it’s crucial to observe theaction
class attribute. This attribute is significant as it instructs WordPress on which theme hook should manage the form submission. Further elaboration on this will follow.
Adding Validation Rules to the Model:
Now, let’s incorporate some validation rules into the model to ensure input integrity. Initially, we’ll configure the class to implement theRulesProviderInterface
. This enables the form package to access these rules and augment the HTML markup with native validation attributes.
class RatingForm extends FormModel implements RulesProviderInterface
Now we need to implement thegetRules()
method on the class.
public function getRules(): iterable{ return [ 'name' => [ new Required(), ], 'email' => [ new Required(), new Email(), ], 'rating' => [ new Required(), new Integer(min: 0, max: 5), ], 'comment' => [ new Length(min: 100), ], ];}
Create the form markup
To generate the form markup, we require an instance ofRatingForm
to be passed to the template. In WordPress, the approach I’ve adopted involves creating a global variable (admittedly not the most elegant solution) prior to rendering the page.
$hydrator = new Hydrator( new CompositeTypeCaster( new NullTypeCaster(emptyString: true), new PhpNativeTypeCaster(), new HydratorTypeCaster(), ));add_filter('template_redirect', function () use ($hydrator) { // Get the queried object $queried_object = get_queried_object(); // Check if it's a page if ($queried_object instanceof WP_Post && is_page()) { if ($queried_object->post_name === 'the-rating-form') { global $form; if ($form === null) { $form = $hydrator->create(RatingForm::class, []); } } }});
It’s worth noting that we’ve instantiated theHydrator
class outside any specific function, enabling us to reuse it for all necessary callbacks. With theRatingForm
instance now available, we’ll proceed to craft the markup for the form within thepage-the-rating-form.php
file.
<?phpuse Glpzzz\Yii3press\Forms\RatingForm;use Yiisoft\FormModel\Field;use Yiisoft\Html\Html;/** @var RatingForm $form */global $form;?><?php get_header(); ?><h1><?php the_title(); ?></h1><?php the_content(); ?><?= Html::form() ->post(esc_url(admin_url('admin-post.php'))) ->open()?><?= Field::hidden($form, 'action')->name('action') ?><?= Field::text($form, 'name') ?><?= Field::email($form, 'email') ?><?= Field::range($form, 'rating') ?><?= Field::textarea($form, 'comment') ?><?= Html::submitButton('Send') ?><?= "</form>" ?><?php get_footer(); ?>
In the markup generation of our form, we’ve leveraged a combination of Yii3’sHtml
helpers and theField
class. Notable points include:
- The form employs the POST method with the action specified as the
admin-post.php
WordPress endpoint. - To include the
action
value in the form submission, we utilized a hidden field named'action'
. We opted to rename the input to'action'
as theField::hidden
method generates field names in the formatTheFormClassName[the_field_name]
, whereas we required it to be simply named'action'
.
This adjustment facilitates hooking into a theme function to handle the form request, as elucidated in the subsequent section.
Before delving further, let’s capitalize on Yii’s capabilities to enhance the form. Although we’ve already defined validation rules in the model for validating input post-submission, it’s advantageous to validate input within the browser as well. While we could reiterate defining these validation rules directly on the input elements, Yii offers a streamlined approach. By incorporating the following code snippet into thefunctions.php
file:
add_action('init', function () { ThemeContainer::initialize([ 'default' => [ 'enrichFromValidationRules' => true, ] ], 'default', new ValidationRulesEnricher() );});
By implementing this code snippet, we activate theValidationRulesEnricher
for the default form theme. Upon activation, we’ll notice that the form fields are now enriched with validation rules such as ‘required’, ‘min’, and ‘ max’, aligning with the validation rules previously defined in the model class. This feature streamlines the process, saving us valuable time and minimizing the need for manual code composition. Indeed, this showcases some of the remarkable functionality offered by Yii3.
Process the POST request
When the form is submitted, it is directed toadmin-post.php
, an endpoint provided by WordPress. However, when dealing with multiple forms, distinguishing the processing of each becomes essential. This is where the inclusion of theaction
value in the POST request proves invaluable.
Take note of the initial two lines in the following code snippet: the naming convention for the hook isadmin_post_<action_name>
. Therefore, if a form hasaction = 'the-rating-form'
, the corresponding hook name will beadmin_post_the_rating_form
.
As for the inclusion of bothadmin_post_<action_name>
andadmin_post_nopriv_<action_name>
, this is because WordPress allows for different handlers depending on whether the user is logged in or not. In our scenario, we require the same handler regardless of the user’s authentication status.
add_action('admin_post_the_rating_form', fn() => handleForms($hydrator));add_action('admin_post_nopriv_the_rating_form', fn() => handleForms($hydrator));function handleForms(Hydrator $hydrator): void{ global $form; $form = $hydrator->create(RatingForm::class, $_POST['RatingForm']); $result = (new Yiisoft\Validator\Validator())->validate($form); if ($form->isValid()) { // handle the form } get_template_part('page-the-rating-form');}
Returning to the Yii aspect: we instantiate and load the posted data into the form utilizing thehydrator
. We then proceed to validate the data. If the validation passes successfully, we can proceed with the intended actions using the validated data. However, if validation fails, we re-render the form, populating it with the submitted data and any error messages generated during validation.
Conclusion
- This was my first attempt at mixing Yii3 packages with a WordPress site. While I’m satisfied with the result, I think it can be improved, especially regarding the use of global variables. Since I’m not very experienced with WordPress,I’d appreciate any suggestions for improvement.
- The Yii3 packages I used are ready for real-world use and offer the same quality and features as their older versions.
- Now you can use these Yii packages independently. This means you can apply your Yii skills to any PHP project.
- This project shows how we can enhance a WordPress site by tapping into the powerful features of Yii, while still keeping the simplicity of the CMS.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse