Using Craft CMS helpers in Twig since changes to create() #18376
-
|
Craft CMS 5.9.0 and 4.17.0, no longer allows you to use the {% set moneyHelper = create('craft\\helpers\\MoneyHelper') %}
{{ moneyHelper.toDecimal(value) }}Due to the recent change:
If you wanted to use the Craft CMS helpers in Twig, what is the preferred approach to do this now? I could create a global of the required helper via Twig extension e.g. <?php
use craft\helpers\MoneyHelper;
use Twig\Extension\AbstractExtension;
use Twig\Extension\GlobalsInterface;
class ExampleTwigExtension extends AbstractExtension implements GlobalsInterface
{
public function getGlobals(): array
{
return [
'moneyHelper' => new MoneyHelper()
];
}
}Perhaps there is a better way though? |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 6 replies
-
|
Ran into the same issue, and yes, it should work this way. However, I received the following warning, (which, tbh, I can't tell whether it's true or relevant): You are correct that it can “work” in practice. So a solution would be to use a 'proxy class' <?php
namespace modules\main\web\twig;
use craft\helpers\MoneyHelper;
/**
* @mixin MoneyHelper
*/
final class MoneyHelperProxy
{
public function __call(string $method, array $args)
{
return MoneyHelper::{$method}(...$args);
}
}and use that in the Twig extension: public function getGlobals(): array
{
return [
'moneyHelper' => new MoneyHelperProxy(),
];
}(The proxy class could be abstracted so that it works for every static class, but I prefer a dedicated class so that IDE code suggestions work). It's worth noting that behind the changelog notice |
Beta Was this translation helpful? Give feedback.
-
|
Interesting, it would be nice if Craft exposed its helpers through Twig directly to be fair. I know some select methods of some helpers are, but that's down to developer choice by the looks of it. It seems a lot of work to have to proxy each helper class you want to use, on the grounds of being "robust across PHP upgrades and strict error handling" for future upgrades. There is perhaps a better way it could be done in Craft CMS core given create() now can't be used to reference them, there are other useful helpers e.g. DateTimeHelper if you needed more advanced logic and didn't want to reinvent the wheel in a macro or something. |
Beta Was this translation helpful? Give feedback.
-
|
Saw a generic class that could avoid creating multiple proxy classes, (untested, bad IDE support) <?php
// StaticClassProxy.php
final class StaticClassProxy
{
public function __construct(private string $classname) {}
public function __call(string $method, array $args)
{
return ($this->classname)::{$method}(...$args);
}
}return [
'Str' => new StaticClassProxy(\Illuminate\Support\Str::class),
] |
Beta Was this translation helpful? Give feedback.
-
It's from a private conversation with a Laravel person in the context of a Laravel -> Craft port, the wording was probably generated with the help of AI, but checked. I found this article in the meanwhile, which confirms the statement but makes it seem less urgent. Calling static methods from an instance is a long-standing, allowed behavior in PHP, but it is widely discouraged due to ambiguity and OOP principle violations. As of 2024, there are no official plans to deprecate this pattern, but developers should avoid it to improve code clarity and future-proof their applications. So for the moment you might feel save to stay with the |
Beta Was this translation helpful? Give feedback.
-
|
I was unaware people are using And that warning is correct @wsydney76. Even if you can instantiate them, you shouldn’t be treating static methods as instance-level methods. That said, I went ahead and adjusted We can figure out a better solution in Craft 6. |
Beta Was this translation helpful? Give feedback.
I was unaware people are using
create()for helpers classes. Those really should beabstractsince they are have exclusively static methods, and are not intended to ever be instantiated.And that warning is correct @wsydney76. Even if you can instantiate them, you shouldn’t be treating static methods as instance-level methods.
That said, I went ahead and adjusted
create()to allowcraft\helpers\*classes to be created for the next release, as there’s no security concern with them. (fae1c5f)We can figure out a better solution in Craft 6.