Yii 2で開発するための便利なトリック

誰かに役立つかもしれない「ヒントとコツ」シリーズからいくつかのクラスとスニペットを集めました。

内容:

-1つのグリッド列の複数の属性

- アクティブなメニュー項目のナビゲーションを修正

- テーブルを他の名前にマッピングする

- 何も変更されない場合、TimestampBehaviorがupdated_atプロパティを更新するのはなぜですか

- ブートストラップDateTimePicker-インターフェースに表示し、サーバーに値を送信するための2つの異なる形式

-DateTimePickerを持つフィールドのユーザータイムゾーンアカウンティング





最初に、1つの製品モデルで単純なCRUDアプリケーションを作成します。











1つのグリッド列の複数の属性



「Created At」列と「Updated At」列を1つに結合してスペースを節約したいが、同時に列の構成を保存して並べ替えたいとします。 これを行うには、通常の「DataColumn」から継承された結合列に個別の小さなクラスを作成し、構成で指定します。



CombinedDataColumn
// common/components/grid/CombinedDataColumn.php namespace common\components\grid; use yii\grid\DataColumn; /** * Renders several attributes in one grid column */ class CombinedDataColumn extends DataColumn { /* @var $labelTemplate string */ public $labelTemplate = null; /* @var $valueTemplate string */ public $valueTemplate = null; /* @var $attributes string[] | null */ public $attributes = null; /* @var $formats string[] | null */ public $formats = null; /* @var $values string[] | null */ public $values = null; /* @var $labels string[] | null */ public $labels = null; /* @var $sortLinksOptions string[] | null */ public $sortLinksOptions = null; /** * Sets parent object parameters for current attribute * @param $key string Key of current attribute * @param $attribute string Current attribute */ protected function setParameters($key, $attribute) { list($attribute, $format) = array_pad(explode(':', $attribute), 2, null); $this->attribute = $attribute; if (isset($format)) { $this->format = $format; } else if (isset($this->formats[$key])) { $this->format = $this->formats[$key]; } else { $this->format = null; } if (isset($this->labels[$key])) { $this->label = $this->labels[$key]; } else { $this->label = null; } if (isset($this->sortLinksOptions[$key])) { $this->sortLinkOptions = $this->sortLinksOptions[$key]; } else { $this->sortLinkOptions = []; } if (isset($this->values[$key])) { $this->value = $this->values[$key]; } else { $this->value = null; } } /** * Sets parent object parameters and calls parent method for each attribute, then renders combined cell content * @inheritdoc */ protected function renderHeaderCellContent() { if (!is_array($this->attributes)) { return parent::renderHeaderCellContent(); } $labels = []; foreach ($this->attributes as $i => $attribute) { $this->setParameters($i, $attribute); $labels['{'.$i.'}'] = parent::renderHeaderCellContent(); } if ($this->labelTemplate === null) { return implode('<br>', $labels); } else { return strtr($this->labelTemplate, $labels); } } /** * Sets parent object parameters and calls parent method for each attribute, then renders combined cell content * @inheritdoc */ protected function renderDataCellContent($model, $key, $index) { if (!is_array($this->attributes)) { return parent::renderDataCellContent($model, $key, $index); } $values = []; foreach ($this->attributes as $i => $attribute) { $this->setParameters($i, $attribute); $values['{'.$i.'}'] = parent::renderDataCellContent($model, $key, $index); } if ($this->valueTemplate === null) { return implode('<br>', $values); } else { return strtr($this->valueTemplate, $values); } } }
      
      









 // frontend/views/product/index.php GridView::widget([ 'dataProvider' => $dataProvider, 'columns' => [ ['class' => 'yii\grid\SerialColumn'], 'id', 'name', [ 'class' => 'common\components\grid\CombinedDataColumn', 'labelTemplate' => '{0} / {1}', 'valueTemplate' => '{0} / {1}', 'labels' => [ 'Created At', '[ Updated At ]', ], 'attributes' => [ 'created_at:datetime', 'updated_at:html', ], 'values' => [ null, function ($model, $_key, $_index, $_column) { return '[ ' . Yii::$app->formatter->asDatetime($model->updated_at) . ' ]'; }, ], 'sortLinksOptions' => [ ['class' => 'text-nowrap'], null, ], ], ['class' => 'yii\grid\ActionColumn'], ], ]);
      
      










ID DESCでデフォルトのソートを行いましょう。



 // frontend/models/ProductSearch.php public function search($params) { ... if (empty($dataProvider->sort->getAttributeOrders())) { $dataProvider->query->orderBy(['id' => SORT_DESC]); } ... }
      
      





$dataProvider->sort->defaultOrder



を介して実行すると、グリッドの列名にソートアイコンが追加されます。







アクティブなメニュー項目のナビゲーションを修正



ユーザー管理を追加します。 「dektrium / yii2-user」モジュールをインストールし、移行を適用し、管理者ユーザー(パスワード123456を使用)を追加し、「layouts / main.php」のメニューのリンクを修正します。 「/ user / login」ページに移動します。 メニューのログインリンクは非アクティブです。



 // frontend/views/layouts/main.php if (Yii::$app->user->isGuest) { $menuItems[] = ['label' => 'Login', 'url' => ['/user/login']]; }
      
      









これは、モジュールが独自のルーティングルールを追加するためです。 このようなリンクをアクティブにするには、これらのルールの適用後に取得される結果のURLを指定する必要があります(この場合、「/ user / security / login」)。 なぜ美しいURLのルートが必要なのですか?



common\components\bootstrap\Nav



クラスをyii\bootstrap\Nav



から継承し、その中のisItemActive()



メソッドを再定義して、 Yii::$app->request->getUrl()



いくつかの一致テストを追加しYii::$app->request->getUrl()



。 「use」セクションの「layouts / main.php」で、クラスを示します。



ナビゲーション
 // common/components/bootstrap/Nav.php namespace common\components\bootstrap; use Yii; use yii\bootstrap\Nav as YiiBootstrapNav; /** * @inheritdoc */ class Nav extends YiiBootstrapNav { /** * Adds additional check - directly compare item URL and request URL. * Used to make an item active when item URL is handled by module routing * * @inheritdoc */ protected function isItemActive($item) { if (parent::isItemActive($item)) { return true; } if (!isset($item['url'])) { return false; } $route = null; $itemUrl = $item['url']; if (is_array($itemUrl) && isset($itemUrl[0])) { $route = $itemUrl[0]; if ($route[0] !== '/' && Yii::$app->controller) { $route = Yii::$app->controller->module->getUniqueId() . '/' . $route; } } else { $route = $itemUrl; } $requestUrl = Yii::$app->request->getUrl(); $isActive = ($route === $requestUrl || (Yii::$app->homeUrl . $route) === '/' . $requestUrl); return $isActive; } }
      
      

















TimestampBehaviorを製品モデルに追加します



 // common/models/Product.php public function behaviors() { return [ 'TimestampBehavior' => [ 'class' => \yii\behaviors\TimestampBehavior::className(), 'value' => function () { return date('Ymd H:i:s'); }, ], ]; }
      
      







これまでのところ、すべてが正常に機能しています。 これに戻ります。







テーブルを他の名前にマッピングする



アプリケーションをもう少し複雑にしましょう。 アクセス制御のために、データベースとRBACにセッションストレージを追加します。

また、列「user_id」と「category_id」を「product」テーブルに追加します。



チーム
 php yii migrate --migrationPath=@vendor/yiisoft/yii2/web/migrations php yii migrate --migrationPath=@yii/rbac/migrations php yii migrate
      
      







移行
 // product_user $this->addColumn('{{%product}}', 'user_id', $this->integer()->after('id') ); $this->addForeignKey('fk_product_user', '{{%product}}', 'user_id', '{{%user}}', 'id'); // product_category $this->createTable('{{%category}}', [ 'id' => $this->primaryKey(), 'name' => $this->string(100), ]); $this->addColumn('{{%product}}', 'category_id', $this->integer()->after('user_id') ); $this->addForeignKey('fk_product_category', '{{%product}}', 'category_id', '{{%category}}', 'id');
      
      









データベースを見てみましょう







たくさんのテーブルがあるので、すぐにそれがどこから来たのか、どのテーブルがアプリケーションに関係し、どのテーブルがセカンダリモジュールに属しているのかがわかりません。 名前を変更するとどうなりますか? また、コード内の何も変更しないことをお勧めします。







これを行うには、 getRawTableName()



メソッドとgetRawTableName()



メソッドをオーバーライドする、標準のものから継承されたデータベース接続とスキームを操作するための独自のクラスを作成する必要があります。 結合クラスには、新しいプロパティ$tableMap



があります。このプロパティでは、アプリケーションで使用されるテーブルの内部名とデータベースで使用される実際のテーブルの間の対応を設定できます。



接続
 // common/components/db/Connection.php namespace common\components\db; use Yii; use yii\db\Connection as BaseConnection; /** * Allows to add mapping between internal table name used in application and real table name * Can be used to set different prefixes for tables from different modules, just to group them in DB */ class Connection extends BaseConnection { /** * @var array Mapping between internal table name used in application and real table name * Can be used to add different prefixes for tables from different modules * Example: 'tableMap' => ['%session' => '%__web__session'] */ public $tableMap = []; /** * @inheritdoc */ public function quoteSql($sql) { return preg_replace_callback( '/(\\{\\{(%?[\w\-\. ]+%?)\\}\\}|\\[\\[([\w\-\. ]+)\\]\\])/', function ($matches) { if (isset($matches[3])) { return $this->quoteColumnName($matches[3]); } else { return $this->getRealTableName($matches[2]); } }, $sql ); } /** * Returns real table name which is used in database * @param $tableName string * @param $useMapping bool */ public function getRealTableName($tableName, $useMapping = true) { $tableName = ($useMapping && isset($this->tableMap[$tableName]) ? $this->tableMap[$tableName] : $tableName); $tableName = str_replace('%', $this->tablePrefix, $this->quoteTableName($tableName)); return $tableName; } }
      
      







mysql /スキーマ
 // common/components/db/mysql/Schema.php namespace common\components\db\mysql; use yii\db\mysql\Schema as BaseSchema; /** * @inheritdoc */ class Schema extends BaseSchema { /** * @inheritdoc * Also gets real table name from database connection object before replacing table prefix */ public function getRawTableName($name) { if (strpos($name, '{{') !== false) { $name = preg_replace('/\\{\\{(.*?)\\}\\}/', '\1', $name); $name = $this->db->getRealTableName($name); return $name; } else { return $name; } } }
      
      









 // common/config/main.php 'components' => [ ... 'db' => [ 'class' => 'common\components\db\Connection', 'schemaMap' => [ 'mysql' => 'common\components\db\mysql\Schema', ], 'tableMap' => [ '%migration' => '%__db__migration', '%session' => '%__web__session', '%auth_assignment' => '%__rbac__auth_assignment', '%auth_item' => '%__rbac__auth_item', '%auth_item_child' => '%__rbac__auth_item_child', '%auth_rule' => '%__rbac__auth_rule', '%user' => '%__user__user', '%profile' => '%__user__profile', '%token' => '%__user__token', '%social_account' => '%__user__social_account', ], ], ... ],
      
      





テーブル「__user__user」の名前は少し奇妙に見えますが、ここではわかりやすくするために名前を変更することはできません



設定が「config / main.php」ファイルで指定されている場合、 'class' => 'yii\db\Connection'



を「config / main-local.php」から削除する必要があります。後で接続されるため、このパラメーターは再定義。 または、「config / main-local.php」で設定全体を設定します。 おそらく、この方法の方がより良いでしょう。開発中は明確な名前がありますが、本番では普通です。



移行によってテーブルの名前を変更する必要はありません。 このような設定でプロジェクトを複製し、移行を開始すると、新しい名前でテーブルが作成されます。 移行時に移行テーブル自体の名前を変更することも非常に困難です。 タンバリンと一緒にテーブルをコピーして新しい名前でその存在をチェックすることで踊ることができますが、これはほとんど正当化されません。







何も変更されない場合、TimestampBehaviorがupdated_atプロパティを更新する理由



製品の編集に入り、ユーザーとカテゴリを設定します。 「更新日時」プロパティが更新されていることに注意してください。 ここで再び編集を開始し、何も変更せずに「保存」をクリックします。 Updated Atプロパティが再び更新されました。 それはそのようであってはなりません。



これは、「user_id」と「category_id」を追加したために発生しました。

チェーンは次のとおりです。 フォームをPOSTリクエストで送信します。 もちろん、その中のデータは文字列形式です。 $model->load(Yii::$app->request->post())



がサーバーで呼び出されます。 たとえば、プロパティuser_id = "1" (string)



ます。

次に、 $model->save()



が呼び出され、 TimestampBehavior



extends AttributeBehavior



extends AttributeBehavior



)が起動します。



AttributeBehavior.php

 public function evaluateAttributes($event) { ... && empty($this->owner->dirtyAttributes) ... }
      
      





BaseActiveRecord.php

 public function getDirtyAttributes($names = null) { ... || $value !== $this->_oldAttributes[$name]) ... }
      
      







$this->_oldAttributes[$name]



データベースからロードされるため、 $this->_oldAttributes['user_id'] = 1 (int)



ます。 厳密な比較はfalseを返し、プロパティは変更されたと見なされます。





これを修正するには、rules()メソッドに値フィルタリングを追加する必要があります。



nullではないプロパティの場合、すべてが非常に単純であり、intにキャストします。 nullの可能性があるプロパティの場合、コールバックを記述する必要があります。 アプリケーションでは、2番目のオプション。



 // not null [['user_id', 'category_id'], 'filter', 'filter' => 'intval'], // null [['user_id', 'category_id'], 'filter', 'filter' => function ($value) { return ($value === '' ? null : (int)$value); }],
      
      











Bootstrap DateTimePicker-インターフェイスに表示するためとサーバーに値を送信するための2つの異なる形式



フィルターcreated_from, created_to, updated_from, updated_to



を追加します。 日付/時刻については、通常、Bootstrap Datepicker / Datetimepickerにkartikウィジェットを使用します







ただし、1つの問題があります。値の表示と保存に異なる形式を設定することはできません。 その結果、「14 junio 2016、mar。」のようなものをサーバーに送信できます。 これを修正するには、新しい形式の非表示フィールドをレンダリングに追加します。 Datetimepickerでは、linkFieldオプションとlinkFormatオプションを設定できます。Datepickerでは、changeDateイベントをキャッチして手動でフォーマットする必要があります。 また、値のクリアボタンをクリックすることも処理する必要があります。



デイトピッカー
 // common/widgets/DatePicker.php namespace common\widgets; use Yii; use yii\helpers\Html; use yii\helpers\FormatConverter; use yii\base\InvalidParamException; /** * Extended DatePicker, allows to set different formats for sending and displaying value */ class DatePicker extends \kartik\date\DatePicker { public $saveDateFormat = 'php:Ym-d'; private $savedValueInputID = ''; private $attributeValue = null; public function __construct($config = []) { $defaultOptions = [ 'type' => static::TYPE_COMPONENT_APPEND, 'convertFormat' => true, 'pluginOptions' => [ 'autoclose' => true, 'format' => Yii::$app->formatter->dateFormat, ], ]; $config = array_replace_recursive($defaultOptions, $config); parent::__construct($config); } public function init() { if ($this->hasModel()) { $model = $this->model; $attribute = $this->attribute; $value = $model->$attribute; $this->model = null; $this->attribute = null; $this->name = Html::getInputName($model, $attribute); $this->attributeValue = $value; if ($value) { try { $this->value = Yii::$app->formatter->asDateTime($value, $this->pluginOptions['format']); } catch (InvalidParamException $e) { $this->value = null; } } } return parent::init(); } protected function parseMarkup($input) { $res = parent::parseMarkup($input); $res .= $this->renderSavedValueInput(); $this->registerScript(); return $res; } protected function renderSavedValueInput() { $value = $this->attributeValue; if ($value !== null && $value !== '') { // format value according to saveDateFormat try { $value = Yii::$app->formatter->asDate($value, $this->saveDateFormat); } catch(InvalidParamException $e) { // ignore exception and keep original value if it is not a valid date } } $this->savedValueInputID = $this->options['id'].'-saved-value'; $options = $this->options; $options['id'] = $this->savedValueInputID; $options['value'] = $value; // render hidden input if ($this->hasModel()) { $contents = Html::activeHiddenInput($this->model, $this->attribute, $options); } else { $contents = Html::hiddenInput($this->name, $value, $options); } return $contents; } protected function registerScript() { $language = $this->language ? $this->language : Yii::$app->language; $format = $this->saveDateFormat; $format = strncmp($format, 'php:', 4) === 0 ? substr($format, 4) : FormatConverter::convertDateIcuToPhp($format, $type); $saveDateFormatJs = static::convertDateFormat($format); $containerID = $this->options['data-datepicker-source']; $hiddenInputID = $this->savedValueInputID; $script = " $('#{$containerID}').on('changeDate', function(e) { var savedValue = e.format(0, '{$saveDateFormatJs}'); $('#{$hiddenInputID}').val(savedValue).trigger('change'); }).on('clearDate', function(e) { var savedValue = e.format(0, '{$saveDateFormatJs}'); $('#{$hiddenInputID}').val(savedValue).trigger('change'); }); $('#{$containerID}').data('datepicker').update(); $('#{$containerID}').data('datepicker')._trigger('changeDate'); "; $view = $this->getView(); $view->registerJs($script); } }
      
      







Datetimepicker
 // common/widgets/DateTimePicker.php namespace common\widgets; use Yii; use yii\helpers\Html; use yii\helpers\FormatConverter; use yii\base\InvalidParamException; /** * Extended DateTimePicker, allows to set different formats for sending and displaying value */ class DateTimePicker extends \kartik\datetime\DateTimePicker { public $saveDateFormat = 'php:Ymd H:i'; public $removeButtonSelector = '.kv-date-remove'; private $savedValueInputID = ''; private $attributeValue = null; public function __construct($config = []) { $defaultOptions = [ 'type' => static::TYPE_COMPONENT_APPEND, 'convertFormat' => true, 'pluginOptions' => [ 'autoclose' => true, 'format' => Yii::$app->formatter->datetimeFormat, 'pickerPosition' => 'top-left', ], ]; $config = array_replace_recursive($defaultOptions, $config); parent::__construct($config); } public function init() { if ($this->hasModel()) { $model = $this->model; $attribute = $this->attribute; $value = $model->$attribute; $this->model = null; $this->attribute = null; $this->name = Html::getInputName($model, $attribute); $this->attributeValue = $value; if ($value) { try { $this->value = Yii::$app->formatter->asDateTime($value, $this->pluginOptions['format']); } catch (InvalidParamException $e) { $this->value = null; } } } return parent::init(); } public function registerAssets() { $format = $this->saveDateFormat; $format = strncmp($format, 'php:', 4) === 0 ? substr($format, 4) : FormatConverter::convertDateIcuToPhp($format, $type); $saveDateFormatJs = static::convertDateFormat($format); $this->savedValueInputID = $this->options['id'].'-saved-value'; $this->pluginOptions['linkField'] = $this->savedValueInputID; $this->pluginOptions['linkFormat'] = $saveDateFormatJs; return parent::registerAssets(); } protected function parseMarkup($input) { $res = parent::parseMarkup($input); $res .= $this->renderSavedValueInput(); $this->registerScript(); return $res; } protected function renderSavedValueInput() { $value = $this->attributeValue; if ($value !== null && $value !== '') { // format value according to saveDateFormat try { $value = Yii::$app->formatter->asDateTime($value, $this->saveDateFormat); } catch(InvalidParamException $e) { // ignore exception and keep original value if it is not a valid date } } $options = $this->options; $options['id'] = $this->savedValueInputID; $options['value'] = $value; // render hidden input if ($this->hasModel()) { $contents = Html::activeHiddenInput($this->model, $this->attribute, $options); } else { $contents = Html::hiddenInput($this->name, $value, $options); } return $contents; } protected function registerScript() { $containerID = $this->options['id'] . '-datetime'; $hiddenInputID = $this->savedValueInputID; if ($this->removeButtonSelector) { $script = " $('#{$containerID}').find('{$this->removeButtonSelector}').on('click', function(e) { $('#{$containerID}').find('input').val('').trigger('change'); $('#{$containerID}').data('datetimepicker').reset(); $('#{$containerID}').trigger('changeDate', { type: 'changeDate', date: null, }); }); $('#{$containerID}').trigger('changeDate', { type: 'changeDate', date: null, }); "; $view = $this->getView(); $view->registerJs($script); } } }
      
      









 // frontend/views/product/_search.php <?= $form->field($model, 'created_from')->widget(\common\widgets\DateTimePicker::classname()) ?>
      
      







クリアボタンを定型化して、見栄えを良くすることもできます。



main.css
 .input-group.date .kv-date-remove, .input-group.date .kv-date-calendar { color: #626262; } .input-group.date .kv-date-remove-custom { position: absolute; z-index: 3; color: #000; opacity: 0.4; font-size: 16px; font-weight: 700; line-height: 0.6; right: 50px; top: 14px; cursor: pointer; } .input-group.date .kv-date-remove-custom:hover { opacity: 0.6; } .input-group.date input { padding-right: 30px; } .input-group.date .input-group-addon.kv-date-calendar + .kv-date-remove-custom { left: 50px; right: auto; } .input-group.date .input-group-addon.kv-date-calendar + .kv-date-remove-custom + input { padding-left: 32px; }
      
      







_search.php
 <?php // frontend/views/product/_search.php $dateTimePickerOptions = [ 'removeButton' => '<span class="kv-date-remove kv-date-remove-custom">×</span>', 'removeButtonSelector' => '.kv-date-remove-custom', 'pluginEvents' => [ 'changeDate' => "function(e) { var isEmpty = ($(this).find('input').val() == ''); $(this).find('.kv-date-remove-custom').toggle(!isEmpty); }", ], ]; ?> <?= $form->field($model, 'created_from')->widget(DateTimePicker::classname(), $dateTimePickerOptions) ?>
      
      













ところで、多くのデートピッカーがローカリゼーションをサポートしているという事実に少し驚いていますが、異なる形式で値を表示して送信する方法はありません。 私の意見では、datepickerはselectタグの直接的な類似物です。 テキストをselectで表示し、オプション値を送信し、datepickerで日付を美しくわかりやすい形式で表示し、テクニカルで送信します。



同じkartikにはモジュールyii2-datecontrolがあり、このモジュールで別の保存形式を指定できます。 しかし、デフォルトでは表示されたテキストをサーバーに送信し、そこで解析し、保存するために指定された形式でフォーマットしてから送信するため、私はそれが好きではありませんでした。 クライアントで書式設定を設定できますが、一般的にはやや面倒で、設定する理由はありません。日付をYYYY-mm-ddで書式設定するだけです。







DateTimePickerを持つフィールドのユーザータイムゾーンアカウンティング



そのため、日付によるフィルターがあります。 ここで、異なるタイムゾーンのユーザーがいることを想像してください。 サーバーとベースはUTCです。 出力のフォーマットはフォーマッターの設定によって決まりますが、入力をどうすればよいですか? フィルターのユーザーは、グリッドデータに表示される予定の時間を設定します。 解決策は簡単です。フォームを読み込んだ後、ユーザーのタイムゾーンからサーバーのタイムゾーンに時間とともにフィールド値を変換する必要があります。 したがって、アプリケーション内では、時刻は常にUTCになります。



InputTimezoneConverter
 // common/components/InputTimezoneConverter.php namespace common\components; use Yii; use yii\i18n\Formatter; /** * Allows to convert time values in user timezone (usually from input fields) * into appplication timezone which is used in models * Conversion from application timezone into user timezone * is usulally done by Yii::$app->formatter->asDatetime() */ class InputTimezoneConverter { /** @var Formatter */ private $formatter = null; public function __construct($formatter = null) { if ($formatter === null) { // we change formatter configuration so we need to clone it $formatter = clone(Yii::$app->formatter); } $this->formatter = $formatter; $this->formatter->datetimeFormat = 'php:Ymd H:i:s'; // swap timeZone and defaultTimeZone of default formatter configuration // to perform conversion back to default timezone $timeZone = $this->formatter->timeZone; $this->formatter->timeZone = $this->formatter->defaultTimeZone; $this->formatter->defaultTimeZone = $timeZone; } /** * @param $value string */ public function convertValue($value) { if ($value === null || $value === '') { return $value; } return $this->formatter->asDatetime($value); } }
      
      









 // common/config/main.php return [ 'timeZone' => 'UTC', ... 'components' => [ ... 'formatter' => [ 'dateFormat' => 'php:md-Y', 'datetimeFormat' => 'php:mdY H:i', 'timeZone' => 'Europe/Moscow', 'defaultTimeZone' => 'UTC', ], ... ], ... ];
      
      







 // frontend/models/ProductSearch.php /** * @inheritdoc * Additionally converts attributes containing time from user timezone to application timezone */ public function load($data, $formName = NULL) { $loaded = parent::load($data, $formName); if ($loaded) { $timeAttributes = ['created_from', 'created_to', 'updated_from', 'updated_to']; $inputTimezoneConverter = new \common\components\InputTimezoneConverter(); foreach ($timeAttributes as $attribute) { $this->$attribute = $inputTimezoneConverter->convertValue($this->$attribute); } } }
      
      















JavaScriptコードのウィジェット



ビューファイルにjavascriptコードを記述する必要がある場合があります。 もちろん、jsファイルに書き込む方が良いですが、別のケースがあります。 多くの場合、彼らはそれを文字列で書き、registerJs()を通して登録して、ドキュメントの最後に残りのスクリプトとともに表示します。 ただし、すべての編集者が行にハイライトを表示するわけではなく、引用符に問題がある可能性があり、行がないと中央に表示されます。 begin()



end()



呼び出しの間にコンテンツを取得し、タグを削除してregisterJs()



を呼び出すウィジェットを作成できbegin()



デフォルトでは\yii\web\View::POS_READY



)。



スクリプト
 // common/widgets/Script.php namespace common\widgets; use Yii; use yii\web\View; /** * Allows to write javascript in view inside '<script></script>' tags and render it at the end of body together with other scripts * '<script></script>' tags are removed from result output */ class Script extends \yii\base\Widget { /** @var string Script position, used in registerJs() function */ public $position = View::POS_READY; /** * @inheritdoc */ public function init() { parent::init(); ob_start(); } /** * @inheritdoc */ public function run() { $script = ob_get_clean(); $script = preg_replace('|^\s*<script>|ui', '', $script); $script = preg_replace('|</script>\s*$|ui', '', $script); $this->getView()->registerJs($script, $this->position); } }
      
      









 <?php // frontend/views/product/_form.php use common\widgets\Script; ?> <?php Script::begin(); ?> <script> console.log('Product form: $(document).ready()'); </script> <?php Script::end(); ?>
      
      







ご注意



また、1つのドメイン(たとえば、ホスティング)に高度なアプリケーションを配置する方法に関するドキュメントへのリンクも残します。 「yii2高度な単一ドメイン」のリクエストに応じてGoogleは、Apache設定を使用した例を提供していますが、実際には、すべてがはるかに単純です。 そして、正しいリンクのために、「yii2高度な共有ホスティング」を入力することを推測する必要があります。 つまり、「backend / web」フォルダーを「frontend / web / admin」フォルダーに移動し、「index.php」のパスを編集する必要があります。



すべての例は、別々のコミットでgithubで表示できます。






All Articles