Due project requirements I have to use a different version of jQuery than the one which comes with Yii2. I know I can disable it by using:
'assetManager' => [
'bundles' => [
// you can override AssetBundle configs here
'yii\web\JqueryAsset' => [
'sourcePath' => null,
'js' => []
],
],
],
Then I go to AppAsset:
class AppAsset extends AssetBundle
{
public $sourcePath = "#app/themes/v1/assets";
public $jsOptions = [ 'position' => \yii\web\View::POS_END ];
public $js = [
"js/lib/jquery/jquery-1.9.1.js",
];
}
But this brings one big issue, jquery loads after yii.validation, etc:
</script><script src="/assets/5b466ff1/yii.js?v=1465857632"></script>
<script src="/assets/5b466ff1/yii.validation.js?v=1465857632"></script>
<script src="/assets/5b466ff1/yii.activeForm.js?v=1465857632"></script>
<script src="/assets/98d185b3/js/lib/jquery/jquery-1.9.1.js?v=1466702133"></script>
My temporal solution is changing POS_END to POS_HEAD, but that affects page SEO right?, as far as I know is considered bad practice to load js on head.
So my question is:
How can I load jQuery from my theme assets, using POS_END and loading before yii.validation etc?
A possibility is to add it on web/js/jquery.js but I would prefer to keep it on the assets bundle.
Any ideas?
Update:
Adding this to AppAsset allows me to have control on the theme itself. It is cleaner than modifying the global config.
public function init()
{
parent::init();
Yii::$app->assetManager->bundles['yii\\web\\JqueryAsset'] = [
'js' => ['lib/jquery/jquery-1.9.1.js'],
'sourcePath' => '#app/themes/v1/assets/js'
];
}
Just Change Your Config Code as :
'assetManager' => [
'bundles' => [
'yii\web\JqueryAsset' => [
'js' => ['lib/jquery/jquery-1.9.1.js'],
'sourcePath' => '#app/themes/v1/assets/js'
]
]
]
No Need to Define jquery in AppAsset
class AppAsset extends AssetBundle
{
public $sourcePath = "#app/themes/v1/assets";
public $jsOptions = [ 'position' => \yii\web\View::POS_END ];
public $js = [
//"js/lib/jquery/jquery-1.9.1.js" Not Required
];
}
Related
With ZF2, it was very simple to register custom view helpers for custom form elements.
You could simply create an element like such:
use Zend\Form\Element;
class Recaptcha extends Element
{
protected $attributes = [
'type' => 'recaptcha',
];
protected $secret;
public function getSecret()
{
return $this->secret;
}
public function __construct($secret)
{
parent::__construct();
$this->secret = $secret;
}
}
Create a matching helper:
use Zend\Form\ElementInterface;
use Zend\Form\View\Helper\FormElement;
class Recaptcha extends FormElement
{
public function render(ElementInterface $element)
{
return '<div class="form-group">
<div id="register_recaptcha">
<div class="g-recaptcha" data-sitekey="' . $element->getSecret() . '"></div>
</div>
</div>
<script src="//www.google.com/recaptcha/api.js"></script>';
}
}
And then wire it up at config:
return [
'form_elements' => [
'factories' => [
Recaptcha::class => RecaptchaFactory::class,
],
],
'view_helpers' => [
'invokables' => [
'recaptcha' => RecaptchaHelper::class,
],
],
];
IIRC, you would have to wire it up in the Bootstrap too
public function onBootstrap($e)
{
$application = $e->getApplication();
$services = $application->getServiceManager();
$services->get('ViewHelperManager')->get('FormElement')->addType('recaptcha', 'recaptcha');
}
Upgrading a project from ZF2 to ZF3, the custom element now appears as a textfield.
If I call the helper directly on the field, it renders properly:
{{ recaptcha( user_form.get('recaptchafield') ) | raw }}
It's the automatic association that's seemingly vanished. Such that calling formRow on each doesn't invoke the helper.
Anyone have the quick fix? Hopeful to save myself from reviewing the actual zend-form and zend-view code.
Thank you!
I had the same issue and I resolved it by replacing
$services->get('ViewHelperManager')->get('FormElement')->addType('recaptcha', 'recaptcha');
with
$services->get('ViewHelperManager')->get('FormElement')->addClass(Recaptcha::class, RecaptchaHelper::class);
The config also needed some adaption. It now reads like this:
return [
'form_elements' => [
'factories' => [
Recaptcha::class => RecaptchaFactory::class,
],
],
'view_helpers' => [
'invokables' => [
RecaptchaHelper::class => RecaptchaHelper::class,
],
],
];
Hope that helps someone else find the issue faster ;)
Invokables no longer exist in ZF3. You need to move your recatpcha view helper to the factories key instead and wire it up to Zend\ServiceManager\Factory\InvokableFactory::class
Is there any solution to add routes from module configuration?
Example. We have main config where we describe
'components' => [
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => require(FILE_PATH_CONFIG_ENV . '_routes.php') // return array
],
]
in each module we load custom config file with private parameters
public function loadConfig($sFileName = 'main', $sFolderName = 'config')
{
Yii::configure($this, require($this->getBasePath() . DS . $sFolderName . DS . $sFileName . '.php'));
return $this;
}
Config file
return [
'components' => [
'urlManager' => [
'class' => 'yii\web\UrlManager',
'rules' => [
'/admin' => '/admin/index/index',
]
]
]
];
And now I need somehow merge current config (main for web application) with config loaded from module. In end I want describe in module config routes only for this module and use them for pretty urls (user friendly url). How can I do this one? This examples not working when I create url /admin/index/index, it shows me /admin/index/index but I want /admin as mentioned in module rules.
So I did this way (this is full answer for a question).
Create Bootstrap class special for module.
namespace app\extensions;
use yii\base\BootstrapInterface;
/**
* Class ModuleBootstrap
*
* #package app\extensions
*/
class ModuleBootstrap implements BootstrapInterface
{
/**
* #param \yii\base\Application $oApplication
*/
public function bootstrap($oApplication)
{
$aModuleList = $oApplication->getModules();
foreach ($aModuleList as $sKey => $aModule) {
if (is_array($aModule) && strpos($aModule['class'], 'app\modules') === 0) {
$sFilePathConfig = FILE_PATH_ROOT . DS . 'modules' . DS . $sKey . DS . 'config' . DS . '_routes.php';
if (file_exists($sFilePathConfig)) {
$oApplication->getUrlManager()->addRules(require($sFilePathConfig));
}
}
}
}
}
Create route file in module folder (/modules/XXX/config/_routes.php)
return [
'/sales' => '/sales/index/index',
'/sales/company' => '/sales/company/index',
'/sales/company/view/<sID:\d+>' => '/sales/company/view',
'/sales/company/update/<sID:\d+>' => '/sales/company/update',
'/sales/company/delete/<sID:\d+>' => '/sales/company/delete',
];
Add boostrap to main config file
$aConfig = [
// some code
'bootstrap' => [
// some code
'app\extensions\ModuleBootstrap',
],
'modules' => [
// some code
'sales' => ['class' => 'app\modules\sales\SalesModule']
]
]
return $aConfig;
That's it. We can define routes only in module 'route' config.
PS: I don't like detection if (is_array($aModule) && strpos($aModule['class'], 'app\modules') === 0) (I mean NOT 'debug', 'log', 'gii' or other native Yii2 modules) maybe someone can suggest better solution?
And this will be more clean and reliable. I have found this on Yii2's github repo here.
<?php
namespace backend\modules\webmasters;
use Yii;
use yii\base\BootstrapInterface;
class Module extends \yii\base\Module implements BootstrapInterface
{
public $controllerNamespace = 'backend\modules\webmasters\controllers';
public function init()
{
parent::init();
// initialize the module with the configuration loaded from config.php
Yii::configure($this, require(__DIR__ . '/config/main.php'));
}
/**
* #inheritdoc
*/
public function bootstrap($app)
{
$app->getUrlManager()->addRules([
[
'class' => 'yii\web\UrlRule',
'pattern' => $this->id,
'route' => $this->id . '/tools/index'
],
[
'class' => 'yii\web\UrlRule',
'pattern' => $this->id . '/<controller:[\w\-]+>/<action:[\w\-]+>',
'route' => $this->id . '/<controller>/<action>'
],
], false);
}
}
and just configure your main.php config file's bootstrap section as
'bootstrap' => [
'log',
'webmasters'
]
'modules' => [
'webmasters' => [
'class' => 'backend\modules\webmasters\Module'
]
]
Separating url rules for modules is mentioned in official documentation here.
And I think this is more optimum approach unlike merging and declaring all rules in one config file.
The rules are parsed in order they are declared (this is mentioned here), so with the separation you can skip other modules urls. In case of large amount of rules it can give perfomance boost.
I have a module calls API, and i want to load config file for it. The guide says that i have to use function \Yii::configure. I use it, but it doesn't apply any new configs. And i tried to use array instead config file, the result is same
class API extends \yii\base\Module
{
public $controllerNamespace = 'api\client\controllers';
public function init()
{
parent::init();
// \Yii::configure($this, require(__DIR__ . '/config/main.php'));
\yii::configure($this, [
'components' => [
'user' => [
'class' => 'yii\web\UserTest',
'identityClass' => 'api\client\models\User',
],
]
]);
echo \yii::$app->user->className();
die();
}
}
How I can override config in my module ?
UPDATE
You have to use setComponents method of Yii::$app
Yii::$app->setComponents(
[
'errorHandler'=>[
'errorAction'=>'forum/forum/error',
'class'=>'yii\web\ErrorHandler',
],
'user' => [
'class' => 'yii\web\User',
'identityClass' => 'app\modules\profile\models\User',
],
]
);
OLD ANSWER
Didn't it give you errors? Your casing are wrong and so instead of "yii" in small letters use "Yii" capitalized
class API extends \yii\base\Module
{
public $controllerNamespace = 'api\client\controllers';
public function init()
{
parent::init();
\Yii::configure($this, [
'components' => [
'user' => [
'class' => 'yii\web\UserTest',
'identityClass' => 'api\client\models\User',
],
]
]);
echo \Yii::$app->user->className();
die();
}
}
I see no reason to override the application components here. I'd use #StefanoMtangoo trick but to set the component to the Module itself instead of Yii::$app:
public function init()
{
parent::init();
$this->setComponents([
'db' => [
'class' => 'yii2tech\filedb\Connection',
'path' => '#app/builder/data',
]
]);
}
Then the tricky part is to differentiate between any app's components and your module's own components. For example if my Module had a model extending yii\db\ActiveRecord I'd override its getDB() as follow (original code here):
public static function getDb()
{
return Yii::$app->getModule('api')->get('db');
// instead of: return Yii::$app->getDb();
}
So whatever the app that is using my module has or hasn't a db component it won't matter.
I followed the instructions on yii2 documentation about using twig template engine
in config/web.php (which is included from index.php) I have:
'view' => [
'class' => 'yii\web\View',
'renderers' => [
'twig' => [
'class' => 'yii\twig\ViewRenderer',
'cachePath' => '#runtime/Twig/cache',
'options' => ['auto_reload' => true], /* Array of twig options */
'globals' => ['html' => '\yii\helpers\Html'],
],
],
],
in SiteController.php:
public function actionIndex()
{
echo $this->render('index.twig');
}
in views/site/index.twig I have some text:
But instead of seeing raw html I see template based on views/layouts/main.php with index.twig content used as variable in main layout.
It was required to set layout to false in order to skip layout processing during redner
class BaseController extends Controller
{
public $layout = false;
}
I have a widget class inside "~/common/extensions/my_widget"
It renders: "~/common/extensions/my_widget/views/index"
public function run()
{
echo $this->render( '#common/extensions/my_widget/views/index');
}
When i make a new application in "~/myapplication/"
how is it possible to override the widget view for only "myapplication" and if there is no view found, use the default view.
~ is my basefolder
Inside config, add your view:
'components' => [
...
'view'=> [
'theme' => [
'pathMap' => [
'#common/extensions/my_widget/views' => [
'#myapplication/views/widgets/my_widget/views', // Override
'#common/extensions/my_widget/views', // Default
],
],
],
]
],