Magento 2 6/2/2022

Magewire: Business logic reclaimed by the backend!

In today's web, it may look like JavaScript has taken over everything. Web apps (even websites that don't need to be an app) are built more and more as rich clients with a great deal of business logic moved to the frontend.

This means:

  1. Frontend developers need a broader skill set (or to be accompanied by dedicated JavaScript developers)
  2. More code is shipped to the frontend, increasing initial load time
In this regard, our friends at Hyvä Themes follow a "back to the roots" philosophy: With their new frontend for Magento, you ship less code to the frontend, embrace the monolith, favor a PWS ("proper website") over a PWA (progressive web app).

At our recent HyväCamp, Willem Poortman, Senior Solution Engineer at Hyvä Themes, presented their relatively new open source project that leads to even less JavaScript being written: Magewire.

What's Magewire?

Its promise: write dynamic frontends just with PHP. Magewire will wire it together via fetch requests (formerly known as "AJAX") automatically.

Or with their words:

Magewire is a Laravel Livewire port for Magento 2. The goal is to make it fun and easy to build modern, reactive and dynamic interfaces, without leaving the comfort of Magento's core layout and template systems. Magewire can be the missing piece when you intend to build dynamic and reactive features, but don't require or feel comfortable working with a full JavaScript framework like Vue or React.
It shall be noted that it is built on the shoulders of giants. As mentioned, the concept is taken from Laravel Livewire. The JS code is basically the same, and the PHP code is ported and adjusted to Magento. Isn't open source great?

How does it work?

You can install Magewire with composer. It's primarily built for Hyvä based themes, but you can also use it in a classic Magento frontend by using the "magewire-require" module:
composer require magewirephp/magewire
composer require magewirephp/magewire-requirejs
To see it in action, some educational examples are bundled in an example module. This is how you install it:
composer require magewirephp/magewire-examples
On the page magento.test/magewire/examples you'll see some abilities of Magewire, for example a “To Do” list:

Screenshot 2023-05-26 130335.jpg

This screenshot was taken on the Magewire examples page in a store with a Luma theme. It looks like it needs more styling.

The examples look better in a Hyvä theme with Tailwind CSS, like this one:

You can learn more about Magewire's abilities on the page /magewire/features but to understand those examples, I recommend checking out their code first. Some of the examples require AlpineJS, so they will typically only work in a Hyvä based theme. Let's have a closer look at the "To Do" example. Both the "Tasks Todo" and the "Tasks Done" block are Magewire components, and they are communicating with each other. I'll explain this later, first let's observe how the example works: When you add an item to the to-do list, a fetch request to a special Magewire URL is performed and Magewire calls the appropriate function of your custom component (basically a remote procedure call). The response contains updated HTML for the component, events to be dispatched in the frontend (e.g. to display the success message) and some other information about changed data. In the frontend, the DOM is then intelligently updated after comparing the returned HTML with the current DOM. When you mark an item as done, two requests are made, one to remove it from the “To Do” component, then another one (triggered by an event from the response) to also update the “Done” component. All this happens completely transparently - as a developer you don't need to spend much thought on it.

How to invoke the magic

Now let's see where the magic happens. Again, we are looking at the relevant code of the "To do" example (some code has been abbreviated here to a minimum working example).

The component

A Magewire component consists of two parts: a PHP class extending from Magewirephp\Magewire\Component and a PHTML template. To bring them together, the PHP component is added to the template block as a view model with the name "magewire" (layout XML):


   
               \Magewirephp\MagewireExamples\Magewire\Todo
       
   

Input handling

The form to add a new task uses special HTML attributes with the "wire" namespace:


Here, "task" refers to a public property and "save Task" to a method of the PHP component:

class Todo extends Component
{
   public $task = '';
   public $tasks = [];

   public function saveTask()
   {
       $title = ucfirst($this->task);

       if (empty($title)) {
           return $this->dispatchErrorMessage(__('Title can not be empty'));
       }
           $this->tasks[] = $title;
           $this->dispatchSuccessMessage(__('Task has been saved successfully.'));
       }
       $this->task = '';
   }

If you know Knockout.js from Magento, the two-way binding between the public property and the input value will look familiar. As soon as a remote procedure call is made (here: saveTask()), $task is populated with the input value. And afterwards, the input value is set to the new value of $task (i.e. it becomes empty again).

With the dispatchXxxMessage() methods we can show flash messages to the user, as you would do with the MessageManager in a controller.

Rendering

But how is dynamic data displayed? Let's see what happens with the array in $tasks in the template ($magewire is the Magewire component):
tasks as $index => $title): ?>
  • Wait, it's all PHP? Yes! The initial state of the component is rendered server-side and for updates the block is rendered again. No new syntax to be learned here.

    Events

    The button is again wired to a method of the component, finishTask(), and as you see we can easily pass arguments.

    Now we've seen that when the task is finished, another component, the "tasks done" list, is updated as well. This is accomplished using the Magewire event system:

    public function finishTask(int $index)
    {
       if (!isset($this->tasks[$index])) {
           return $this->dispatchErrorMessage(__('Task with ID "%1" doesn't exist', [$index]));
       }
    
       $this->emitTo('magewire.todo-checklist', 'todo:finish', [$this->tasks[$index]]);
       unset($this->tasks[$index]);
    }
    

    The call to the emitTo() method sends the event "todo:finish" to the component "magewire.todo-checklist", with the finished task as an additional argument. The component name is the name of the block in the layout. Note that you don't have to specify a target component. With the method emit() you can publish an event that any other component that subscribed to it can react to. To listen to the event in the other component, the event name is mapped to a method via the $listeners property:

    class TodoChecklist extends Component
    {
       public $tasks = [];
    
       protected $listeners = ['todo:finish' => 'saveTask'];
    
       public function saveTask(string $title = null): void
       {
           $this->tasks[] = $title;
       }
    

    There's much more!

    I hope this small example is enough to make you curious. If you check out the example module and the feature documentation, you'll find much more!

    Some highlights:

    • Events can also be sent from client to server and vice versa
    • Your favorite JS framework has component lifecycle hooks to give you more control? So does Magewire - in JavaScript AND in PHP!
    • Optional lazy updating to prevent requests on every button click or key press
    • Prebuilt components for forms and pagination (more to come)

    But what is the use of Magewire?

    You may wonder what the best use case of Magewire is. What about this: Building a checkout for Magento with writing as little JavaScript as possible? As the typical Magento developer does not hold a deep love for JS, that's promising! Good news, this is already in the making (see below).

    And if you have any other highly interactive pages, e.g. a product configurator, it's definitely worth considering Magewire for the implementation.

    What's next for Magewire?

    At Reacticon 2021, Willem Poortman already announced that he's working on a new checkout for Hyvä, based on Magewire. In the past months a lot of effort has been put into it to make it a great, extensible, user- and developer-friendly default checkout solution for Hyvä and we're thrilled to work with it. The Magewire based checkout is planned to be made available as a standalone product that you can acquire separately from a Hyvä Themes license since it can be used with the default Magento frontend as well. Until that time, which may be a couple of months in the future, there are other reliable options for your checkout, like the Hyvä React Checkout that is compatible with any kind of frontend theme.