How to add a custom function to smarty extension yii2 - php

i have a little problem with smarty extension for yii2.
I've created a new smarty function, and i've added the code into this file:
backend/vendor/yiisoft/yii2-smarty/src/Extension.php
public function __construct($viewRenderer, $smarty)
{
//other code
/* CUSTOM FUNCTION REGISTER */
$smarty->registerPlugin('function', 'test', [$this, 'functionTest']);
}
//this is the custom function
public function functionTest($params, $template){
return "Test custom funcion";
}
And i can use this custom function into my template like this {test} and all works fine.
Today i have update the yii2 to the 2.0.20 version, and obviously the Extension.php file was replaced, so i can't access anymore to the custom function.
My question is: How i can add a custom function for smarty in yii2?
I'll set the config array in this way:
//this is in backend/config/main.php
'view' => [
'renderers' => [
'tpl' => [
'class' => 'yii\smarty\ViewRenderer',
'pluginDirs' => ['#backend/saSmartyPlugin'],
'widgets' =>[
'functions' => [['test' => 'test'], ],
],
//'cachePath' => '#runtime/Smarty/cache',
],
],
],
and the into saSmartyPlugin folder i insert my test.php file like this:
<?php
class Test{
function functionTest($params, $template){
return "Test custom funcion";
}
}
But i get this error:
Smarty: Undefined class 'test' in register template class

I agree with Muhammad Omer Aslam, you should extend from backend/vendor/yiisoft/yii2-smarty/src/Extension.php in order to create Any new methods and be able to use them after update. After that you just write in your config file path to your extended class.

I'll find a solution thinking about #MuhammadOmerAslam and #SergheiLeonenco suggest me.
I write this answer for anyone who has this problem.
First i create my php file Test.php and i extend the Extension class of Smarty
namespace common\components;
use yii\smarty\Extension;
class Test extends Extension{
public function __construct($viewRenderer, $smarty){
parent::__construct($viewRenderer, $smarty);// call parent construct
$smarty->registerPlugin('function', 'bread', [$this, 'functionBreadcrumbs']);//register my custom function
}
//My custom function
function functionTest($params, $template){
return "Test custom funcion";
}
And i save this file into common/components/
After that i have modified my config.php file
'view' => [
'renderers' => [
'tpl' => [
'class' => 'yii\smarty\ViewRenderer',
'extensionClass' => 'common\components\Test'
],
],
],
],

Related

In Laravel can I set a default context for the Log facade

I'm using the Log:: facade a lot and have a helper class called LogHelper which provide me with a static method LogHelper::context() which include many key values I need to track the requests. But having to type it every time for each usage make it error prune and fill not so efficient.
I'm looking for a way to inject the values by default, and allow me to overwrite them if needed specifically.
At the moment this is how I use it,
Log::debug('Request Started', LogHelper::context());
what I'm looking for is to inject the context by default
Log::debug('Request Started');
and have the option to overwrite it, if need it:
Log::debug('Request Started', ['more' => 'context'] + LogHelper::context());
PS, the LogHelper::context() return a simple key => value array which include some staff i need to debug requests, and the reason it do not use the values directly in the message is because i log to graylog as structured data, and this way i can filter by any key.
I have solved this issue by using the tap functionality and $logger->withContext() (note: the latter was added in Laravel 8.49).
You want to create a new class which contains your context logic. I've created an extra Logging folder in app/ in which my logging customizations sit.
app/Logging/WithAuthContext.php:
<?php
namespace App\Logging;
use Illuminate\Log\Logger;
class WithAuthContext
{
public function __invoke(Logger $logger)
{
$logger->withContext([
'ip' => request()?->ip(),
'ua' => request()?->userAgent(),
]);
}
}
Depending on which logging channel(s) you use, you will have to add the class to each one you want to add context to. So in app/config/logging.php:
<?php
use App\Logging\WithAuthContext;
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
return [
// ...
'channels' => [
// ...
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'tap' => [WithAuthContext::class],
],
// ...
],
];
There is a way, but it is not pretty. You can create a custom monolog logger driver. The process is described at https://laravel.com/docs/8.x/logging#creating-monolog-handler-channels.
Here's a possible implementation:
class ContextEnrichingLogger extends \Monolog\Handler\AbstractHandler {
private $logger;
public function __construct($level = Monolog\Logger::DEBUG, bool $bubble = true, $underlyingLogger = 'single') {
$this->logger = Log::driver($underlyingLogger);
}
public function handle(array $record) {
$record['context'] += LogHelper::context();
return $this->logger->handle($record);
}
}
Then register this as a custom logger in your config/logging.php:
return [
'default' => 'enriched',
//...
'channels' => [
// ...
'enriched' => [
'driver' => 'monolog',
'handler' => ContextEnrichingLogger::class,
'level' => env('APP_LOG_LEVEL', 'debug'),
"with" => [
"underlyingLogger" => env('LOG_CHANNEL', 'single')
]
]
]
];
I haven't tested this particular one but this is how I've defined other custom loggers.
Note, this is probably also achievable via a custom formatter though I think it's probably the same trouble.

wherein Laravel Route parameter

Suppose I have a route like this :
$api->group(['prefix' => 'Tag/{type}'], function ($api) {
});
As you can see there is a required type parameter.
Now I want type parameter can only one of items of an array that is defined in a config file like this :
return [
'name' => 'Tag',
'types' => [
'product' => 'product',
'user' => 'user'
]
];
Means type can be product or user only. I know that I should use Regular Expression Constraints but I do not know how ?
simple!!!
create a helper file(I hope u know how to create a helper file) and make a function that return an implode version. for instance.
you created a arrayconfig.php in config folder and the code inside it is
return [
'name' => 'Tag',
'types' => [
'product' => 'product',
'user' => 'user'
]
];
now in helper.php create a function
function typechecker(){
$array = config()->get('arrayConfig.types');
return implode("|",$arr);
}
you must remember you add it to composer.json file and run a command in console
composer dump-autoload
after adding helper files
what is to be added in composer.json
if you create a folder helpers in app and your file path is
app/Helpers/helpers.php
Your composer.json will be something like this
"autoload": {
"classmap": [
....
],
"files": [
"app/Helpers/helpers.php"
],
"psr-4": {
....
}
}
just concentrate on files. rest of the section is already there. I added all code to make it understandable
Final step in your route
$api->group(['prefix' => 'Tag/{type}'], function ($api) {})->where('type', typechecker());
Cheers!!!
N.B it's 100% workable. If it does not work, that means you did something wrong

Yii2 url rule for module and parameters

I am trying to configure Yii2 url manager in a manner that if a controller name is skipped in url it should call the default controller for action. I have managed to achieve this without action parameter. But got stuck when using parameters in action name.
Here is my route config:
return [
'catalog/category/<alias:[\w-]+>' => 'catalog/default/category',
'catalog/<action:\w+>' => 'catalog/default/<action>',
];
Controller File:
namespace app\modules\catalog\controllers;
use yii\base\Controller;
use app\modules\catalog\models\Categories;
class DefaultController extends Controller
{
public function actionShopbydepartment()
{
$data['categories'] = Categories::findParentSubHierarchy();
return $this->renderPartial('shopbydepartment', $data);
}
public function actionCategory($alias = null)
{
die(var_dump($alias));
$data['category'] = Categories::findCategoryBySlug($alias);
return $this->render('category', $data);
}
}
when I access the following url it loads perfectly.
http://domain.com/index.php/catalog/shopbydepartment
But when i access the below url it called the right function but did not pass the $alias value:
http://domain.com/index.php/catalog/category/appliances
UPDATE:
I have used the following approach for module wise url rules declaration:
https://stackoverflow.com/a/27959286/1232366
Here is what i have in the main config file:
'rules' => [
[
'pattern' => 'admin/<controller:\w+>/<action:[\w-]+>/<id:\d+>',
'route' => 'admin/<controller>/<action>'
],
[
'pattern' => 'admin/<module:\w+>/<controller:\w+>/<action:[\w-]+>/<id:\d+>',
'route' => 'admin/<module>/<controller>/<action>'
],
],
the admin is working fine and this is my first module so rest of the rules are mentioned already
Well just to help other fellows I have retrieve the value of $alias using the following approach:
$alias = \Yii::$app->request->get('alias');
But definitely this is not an accurate answer of the question. I still didn't know what i am doing wrong that i didn't get the value using the approach mentioned in question.
It wirk!
[
'name' => 'lang_country_seller_catalog',
'pattern' => '<lang:\w+>-<country:\w+>/seller/catalog/<module>/<controller>/<action>',
'route' => 'seller/catalog/<module>/<controller>/<action>',
],
[
'name' => 'lang_country_seller_catalog_attributes',
'pattern' => '<lang:\w+>-<country:\w+>/seller/catalog/attributes/<module>',
'route' => 'seller/catalog/attributes/<module>',
],

Override translation path of module on yii2

Suppose I installed module Foo form a repository with composer. The module structure is like this:
- Foo
|- models
|- controllers
|- views
|- messages
|- config
Messages folder of Foo contains translation files of module. Now I want override some translation strings of Foo. From Yii2 i18n Documentation I tried to use fileMap property on configuration of translation component to map bar category to bar.php (instead of reading from app\modules\Foo\messages), but it does not have any effect on translations. My i18n component configuration is:
'i18n' => [
'translations' => [
'*' => [
'class' => 'yii\i18n\PhpMessageSource',
'fileMap' => [
'bar' => 'bar.php'
],
],
],
],
How do i achieve my goal?
If you are wanting to have translations for each module contained within the module, then you need to register the translations for that module. It can't be done simply from the config file. You probably already have this in your module file,, I just include for completeness. The code is copied from the documentation, and needs to be in your module file, so in app/modules/Foo.php
<?php
namespace app\modules\foo;
use Yii;
class Module extends \yii\base\Module
{
public $controllerNamespace = 'app\modules\foo\controllers';
public function init()
{
parent::init();
/** Register custom translations for this module **/
$this->registerTranslations();
}
public function registerTranslations()
{
/**This registers translations for the Foo module **/
Yii::$app->i18n->translations['modules/foo/*'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'basePath' => '#app/modules/foo/messages',
/**Tells yii where to find the translations for validation and form categories **/
'fileMap' => [
'modules/foo/validation' => 'validation.php',
'modules/foo/form' => 'form.php',
...
],
];
}
public static function t($category, $message, $params = [], $language = null)
{
return Yii::t('modules/users/' . $category, $message, $params, $language);
}
}
In your case it doesn't look like you need to provide file mapping.You could simply use this format for your files
[[basePath]]/LanguageID/CategoryName.php
Unfortunately I can't seem to find a list of the available categories.
If you then want to override some of the module translations you will need to specify the category to be used, like this in your config file. It specifically overrides the modules/foo/bar category.
'i18n' => [
'translations' => [
'modules/foo*' => [
'class' => 'yii\i18n\PhpMessageSource',
'basePath' => '#app/messages',
],
],
],
Your translation file needs to follow a folder structure like that in the translation description, so in the above example it would be
app/messages/ [language code] /modules/foo/bar.php
Otherise, you can use fileMap to map to different locations, like if your bar.php file is in app/messages/[language code]
'fileMap' => [
'modules/foo/bar' => 'bar.php'
]

i18n and modules retrieves from wrong path in Yii2

I don't know if this is a bug or a mistake on my end but basically I followed the Yii2 documentation to setup i18n translations for modules. The following snippet is directly copy pasted from the Yii2 guide.
public function init()
{
parent::init();
$this->registerTranslations();
}
public function registerTranslations()
{
Yii::$app->i18n->translations['modules/users/*'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en',
'basePath' => '#app/modules/users/messages',
];
}
public static function t($category, $message, $params = [], $language = null)
{
return Yii::t('modules/users/' . $category, $message, $params, $language);
}
According to the guide I should call it like this:
Module::t('validation', 'your custom validation message')
However, Yii2 tries to load the the 'validation.php' from the wrong location. This is the output of the debugger:
The message file for category 'modules/users/validation' does not exist: /Applications/MAMP/htdocs/.../domains/localhost/public_html/.../backend/modules/users/messages/en/modules/users/validation.php
From what I understand, it should be looking for modules/users/message/<lang>/validation.php instead, which makes a lot more sense than what it is looking for right now.
What am I doing wrong?
Thank you in advance.
You should simply add a filemap param, e.g. :
public function registerTranslations()
{
Yii::$app->i18n->translations['modules/users/*'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en',
'basePath' => '#app/modules/users/messages',
'fileMap' => [
'modules/users/validation' => 'validation.php',
],
];
}
Read more : http://www.yiiframework.com/doc-2.0/guide-tutorial-i18n.html#translating-module-messages
EDIT : As stated in Yii2 guide, if you want to remove filemap, your validation.php file should be in modules/users/messages/[lang]/modules/users/validation.php.
Instead of configuring fileMap you can rely on convention which is to use the category name as the file name (e.g. category app/error will result in the file name app/error.php under the basePath.

Categories