For the last 1.5 years or so I'm working quite a lot with Magewire, mainly thanks to the Hyvä Checkout. As I'm doing a lot of payment method integrations, I'm often required to use third-party javascript SDKs to do things like tokenization of credit card forms for PCI compliance.
One of the things I struggled when I started with integrating these is how do you communicate back to this when something happens in your application that your app needs to know about? An example of this is that when the user clicks "next" or "place order", the entered data needs to be validated. But this data can be incorrect, and you might need to communicate this back to the third party library. How do you do that?
This turns out to be quite simple: By using browser events. To demonstrate this I created a small demo of a world globe, that navigates to the country where the last order came from.
First the layout XML:
<block template="ControlAltDelete_OrdersGlobe::orders.phtml" name="controlaltdelete_globe_orders" > <arguments> <argument name="magewire" xsi:type="object">ControlAltDelete\OrdersGlobe\Magewire\Globe\Orders</argument> </arguments> </block>
Nothing special there, except maybe for the Magewire argument.
Next up, the orders.phtml:
<div wire:poll.3000ms="checkLastOrder"> <div id="globe" wire:ignore ></div> </div> <script src="https://unpkg.com/three"></script> <script src="https://unpkg.com/globe.gl"></script> <script> // Wait for the DOM to load document.addEventListener('DOMContentLoaded', () => { // Select the container const globeContainer = document.getElementById('globe'); // Create the Globe const myGlobe = Globe()(globeContainer) .globeImageUrl('//unpkg.com/three-globe/example/img/earth-day.jpg') .bumpImageUrl('//unpkg.com/three-globe/example/img/earth-topology.png') .backgroundImageUrl('//unpkg.com/three-globe/example/img/night-sky.png') .width(globeContainer.clientWidth) .height(globeContainer.clientHeight) ; // Resize the globe when the window is resized window.addEventListener('resize', (event) => { myGlobe.width(globeContainer.clientWidth) myGlobe.height(globeContainer.clientHeight) }); }); </script>
Here, there are two special things: wire:poll.3000ms="checkLastOrder"
and wire:ignore
. The first one will call checkLastOrder
in our Magewire class every 3 seconds. The second one tells Magewire not to touch the HTML. It will update it every 3 seconds after completing the request. But this HTML is being maintained by the javascript library, so Magewire doesn't have to touch it.
Last but not least, our Magewire class, Orders.php
:
declare(strict_types=1); use Magewirephp\Magewire\Component; class Orders extends Component { public function checkLastOrder(): void { [$lat, $lng] = $this->getLastOrderLatLong(); $this->dispatchBrowserEvent('customer_placed_order', [ 'lat' => $lat, 'lng' => $lng, ]); } private function getLastOrderLatLong() { // ... This is up to you. } }
If we done this alright, we will get a page rendered something like this:
In our phtml we already saw that checkLastOrder()
is being called every 3 seconds. This calculates the lan/long for the last order, and sends it back to the browser using $this->dispatchBrowserEvent()
. But how do we handle this in the browser?
The solution is quite simple: using an even listener on the window object:
window.addEventListener('customer_placed_order', (event) => { myGlobe.pointOfView({ lat: event.detail.lat, lng: event.detail.lng, altitude: 1.5 }, 1000); });
This listens for the browser event, and communicates with the external library that it needs to navigate to this position on the globe. The extra data we added to the event in the Magewire class is available in the event.details
object.
Hey, I'm running a developer focussed newsletter called MageDispatch.com, which is for the community and by the community. Here you can share links that you think that the community should know about. If you like this kind of technical content you will like this newsletter too.
The end result
When you have a lot of orders, the live result will look something like this:
Want to respond?