HTML、PNG、SVG出力形式をサポートする汎用性の高いQRコード生成ライブラリです。
Duna QRコードライブラリは、元々はv8.0までDunaにバンドルされていたQrCode
ライブラリ(Laurent Minguet氏開発)をベースとしたQRコード生成ツールです。LGPLライセンスで配布されており、QRコード生成のための柔軟でオープンソースのソリューションを提供します。
ライブラリのインストールには、Composerを使用します。
$ composer require duna/qrcode
Duna QRコードライブラリの使用方法に関する簡単なガイドです。
まず、必要なクラスを含めて、QRコードのインスタンスを作成します。
<?php
use Duna\Helpers\QrCode\QrCode;
use Duna\Helpers\QrCode\Output;
$qrCode = new QrCode('Lorem ipsum dolor sit amet');
寸法と色を指定してQRコードのPNG画像を生成するには、以下を使用します。
// Create PNG output
$output = new Output\Png();
// Generate PNG data with a specified width, background color (white), and foreground color (black)
$data = $output->output($qrCode, 100, [255, 255, 255], [0, 0, 0]);
// Save the PNG data to a file
file_put_contents('file.png', $data);
スケーラブルベクターグラフィックスに便利なSVG出力の場合。
// Create SVG output
$output = new Output\Svg();
// Generate SVG data with a specified width, background color (white), and foreground color (black)
echo $output->output($qrCode, 100, 'white', 'black');
QRコードをHTMLテーブルとして表示するには。
// Create HTML output
$output = new Output\Html();
// Generate HTML table representation of the QR code
echo $output->output($qrCode);
このライブラリはGNU Lesser General Public License (LGPL) v3.0に基づいて提供されています。詳細はLICENSEファイルを参照してください。
コントリビューションは大歓迎です!詳細は、CONTRIBUTINGガイドラインを参照してください。
問題やサポートについては、イシュートラッカーを参照するか、コミュニティにご連絡ください。
]]>Yii HTMLパッケージのバージョン3.7がリリースされました。いくつかの改善が含まれています。
Script::nonce()
とScript::getNonce()
を追加しました。Select
タグにバックエンド列挙値のサポートを追加しました。Yii Hydratorパッケージのバージョン1.5がリリースされました。新バージョンに含まれる改善点のリストを以下に示します。
EnumTypeCaster
を追加しました。Yii Validatorパッケージのバージョン2.1がリリースされました。新バージョンに含まれる変更点のリストを以下に示します。
getRules()
メソッドで提供されるルールとマージします。Ip
ルールでYiisoft\NetworkUtilities\IpRanges
を使用:getIpRanges()
メソッドを追加し、getRanges()
、getNetworks()
、isAllowed()
メソッドを非推奨とします。network-utilities
パッケージのNEGATION_CHARACTER
定数をIpHandler
で使用します。Yii2アプリケーションに個別のログインを使用する方法を示す複数のブログがありますが、この記事では、すべてのYii2 Advanced、Yii2 Basic、アプリケーションに対して単一のログイン画面を使用する方法を示します。これは、異なるサーバーまたは同じサーバー上のドメインでも機能します。
これを達成するために従う必要がある手順をいくつか示します。
1. Advancedテンプレートの場合
ステップ1:コンポーネント内に追加します
/path/common/config/main.php
'components' => [
'user' => [
'identityClass' => 'common\models\User',
'enableAutoLogin' => true,
'identityCookie' => ['name' => '_identity', 'httpOnly' => true],
],
'request' => [
'csrfParam' => '_csrf',
],
],
ステップ2:main-local.phpにSessionとRequestを追加します
/path/common/config/main-local.php
'components' => [
'session' => [
'cookieParams' => [
'path' => '/',
'domain' => ".example.com",
],
],
'user' => [
'identityCookie' => [
'name' => '_identity',
'path' => '/',
'domain' => ".example.com",
],
],
'request' => [
'csrfCookie' => [
'name' => '_csrf',
'path' => '/',
'domain' => ".example.com",
],
],
],
注:**example.com**はメインドメインです。他のすべてのドメインは、このサブドメインである必要があります。
ステップ3:すべてのアプリケーションで同じ検証キーを更新します
/path/frontend/config/main-local.php
/path/backend/config/main-local.php
'components' => [
'request' => [
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey' => 'fFUeb5HDj2P-1a1FTIqya8qOE',
],
],
**注:**フロントエンドとバックエンドアプリケーションのmain.phpからSessionとRequestキーを削除します。
ステップ4:コンソールアプリケーションも持っていることに注意してください。そのため、コンソールアプリケーションのmain-local.phpにsession、user、requestを更新します。
/path/console/config/main-local.php
'components' => [
'session' => null,
'user' => null,
'request' => null,
]
2. Basicテンプレートの場合
さらに、別のプロジェクトにBasicテンプレートがインストールされていて、そのテンプレートにも同じログインを使用したい場合。これを達成するには、次の手順に従います。
ステップ1:Basicテンプレートのmain-local.phpを更新します
/path/basic-app/config/main-local.php
'components' => [
'session' => [
'cookieParams' => [
'path' => '/',
'domain' => ".example.com",
],
],
'user' => [
'identityCookie' => [
'name' => '_identity',
'path' => '/',
'domain' => ".example.com",
],
],
'request' => [
'csrfCookie' => [
'name' => '_csrf',
'path' => '/',
'domain' => ".example.com",
],
],
],
すべてのドメインとサブドメイン、またはリポジトリで単一のログインを使用する方法を理解していただければ幸いです。
:) ご覧いただきありがとうございます。
]]>Yii Swaggerパッケージのバージョン2.1がリリースされました。新バージョンに含まれる変更点のリストを以下に示します。
^2.0
のpsr/http-message
のサポートを追加しました。yiisoft/yii-view
のバージョンを^7.1
に上げました。\Yiisoft\Swagger\Action\SwaggerJson
と\Yiisoft\Swagger\Action\SwaggerUi
アクションを追加し、\Yiisoft\Swagger\Middleware\SwaggerJson
と\Yiisoft\Swagger\Middleware\SwaggerUi
クラスを非推奨としました(次のメジャーバージョンで削除されます)。swagger-api/swagger-ui
のサポートを追加しました。Yii Network Utilitiesパッケージのバージョン1.2がリリースされました。新バージョンに含まれる変更点のリストを以下に示します。
IP_PATTERN
とIP_REGEXP
定数をIpHelper
に追加しました。NEGATION_CHARACTER
定数をIpRanges
に追加しました。isIpv4()
、isIpv6()
、isIp()
メソッドをIpHelper
に追加しました。Yii Form Modelパッケージの最初のリリースが行われました。フォームモデルの基盤を提供し、それらの入力、検証、表示を支援します。
使用方法については、フォームモデルを定義します。
use Yiisoft\FormModel\Attribute\Safe;
use Yiisoft\FormModel\FormModel;
use Yiisoft\Validator\Rule\Email;
use Yiisoft\Validator\Rule\Length;
use Yiisoft\Validator\Rule\Required;
final class LoginForm extends FormModel
{
#[Label('Your login')]
#[Required]
#[Length(min: 4, max: 40, skipOnEmpty: true)]
#[Email(skipOnEmpty: true)]
private ?string $login = null;
#[Label('Your password')]
#[Required]
#[Length(min: 8, skipOnEmpty: true)]
private ?string $password = null;
#[Label('Remember me for 1 week')]
#[Safe]
private bool $rememberMe = false;
}
データを入力し、フォームハイドレーターを使用して検証します。
use Psr\Http\Message\RequestInterface;
use Yiisoft\FormModel\FormHydrator;
use Yiisoft\FormModel\FormModel;
final class AuthController
{
public function login(RequestInterface $request, FormHydrator $formHydrator): ResponseInterface
{
$formModel = new LoginForm();
$errors = [];
if ($formHydrator->populateFromPostAndValidate($formModel, $request)) {
$errors = $formModel->getValidationResult()->getErrorMessagesIndexedByProperty();
}
// You can pass $formModel and $errors to the view now.
}
}
ビューのフィールドを使用して表示します。
use Yiisoft\FormModel\Field;
use Yiisoft\FormModel\FormModel;
echo Field::text($formModel, 'login');
echo Field::password($formModel, 'password');
echo Field::checkbox($formModel, 'rememberMe');
// ...
]]>Yii Formパッケージの最初のリリースが行われました。これは、HTMLフォームの動的なサーバー側生成を支援するためのウィジェットのセットを提供します。次のウィジェットがすぐに利用できます。
Checkbox
、CheckboxList
、Date
、DateTimeLocal
、Email
、File
、Hidden
、Image
、Number
、Password
、RadioList
、Range
、Select
、Telephone
、Text
、Textarea
、Time
、Url
;Button
、ResetButton
、SubmitButton
;ButtonGroup
、Fieldset
。ErrorSummary
。一般的な使用方法
use Yiisoft\Form\PureField\Field;
echo Field::text('firstName', theme: 'horizontal')
->label('First Name')
->autofocus();
echo Field::text('lastName', theme: 'horizontal')
->label('Last Name');
echo Field::select('sex')
->label('Sex')
->optionsData(['m' => 'Male', 'f' => 'Female'])
->prompt('—');
echo Field::number('age')
->label('Age')
->hint('Please enter your age.');
echo Field::submitButton('Submit')
->buttonClass('primary');
]]>Yii Hydratorパッケージのバージョン1.4がリリースされました。新バージョンに含まれる改善点のリストを以下に示します。
ToArrayOfStrings
パラメーター属性を追加しました。Collection
にバックエンド列挙値のサポートを追加しました。Yii HTMLパッケージのバージョン3.6がリリースされました。いくつかの改善と修正が含まれています。
Html::renderAttribute()
でInvalidArgumentException
をスローします。Stringable
と配列値のサポートを追加しました。CheckboxList
とRadioList
ウィジェットにバックエンド列挙値のサポートを追加しました。Html::renderTagAttributes()
でのnull
値属性の出力を修正しました。Yii Auth JWTパッケージのバージョン2.1がリリースされました。新バージョンに含まれる変更点のリストを以下に示します。
web-token/*
パッケージを1つのweb-token/jwt-library
に置き換え、PHPの最小バージョンを8.1に更新しました。^2.0
のpsr/http-message
のサポートを追加しました。Yii Hydratorパッケージのバージョン1.3がリリースされました。新バージョンに含まれる改善点のリストを以下に示します。
Collection
PHP属性を介したコレクションのサポートを追加しました。ParameterAttributesHandler
にハイドレーターの依存関係とwithHydrator()
メソッドを追加しました。ParameterAttributeResolveContext
にハイドレーターの依存関係とgetHydrator()
メソッドを追加しました。readonly
プロパティのハイドレーションを許可しました。Yii Network Utilitiesパッケージのバージョン1.1がリリースされました。いくつかの改善が含まれています。
IpRanges
を追加しました。Yii Validator のメジャーバージョンがタグ付けされました。
Each
ルールの処理中に利用可能な検証コンテキストパラメータ Each::PARAMETER_EACH_KEY
を追加しました。現在のキーが含まれています。InEnum
ルールを追加しました。Error::getValuePath()
の $escape
引数の型を bool|string|null
から string|null
に変更しました。OneOf
と AtLeast
ルールのエラーメッセージに翻訳された属性をリストアップしました。OneOf
ルールのエラーメッセージの意味を修正しました。OneOf
と AtLeast
ルールのエラーメッセージの意味を改善し、複数形を使用しました。AtLeast
の設定で、$min
が $attributes
の数より大きくなることを許可しなくなりました。getName()
メソッドを RuleInterface
から RuleWithOptionsInterface
に移動しました。RuleWithOptionsInterface
の名前を DumpedRuleInterface
に変更しました。RulesDumper
でのエクスポート時に、組み込みルールの名前としてFQCNを使用します。RulesDumper
でのエクスポート時に、DumpedRuleInterface
を実装していないルールの名前としてFQCNを使用します。$skipOnEmpty
引数の型を mixed
から bool|callable|null
に変更しました。RuleHandlerInterface::validate()
の $rule
引数の型を object
から RuleInterface
に変更しました。AtLeast
ルール名を FilledAtLeast
に、OneOf
ルール名を FilledOnlyOneOf
に変更しました。JsonHandler
でPHP組み込み関数json_validate()
を使用します。Result
クラスのPsalmアノテーションを改善しました。JsonHandler
でのJSONの検証を簡素化しました。Result::add()
をリファクタリングしました: foreach
から array_merge()
を削除しました。RulesNormalizer::normalize()
のパラメータ $rules
をオプションにしました。Json::$message
をより明確にしました。Nested
ルールのルールのエラーメッセージでのプロパティ名の使用を修正しました。RulesNormalizer::normalize()
では使用されていませんでした。Each::$incorrectInputKeyMessage
の type
パラメータの値が間違っていました。アプリケーションでこのメジャーバージョンにパッケージをアップグレードする方法については、アップグレード手順を参照してください。
]]>Yii Framework バージョン 2.0.51 のリリースを発表できることを嬉しく思います。
このバージョンをインストールまたはアップグレードするには、https://yii.dokyumento.jp/download/ の手順を参照してください。
このリリースでは、2.0.50 の回帰、PHP 8.3 とのエラーハンドラの互換性、およびいくつかのバグを修正しています。
フレームワークに貢献してくださった Yii コミュニティの皆様、ドキュメントの翻訳を最新の状態に保ってくださった翻訳者の方々、フォーラムで質問に答えてくださったコミュニティの皆様に感謝いたします。
活発な Yii コミュニティが多数ありますので、ヘルプが必要な場合や経験を共有したい場合は、お気軽に参加してください。
変更の完全なリストは、CHANGELOG で確認できます。
]]>Inertia.js の動作方法を示す Yii 2 デモアプリケーションです。
Inertia を使用すると、API を構築することなく、従来のサーバーサイドルーティングとコントローラーを使用してシングルページアプリケーションを構築できます。
このアプリケーションは、元のLaravel で記述された Ping CRM を移植したもので、Yii 2 Basic Project Template をベースにしています。
Yii 2 上の Ping CRM アプリケーション github と yii 拡張機能 をベースにしています。
変更点: Vue をバージョン 3 に更新、npm パッケージと composer を更新。Vue ファイルを Composition API (script setup) に変換しました。
リポジトリをローカルにクローンします。
git clone https://github.com/toatall/pingcrm-yii2-vue3 pingcrm-yii2-vue3
cd pingcrm-yii2-vue3
PHP の依存関係をインストールします。
composer install
NPM の依存関係をインストールします。
npm ci
アセットをビルドします。
npm run css-dev
npm run dev
SQLite データベースを作成します。別のデータベース (MySQL、Postgres) を使用することもできます。その場合は、構成を適宜更新してください。
touch database/database.sqlite
データベースマイグレーションを実行します。
php yii migrate
データベースシードを実行します。
php yii db/seed
開発サーバーを実行します (出力にアドレスが表示されます)
php yii serve
準備完了です!ブラウザで Ping CRM にアクセスし、次の情報でログインしてください。
Ping CRM のテストを実行するには、次のコマンドを実行します。
(to be done)
このプロジェクトに新しい機能を追加する場合は、次の手順が必要です。
<?php
namespace app\controllers;
use tebe\inertia\web\Controller;
class CustomController extends Controller
{
public function actionIndex()
{
$params = [
'data' => [],
];
return $this->inertia('demo/index', $params);
}
}
https://github.com/tbreuss/yii2-inertia に詳しい情報があります。
resources/js/Pages
の下に新しいページを追加します。https://inertia.dokyumento.jp に詳しい情報があります。
Yii HTTP Runner のメジャーバージョンがタグ付けされました。このバージョンではいくつかの変更が加えられました。
NullLogger
)。ServerRequestFactory
を削除しました。SapiEmitter
を内部としてマークしました。Yii Error Handler パッケージが以下の機能強化で更新されました。
@anonymous
接尾辞の削除。exit(1)
を実行します。Yii HTML パッケージのバージョン3.5がリリースされました。いくつかの改善点があります。
hr
とメソッドHtml::hr()
のクラスを追加しました。aria-describedby
属性で複数の要素をサポートしました。Yii Logging Library パッケージが以下の機能強化と新機能で更新されました。
Logger::assertLevelIsValid()
、Logger::assertLevelIsString()
、Logger::assertLevelIsSupported()
を追加しました。{foo.bar}
)。DateTime
とDateTimeImmutable
のサポートを追加しました。Message::category()
メソッドとMessage::DEFAULT_CATEGORY
定数を追加し、それを優先してCategoryFilter::DEFAULT
を非推奨にしました。Message::trace()
メソッドを追加しました。Message::time()
メソッドを追加しました。Logger::validateLevel()
メソッドを非推奨にしました。Logger
メソッドsetTraceLevel()
とsetExcludedTracePaths()
を非推奨にしました。Target
クラスのsetCommonContext()
メソッドとgetCommonContext()
メソッドを非推奨にしました。gettype()
をget_debug_type()
に置き換えました。Message
コンストラクタの$level
パラメータの型をstring
に変更しました。Yii Mailer Library のマイナーバージョンがタグ付けされました。いくつかの改善と修正があります。
MessageFactory
でデフォルトの「from」値を設定できるようにしました。^8.1
に上げました。yiisoft/view
バージョンを^10.0
に上げました。Yii View Renderer のマイナーバージョンがタグ付けされました。
yiisoft/view
バージョンを^10.0
に上げました。RandomProvider は、ActiveDataProvider(Yii 2.0 PHPフレームワーク)を継承したものです。レコードをランダムに選択します。これは、通常のActiveDataProvider(通常は)が順番にレコードを選択する方式とは異なり、場合によってはより魅力的な方法です。RandomProvider は、私のLoadMorePagerと連携することを意図していますが、LinkPagerや他のページャーとも動作します。
RandomProviderは、CUBRID
またはdblib
データベースドライバをサポートしていません。さらに、mysql
でのみテスト済みです。ただし、他のドライバでも動作する可能性が高いと考えています。ご経験のある方はぜひ情報提供いただければ幸いです。
RandomProviderは、「Order By Rand()」というアルゴリズムを使用している点にも注意してください。これは比較的遅く、スケーラビリティも高くありません。そのため、RandomProviderは、比較的少ないデータセット(数千レコード未満)でのみ使用することをお勧めします。詳細はこちらを参照してください。
RandomProviderのデモはこちらにあります。
Composerを使用して、通常の方法でyii2-random-providerをインストールします。composer.json
ファイルのrequire
セクションに以下を追加してください。
"sjaakp/yii2-random-provider": "*"
または、以下を実行します。
composer require sjaakp/yii2-random-provider
yii2-random-providerは、ソースコードをZIP形式でダウンロードして、手動でインストールすることもできます。
RandomProviderは、YiiのActiveDataProviderの置き換えとして使用できます。ActiveDataProviderと同様に使用してください。
]]>本プロジェクトはyii2-debugの拡張機能であり、MongoDBを使用してデバッグデータを保存します。
src/ 代码目录
src/models/ 数据模型
src/views/ 视图文件
src/controllers/ 控制器
composer require yagas/yii2-debug4mongo
if (YII_ENV_DEV) {
$config['bootstrap'][] = 'debug';
$config['modules']['debug'] = [
'class' => 'yagas\debug\Module',
'logTarget' => [
'class' => 'yagas\debug\LogTarget',
'app_no' => 'localhost_001', // 为当前站点设定标识
],
'percent' => 10, // 百分之十的几率清除历史数据(GC)
];
}
]]>Yii2 用 PDF.js による PDF ファイルのプレビュー
Yii2 PDF.js は PDF.js を使用します。
デモ: https://mozilla.github.io/pdf.js/web/viewer.html
パッケージは Packagist で利用可能です。Composer を使用してインストールできます。
composer require diecoding/yii2-pdfjs '^1.0'
または、composer.json
ファイルの require セクションに追加します。
'diecoding/yii2-pdfjs': '^1.0'
...
'modules'=>[
'pdfjs' => [
'class' => \diecoding\pdfjs\Module::class,
],
],
...
echo \diecoding\pdfjs\PdfJs::widget([
'url' => '@web/uploads/dummy.pdf',
]);
echo Url::to(["/pdfjs", 'file' => Url::to('@web/uploads/dummy.pdf', true)], true);
echo \diecoding\pdfjs\PdfJs::widget([
'url' => '@web/uploads/dummy.pdf',
'options' => [
'style' => [
'width' => '100%',
'height' => '500px',
],
],
]);
echo \diecoding\pdfjs\PdfJs::widget([
'url' => '@web/uploads/dummy.pdf',
'sections' => [
'toolbarContainer' => false,
],
]);
]]>メッセージキューは主にビジネスのデカップリングに使用されます。このプロジェクトではrabbitmqを採用し、thinkPHP、laravel、webman、yiiなどの一般的なフレームワークをサポートしており、単独で使用することもできます。
composer require xiaosongshu/rabbitmq
<?php
namespace app\commands;
require_once __DIR__.'/vendor/autoload.php';
class Demo extends \Xiaosongshu\Rabbitmq\Client
{
/** 以下是rabbitmq配置 ,请填写您自己的配置 */
/** @var string $host 服务器地址 */
public static $host = "127.0.0.1";
/** @var int $port 服务器端口 */
public static $port = 5672;
/** @var string $user 服务器登陆用户 */
public static $user = "guest";
/** @var string $pass 服务器登陆密码 */
public static $pass = "guest";
/**
* 业务处理
* @param array $params
* @return int
*/
public static function handle(array $params): int
{
//TODO 这里写你的业务逻辑
// ...
var_dump($params);
return self::ACK;
//return self::NACK;
}
}
\app\commands\Demo::publish(['name'=>'tome','age'=>15]);
メッセージはどこからでも送信できます。
\app\commands\Demo::consume();
コンシューマーをコマンドラインに配置し、コマンドラインを使用してキューの消費を実行できます。例(yiiを例として示します。laravel、webman、thinkPHPなどの他のフレームワークでも同様です):`
php <?php
namespace app\commands;
use yii\console\Controller;
/**
@note これは単なる例です */ class QueueController extends Controller {
/**
开启消费者命令 consume
```bash
php yii queue/index
注:複数のコンシューマーを開始する必要がある場合は、複数のウィンドウでコンシューマー開始コマンドを実行できます。もちろん、マルチプロセスを使用して処理することもできます。
\app\commands\Demo::close();
キューの使用中は、\RuntimeExceptionと\Exceptionを使用して例外をキャッチしてください。
このプロジェクトのルートディレクトリには、demo.phpというテストファイルがあります。これをプロジェクトのルートディレクトリにコピーし、コマンドラインで次のコマンドを実行できます。`
php php demo.php テストファイルコードは以下のとおりです。
php <?php
namespace xiaosongshu\test; require_once __DIR__ . '/vendor/autoload.php';
/**
@purpose キューの定義例 */ class Demo extends \Xiaosongshu\Rabbitmq\Client {
/* 以下はrabbitmqの設定です。ご自身の設定を入力してください / /* @var string $host サーバーアドレス / public static $host = "127.0.0.1";
/* @var int $port サーバーポート / public static $port = 5672;
/* @var string $user サーバーログインユーザー / public static $user = "guest";
/* @var string $pass サーバーログインパスワード / public static $pass = "guest";
/**
/* 通常メッセージの送信 */ \xiaosongshu\test\Demo::publish(['name' => 'tom']); \xiaosongshu\test\Demo::publish(['name' => 'jim']); \xiaosongshu\test\Demo::publish(['name' => 'jack']); /* コンシューマーを開始します。この関数はブロッキングなので、以降のコードは実行されません / \xiaosongshu\test\Demo::consume(); /* コンシューマーの停止 */ \xiaosongshu\test\Demo::close(); `
composer require xiaosongshu/yii2-rabbitmq
<?php
namespace app\commands;
require_once __DIR__.'/vendor/autoload.php';
class Demo extends \Xiaosongshu\Rabbitmq\Client
{
/** 以下是rabbitmq配置 ,请填写您自己的配置 */
/** @var string $host 服务器地址 */
public static $host = "127.0.0.1";
/** @var int $port 服务器端口 */
public static $port = 5672;
/** @var string $user 服务器登陆用户 */
public static $user = "guest";
/** @var string $pass 服务器登陆密码 */
public static $pass = "guest";
/**
* 业务处理
* @param array $params
* @return int
*/
public static function handle(array $params): int
{
//TODO 这里写你的业务逻辑
// ...
var_dump($params);
return self::ACK;
//return self::NACK;
}
}
\app\commands\Demo::publish(['name'=>'tome','age'=>15]);
メッセージはどこからでも送信できます。
\app\commands\Demo::consume();
コンシューマーをコマンドラインに配置し、コマンドラインを使用してキューの消費を実行できます。例:`
php <?php
namespace app\commands;
use yii\console\Controller;
/**
@note これは単なる例です */ class QueueController extends Controller {
/**
开启消费者命令 consume
```bash
php yii queue/index
キューの使用中は、\RuntimeExceptionと\Exceptionを使用して例外をキャッチしてください。
composer require xiaosongshu/yii2-elasticsearch
`
php
'components' => [
'ESClient' => [
'class' => \Xiaosongshu\Elasticsearch\ESClient::class,
'node'=>['192.168.101.170:9200'],
'username' => '',
'password' => '',
],
]
`
$res = Yii::$app->ESClient->search('index','_doc','title','测试')['hits']['hits'];
创建索引:createIndex
创建表结构:createMappings
删除索引:deleteIndex
获取索引详情:getIndex
新增一行数据:create
批量写入数据:insert
根据id批量删除数据:deleteMultipleByIds
根据Id 删除一条记录:deleteById
获取表结构:getMap
根据id查询数据:find
根据某一个关键字搜索:search
使用原生方式查询es的数据:nativeQuerySearch
多个字段并列查询,多个字段同时满足需要查询的值:andSearch
or查询 多字段或者查询:orSearch
根据条件删除数据:deleteByQuery
根据权重查询:searchByRank
获取所有数据:all
添加脚本:addScript
获取脚本:getScript
使用脚本查询:searchByScript
使用脚本更新文档:updateByScript
索引是否存在:IndexExists
根据id更新数据:updateById
<?php
require_once 'vendor/autoload.php';
/** 实例化客户端 */
$client = new \Xiaosongshu\Elasticsearch\ESClient([
/** 节点列表 */
'nodes' => ['192.168.4.128:9200'],
/** 用户名 */
'username' => '',
/** 密码 */
'password' => '',
]);
/** 删除索引 */
$client->deleteIndex('index');
/** 如果不存在index索引,则创建index索引 */
if (!$client->IndexExists('index')) {
/** 创建索引 */
$client->createIndex('index', '_doc');
}
/** 创建表 */
$result = $client->createMappings('index', '_doc', [
'id' => ['type' => 'long',],
'title' => ['type' => 'text', "fielddata" => true,],
'content' => ['type' => 'text', 'fielddata' => true],
'create_time' => ['type' => 'text'],
'test_a' => ["type" => "rank_feature"],
'test_b' => ["type" => "rank_feature", "positive_score_impact" => false],
'test_c' => ["type" => "rank_feature"],
]);
/** 获取数据库所有数据 */
$result = $client->all('index','_doc',0,15);
/** 写入单条数据 */
$result = $client->create('index', '_doc', [
'id' => rand(1,99999),
'title' => '我只是一个测试呢',
'content' => '123456789',
'create_time' => date('Y-m-d H:i:s'),
'test_a' => 1,
'test_b' => 2,
'test_c' => 3,
]);
/** 批量写入数据 */
$result = $client->insert('index','_doc',[
[
'id' => rand(1,99999),
'title' => '我只是一个测试呢',
'content' => '你说什么',
'create_time' => date('Y-m-d H:i:s'),
'test_a' => rand(1,10),
'test_b' => rand(1,10),
'test_c' => rand(1,10),
],
[
'id' => rand(1,99999),
'title' => '我只是一个测试呢',
'content' => '你说什么',
'create_time' => date('Y-m-d H:i:s'),
'test_a' => rand(1,10),
'test_b' => rand(1,10),
'test_c' => rand(1,10),
],
[
'id' => rand(1,99999),
'title' => '我只是一个测试呢',
'content' => '你说什么',
'create_time' => date('Y-m-d H:i:s'),
'test_a' => rand(1,10),
'test_b' => rand(1,10),
'test_c' => rand(1,10),
],
]);
/** 使用关键字搜索 */
$result = $client->search('index','_doc','title','测试')['hits']['hits'];
/** 使用id更新数据 */
$result1 = $client->updateById('index','_doc',$result[0]['_id'],['content'=>'今天你测试了吗']);
/** 使用id 删除数据 */
$result = $client->deleteById('index','_doc',$result[0]['_id']);
/** 使用条件删除 */
$client->deleteByQuery('index','_doc','title','测试');
/** 使用关键字搜索 */
$result = $client->search('index','_doc','title','测试')['hits']['hits'];
/** 使用条件更新 */
$result = $client->updateByQuery('index','_doc','title','测试',['content'=>'哇了个哇,这么大的种子,这么大的花']);
/** 添加脚本 */
$result = $client->addScript('update_content',"doc['content'].value+'_'+'谁不说按家乡好'");
/** 添加脚本 */
$result = $client->addScript('update_content2',"(doc['content'].value)+'_'+'abcdefg'");
/** 获取脚本内容 */
$result = $client->getScript('update_content');
/** 使用脚本搜索 */
$result = $client->searchByScript('index', '_doc', 'update_content', 'title', '测试');
/** 删除脚本*/
$result = $client->deleteScript('update_content2');
/** 使用id查询 */
$result = $client->find('index','_doc','7fitkYkBktWURd5Uqckg');
/** 原生查询 */
$result = $client->nativeQuerySearch('index',[
'query'=>[
'bool'=>[
'must'=>[
[
'match_phrase'=>[
'title'=>'测试'
],
],
[
'script'=>[
'script'=>"doc['content'].value.length()>2"
]
]
]
]
]
]);
/** and并且查询 */
$result = $client->andSearch('index','_doc',['title','content'],'测试');
/** or或者查询 */
$result = $client->orSearch('index','_doc',['title','content'],'今天');
この拡張パッケージのphpunit.xmlファイルをプロジェクトのルートディレクトリにコピーし、次のコマンドを実行します。`
bash php ./vendor/bin/phpunit -c phpunit.xml `
2723659854@qq.com
]]>$config['components']['mailer'] = [
'class' => 'jatin\resend\Mailer',
'useFileTransport' => false,
'viewPath' => '@app/mail',
'transport' => [
'apiKey' => '<YOUR_API_KEY>'
],
];
]]>
これはYii2フレームワーク用のシンプルなプロキシです。この拡張機能は、Yiiフレームワーク2.0 のHTTPプロキシアクションを提供します。
ライセンス情報は、LICENSE ファイルを確認してください。
composer require asminog/yii2-proxy
use asminog\proxy\ProxyAction;
class SiteController extends Controller
{
public function actions()
{
return [
'proxy' => [
'class' => ProxyAction::class,
// 'accessToken' => 'your-access-token', // - set access token for secure requests
// 'throw404Exception' => true, // - show 404 error if access token is not valid or request url is not valid
// 'proxyHeaders' => ['User-Agent', 'Content-Type'], // - set headers for proxy request
// 'proxyCookies' => ['cookie1', 'cookie2'], // - set cookies for proxy request
],
];
}
}
]]>Bootstrap 5 用 Yii2 フォームウィザードウィジェット
この拡張機能をインストールする推奨方法は、composer を使用することです。
以下のコマンドを実行するか、
php composer.phar require --prefer-dist sandritsch91/yii2-form-wizard
または、
"sandritsch91/yii2-form-wizard": "*"
をcomposer.jsonのrequireセクションに追加します。
use sandritsch91\yii2-form-wizard\FormWizard;
echo FormWizard::widget([
// required
'model' => $model, // The model to be used in the form
'tabOptions' => [ // These are the options for the Bootstrap Tab widget
'items' => [
[
'label' => 'Step 1', // The label of the tab, if omitted, a default-label will be used (Step 1, Step 2, ...)
'content' => $this->render('_step1', ['model' => $model]), // Either the content of the tab
],
[
'label' => 'Step 2',
'view' => '/test/_step2', // or a view to be rendered. $model and $form are passed to the view
'params' => ['a' => 1, 'b' => 2] // Pass additional parameters to the view
]
],
'navType' => 'nav-pills'
],
// optional
'validateSteps' => [ // Optional, pass the fields to be validated for each step.
['name', 'surname'],
[], // Leave array empty if no validation is needed
['email', 'password']
],
'options' => [], // Wizard-container html options
'formOptions' => [], // Form html options
'buttonOptions' => [ // Button html options
'previous' => [
'class' => ['btn', 'btn-secondary'],
'data' => [
'formwizard' => 'previous' // If you change this, make sure the clientOptions match
]
],
'next' => [...],
'finish' => [...]
],
'clientOptions' => [ // Client options for the form wizard, if you need to change them
// 'finishSelector' => '...',
// 'nextSelector' => '...',
// 'previousSelector' => '...',
// 'keepPosition' => true // Keep scroll position on step change.
// Set to false to disable, or pass a selector if you have a custom scroll container.
// Defaults to true.
],
'clientEvents' => [ // Client events for the form wizard
// 'onNext' => 'function () {...}',
// 'onPrevious' => 'function () {...}',
// 'onFinish' => 'function (){...}'
]
]);
ご協力お待ちしております。
ご質問、アイデア、ご提案、バグなどございましたら、お気軽にissueをご報告ください。
このパッケージは、テストにcodeceptionを使用しています。テストを実行するには、次のコマンドを実行します。
#### Unit tests
run ```php.exe .\vendor\bin\codecept run Unit``` in the root directory of this repository.
#### Functional tests
run ```php.exe .\vendor\bin\codecept run Functional``` in the root directory of this repository.
#### Accpetance tests
To be able to run acceptance tests, a few requirements are needed:
For Windows:\
- install java runtime environment
- install nodejs
- install selenium-standalone: `npm install -g selenium-standalone`
- start selenium-standalone: `selenium-standalone install && selenium-standalone start`
- host a yii2 application on a server or locally via ```./yii serve```
- add this plugin as a dependency to your ```composer.json``` and update dependencies
- site must be reachable over http://formwizard.com/
- add an action ```actionTest``` to the ```SiteController```, as described below
- this action must return a view file, as described below
- run ```php.exe .\vendor\bin\codecept run Acceptance```
For Linux:
Never did that before, but I think it is similar to the Windows setup.
The action in the SiteController:
```php
public function actionTest(): string
{
include __DIR__ . '/../vendor/sandritsch91/yii2-widget-form-wizard/tests/Support/Data/models/User.php';
$model = new User();
if (Yii::$app->request->post() && $model->load(Yii::$app->request->post()) && $model->validate()) {
return 'success';
}
return $this->render('test', [
'model' => new User()
]);
}
```
The view returned by the action:
```php
/** @var User $model */
use sandritsch91\yii2\formwizard\FormWizard;
use sandritsch91\yii2\formwizard\tests\Support\Data\models\User;
$wizard = FormWizard::widget([
'model' => $model,
'tabOptions' => [
'options' => [
'class' => 'mb-3'
],
'items' => [
[
'label' => 'Step 1',
'view' => '@app/vendor/sandritsch91/yii2-widget-form-wizard/tests/Support/Data/views/site/step1',
'linkOptions' => [
'id' => 'step1-link',,
'params' => [
'test' => 'some test variable'
]
]
],
[
'label' => 'Step 2',
'view' => '@app/vendor/sandritsch91/yii2-widget-form-wizard/tests/Support/Data/views/site/step2',
'linkOptions' => [
'id' => 'step2-link',
]
],
[
'label' => 'Step 3',
'view' => '@app/vendor/sandritsch91/yii2-widget-form-wizard/tests/Support/Data/views/site/step3',
'linkOptions' => [
'id' => 'step3-link',
]
]
],
'navType' => 'nav-pills'
],
'validateSteps' => [
['firstname', 'lastname'],
['username', 'password', 'password_validate'],
['email']
],
'clientOptions' => [
'keepPosition' => true
]
]);
echo \yii\helpers\Html::tag('div', $wizard, [
'class' => 'col-4'
]);
```
After the initial installation, you only have to start the selenium-standalone server ```selenium-standalone start```
and run the tests ```php.exe .\vendor\bin\codecept run Acceptance``` in the root directory of this repository.
If you do not want to setup an application, just run the unit and functional tests by
running ```php.exe .\vendor\bin\codecept run Unit,Functional```, I can modify and run the acceptance tests for you,
after you opened a pull request.
]]>最近、WordPressウェブサイトに複数の複雑なフォームを統合するタスクを割り当てられました。これらのフォームには、多数のフィールド、複雑な検証ルール、動的フィールド(1対多の関係)、そしてPHPの継承を使用してコードの重複を軽減できる相互依存関係が含まれていました。
最初の調査では、WordPressでフォームを処理する従来のアプローチは、通常、プラグインのインストールか、エディターまたはカスタムページテンプレートを使用してマークアップを手動で埋め込むかのどちらかであることが明らかになりました。その後、フォームの送信を管理するためにプラグインの機能に大きく依存するか、カスタムコーディングに頼ることになります。
私のタスクの一部にデータのログ記録、APIエンドポイントとのインターフェース、メールの送信などが含まれていたため、既存のプラグインがこれらの要件を満たしているかどうかを確認するのではなく、自分で機能を開発することを選択しました。
さらに、公式の情報源によると、2024年3月現在、ほとんどのYii 3パッケージは本番環境で使用できる状態と見なされており、長年のYiiフレームワークユーザーとして、これらのアップデートを調査し、習熟する好機であると考えました。
Githubでプロジェクト全体とコードを確認できます。
さらに、プロジェクトのルートディレクトリからdocker-compose up
を実行するだけで、Dockerを使用して簡単にデプロイできます。WordPressのセットアップとコンテンツ生成は自動的に行われますので、Dockerfile
を確認してください。
私の目的は、Yii3パッケージを使用してWordPressフレームワーク内でフォームをレンダリングおよび管理することでした。デモのために、データの検証のみに焦点を当て、それ以上の処理を行わない基本的な評価フォームを実装することにしました。
それでは、最小限のクラシックテーマを例として始めましょう。ダッシュボード内に「評価フォーム」という名前のWordPressページを作成しました。次に、この特定のページを表示するために、テーマのルートフォルダ内にpage-the-rating-form.php
という名前のファイルを作成する必要があります。
この指定されたファイルは、フォームのマークアップを定義するための設計図として機能します。
Yii3の機能を活用するために、次のパッケージを組み込みます。
まず、composer init
を実行して、テーマのルートにComposerプロジェクトを初期化します。このプロセスにより、composer.jsonファイルが生成されます。その後、プロジェクトにYii3パッケージを含めます。
composer require yiisoft/form-model:dev-master yiisoft/validator yiisoft/form:dev-master
そして、functions.php
ファイルに次の行を追加して、テーマにcomposerの自動読み込みを指示します。
require __DIR__ . '/vendor/autoload.php';
composer init
コマンドの実行後、テーマのルートディレクトリにsrc
ディレクトリが作成されました。このディレクトリ内にフォームモデルクラスを追加します。
プロジェクトの拡張を考慮して、整理を維持することが重要です。そのため、src/Forms
ディレクトリを作成し、その中にRatingForm
クラスを配置します。
<?php
namespace Glpzzz\Yii3press\Forms;
use Yiisoft\FormModel\FormModel;
class RatingForm extends FormModel
{
private ?string $name = null;
private ?string $email = null;
private ?int $rating = null;
private ?string $comment = null;
private string $action = 'the_rating_form';
public function getPropertyLabels(): array
{
return [
'name' => 'Name',
'email' => 'Email',
'rating' => 'Rating',
'comment' => 'Comment',
];
}
}
評価ユースケースに必要なフィールド以外にも、action
クラス属性に注目することが重要です。この属性は、WordPressにどのテーマフックがフォームの送信を管理するべきかを指示する上で重要です。これについては後で詳しく説明します。
ここで、入力の整合性を確保するために、モデルにいくつかの検証ルールを組み込みます。まず、RulesProviderInterface
を実装するようにクラスを設定します。これにより、フォームパッケージはこれらのルールにアクセスし、ネイティブの検証属性を使用してHTMLマークアップを拡張できます。
class RatingForm extends FormModel implements RulesProviderInterface
これで、クラスにgetRules()
メソッドを実装する必要があります。
public function getRules(): iterable
{
return [
'name' => [
new Required(),
],
'email' => [
new Required(),
new Email(),
],
'rating' => [
new Required(),
new Integer(min: 0, max: 5),
],
'comment' => [
new Length(min: 100),
],
];
}
フォームマークアップを生成するには、RatingForm
のインスタンスをテンプレートに渡す必要があります。WordPressでは、ページをレンダリングする前にグローバル変数を作成するアプローチを採用しました(最もエレガントな解決策ではありませんが)。
$hydrator = new Hydrator(
new CompositeTypeCaster(
new NullTypeCaster(emptyString: true),
new PhpNativeTypeCaster(),
new HydratorTypeCaster(),
)
);
add_filter('template_redirect', function () use ($hydrator) {
// Get the queried object
$queried_object = get_queried_object();
// Check if it's a page
if ($queried_object instanceof WP_Post && is_page()) {
if ($queried_object->post_name === 'the-rating-form') {
global $form;
if ($form === null) {
$form = $hydrator->create(RatingForm::class, []);
}
}
}
});
特定の関数外でHydrator
クラスをインスタンス化しているので、すべての必要なコールバックで再利用できることに注意してください。RatingForm
インスタンスが利用可能になったので、page-the-rating-form.php
ファイル内にフォームのマークアップを作成します。
<?php
use Glpzzz\Yii3press\Forms\RatingForm;
use Yiisoft\FormModel\Field;
use Yiisoft\Html\Html;
/** @var RatingForm $form */
global $form;
?>
<?php get_header(); ?>
<h1><?php the_title(); ?></h1>
<?php the_content(); ?>
<?= Html::form()
->post(esc_url(admin_url('admin-post.php')))
->open()
?>
<?= Field::hidden($form, 'action')->name('action') ?>
<?= Field::text($form, 'name') ?>
<?= Field::email($form, 'email') ?>
<?= Field::range($form, 'rating') ?>
<?= Field::textarea($form, 'comment') ?>
<?= Html::submitButton('Send') ?>
<?= "</form>" ?>
<?php get_footer(); ?>
フォームのマークアップ生成では、Yii3のHtml
ヘルパーとField
クラスを組み合わせて使用しました。注目すべき点は、
admin-post.php
WordPressエンドポイントとして指定されたPOSTメソッドを使用しています。action
値を含めるために、'action'
という名前の隠しフィールドを使用しました。Field::hidden
メソッドはフィールド名をTheFormClassName[the_field_name]
の形式で生成しますが、単に'action'
という名前にする必要があったため、入力を'action'
に名前変更しました。この調整により、次のセクションで説明するように、テーマ関数にフックしてフォームリクエストを処理できます。
さらに進む前に、Yiiの機能を利用してフォームを強化しましょう。既にモデルで入力の送信後の検証ルールを定義していますが、ブラウザ内でも入力の検証を行う方が有利です。入力要素にこれらの検証ルールを直接再定義することもできますが、Yiiは簡素化されたアプローチを提供しています。次のコードスニペットをfunctions.php
ファイルに組み込むことで
add_action('init', function () {
ThemeContainer::initialize([
'default' => [
'enrichFromValidationRules' => true,
]
], 'default', new ValidationRulesEnricher()
);
});
このコードスニペットを実装することにより、デフォルトのフォームテーマに対してValidationRulesEnricher
を有効にします。有効にすると、フォームフィールドに「required」、「min」、「max」などの検証ルールが追加され、モデルクラスで定義された検証ルールと一致します。この機能によりプロセスが簡素化され、貴重な時間を節約し、手動でのコード作成の必要性が最小限に抑えられます。実際、これはYii3が提供する優れた機能の一部を示しています。
フォームが送信されると、WordPressによって提供されるエンドポイントであるadmin-post.php
に送信されます。ただし、複数のフォームを扱う場合、それぞれの処理を区別することが不可欠になります。ここで、POSTリクエストにaction
値を含めることが非常に重要になります。
次のコードスニペットの最初の2行に注意してください。フックの命名規則はadmin_post_<action_name>
です。したがって、フォームにaction = 'the-rating-form'
がある場合、対応するフック名はadmin_post_the_rating_form
になります。
admin_post_<action_name>
とadmin_post_nopriv_<action_name>
の両方が含まれているのは、WordPressがユーザーがログインしているかどうかに応じて異なるハンドラーを許可しているためです。私たちのシナリオでは、ユーザーの認証ステータスに関係なく、同じハンドラーが必要です。
add_action('admin_post_the_rating_form', fn() => handleForms($hydrator));
add_action('admin_post_nopriv_the_rating_form', fn() => handleForms($hydrator));
function handleForms(Hydrator $hydrator): void
{
global $form;
$form = $hydrator->create(RatingForm::class, $_POST['RatingForm']);
$result = (new Yiisoft\Validator\Validator())->validate($form);
if ($form->isValid()) {
// handle the form
}
get_template_part('page-the-rating-form');
}
Yiiの側面に戻ると、hydrator
を使用して投稿されたデータをフォームにインスタンス化してロードします。次に、データを検証します。検証が成功すると、検証されたデータを使用して目的のアクションを実行できます。ただし、検証に失敗した場合は、フォームを再レンダリングし、送信されたデータと検証中に生成されたエラーメッセージを入力します。
原文はhttps://glpzzz.dev/2024/03/03/integrating-yii3-packages-into-wordpress.htmlに投稿されました。
]]>Yii2のActiveRecordモデルの変更を記録します。
このパッケージを使用すると、モデルの変更履歴を保持し、疑わしいアクティビティを示す可能性のある情報における不一致や異常に関する情報を提供できます。受信および保存された情報は、後でさまざまな方法で表示できます。
この拡張機能をインストールする推奨方法は、composerを使用することです。
次に実行します。
php composer.phar require --prefer-dist neoacevedo/yii2-auditing "*"
または、
"neoacevedo/yii2-auditing": "*"
をcomposer.json
ファイルのrequireセクションに追加します。
拡張機能がインストールされたら、アプリケーションのコンソール設定ファイルのmigrationPath
セクションに、
...
'@vendor/neoacevedo/yii2-auditing/neoacevedo/auditing/migrations',
...
次に、モデルのコードのbehaviors
メソッド内に、
public function behaviors()
{
return [
[
'class' => \neoacevedo\auditing\behaviors\AuditBehavior::class,
'deleteOldData' => true, // Para borrar datos antiguos del registro de eventos
'deleteNumRows' => 20, // Borra esta cantidad de registros
],
...
];
}
アプリケーションのウェブ内で実装した他のモデルと同様に、情報を表示できます。
履歴を一覧表示するGridView
を使用するコントローラーとビューを使用できます。たとえば、AuditingController
というコントローラーを作成し、actionIndex
メソッドを次のように作成できます。
/**
* Lists all Auditing models.
*
* @return string
*/
public function actionIndex()
{
$searchModel = new AuditingSearch();
$dataProvider = $searchModel->search($this->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
データを視覚化するために、actionView
メソッドを作成します。
/**
* Displays a single Auditing model.
* @param int $id ID
* @return string
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
view
ビュー内に、履歴を一覧表示するGridView
を追加できます。
...
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
'user_id',
'description',
'event',
'model',
'attribute',
'old_value',
'new_value',
'action',
'ip',
'created_at',
],
]); ?>
...
]]>DMデータベース用のデータベース拡張機能
この拡張機能をインストールする推奨方法は、composer を使用することです。
以下のコマンドを実行するか、
php composer.phar require --prefer-dist luguohuakai/yii2-dm "*"
または、
"luguohuakai/yii2-dm": "*"
をcomposer.json
ファイルのrequireセクションに追加します。
拡張機能がインストールされたら、次のようにコードで使用します。
'components' => [
'db' => [
'class' => 'luguohuakai\db\dm\Connection',
'dsn' => 'dm:host=localhost:xxx;schema=xxx',
'username' => 'SYSDBA',
'password' => 'SYSDBA',
]
]
]]>DataTableウィジェットは、インタラクティブで動的なデータテーブルを作成するために使用されます。提供されたJavaScriptコードは、サーバーサイド処理、カスタムデータ処理、列レンダリング、および完全なサーバーサイドエクスポートを使用してDataTableを初期化する方法を示しています。
`composer require rashedalkhatib/yii2-datatables:1.0.0
``../frontend/assets/AppAsset.php
`に移動します。$depends
配列にrashedalkhatib\datatables\DataTableAsset
を追加します。 public $depends = [
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset',
'yii\bootstrap\BootstrapPluginAsset',
'rashedalkhatib\datatables\DataTableAsset'
];
$searchFormSelector = '#searchForm';
$ajaxUrl = Url::to(['api/yourEndPoint']); // Adjust the URL based on your routes
// Define your DataTable columns
$columns = [
[
'title' => 'ID',
'data' => 'id',
'visible' => true,
'render' => new JsExpression('function(data, type, row) {
return "demo";
}'),
],
];
// Configure other DataTable parameters
$processing = true;
$serverSide = true;
$pageLength = 10;
$dom = 'Btip';
$buttons = [
[
'extend' => 'excel',
'text' => 'Excel',
'titleAttr' => 'Excel',
'action' => new JsExpression('exportAll') // this is required
],
];
// Configure Ajax settings
$ajaxConfig = [
'url' => $ajaxUrl,
'bdestroy' => true,
'type' => 'POST',
'data' => new JsExpression('function(d) {
var searchForm = $('body').find('#searchForm').serializeArray();
searchForm[searchForm.length] = { name: 'YourModel[page]', value: d.start }; // required
searchForm[searchForm.length] = { name: 'YourModel[length]', value: d.length }; // required
searchForm[searchForm.length] = { name: 'YourModel[draw]', value: d.draw }; // required
var order = {
'attribute': d.columns[d.order[0]['column']]['data'],
'dir': d.order[0]['dir']
}; // required
searchForm[searchForm.length] = { name: 'YourModel[order]', value: JSON.stringify(order) };
return searchForm;
}'),
'dataSrc' => new JsExpression('function(d) {
var searchForm = $("' . $searchFormSelector . '").serializeArray();
if (d.validation) {
searchForm.yiiActiveForm("updateMessages", d.validation, true);
return [];
}
return d.data;
}'),
];
// Use the DataTableWidget with configured parameters
DataTable::widget([
'id' => 'yourDataTable',
'ajaxConfig' => $ajaxConfig,
'columns' => $columns,
'processing' => $processing,
'serverSide' => $serverSide,
'pageLength' => $pageLength,
'dom' => $dom,
'buttons' => $buttons,
]);
// The HTML container for your DataTable
echo '<form id="searchForm">// your inputs </form>';
echo '<table id="yourDataTable" class="display"></table>';
<form id="searchForm">
// your inputs
</form>
<table id="yourDataTable" class="display" style="width:100%">
</table>
var arrayToExport = [0,1];
$('#yourDataTable').DataTable({
"ajax": {
// Server-side processing configuration
"url": "../api/yourEndPoint",
"bdestroy": true, // this allows you to re init the dataTabel and destory it
"type": "POST", // request method
"data": function (d) { // this represent the data you are sending with your ajax request
// Custom function for sending additional parameters to the server
var searchForm = $('body').find('#searchForm').serializeArray();
searchForm[searchForm.length] = { name: "YourModel[page]", value: d.start }; // required
searchForm[searchForm.length] = { name: "YourModel[length]", value: d.length }; // required
searchForm[searchForm.length] = { name: "YourModel[draw]", value: d.draw }; // required
var order = {
'attribute': d.columns[d.order[0]['column']]['data'],
'dir': d.order[0]['dir']
}; // required
searchForm[searchForm.length] = { name: "YourModel[order]", value: JSON.stringify(order) };
return searchForm;
},
dataSrc: function (d) {
// Custom function to handle the response data
// EX:
var searchForm = $('body').find('#searchForm').serializeArray();
if (d.validation) {
searchForm.yiiActiveForm('updateMessages', d.validation, true);
return [];
}
return d.data;
}
},
"columns": [{
// Column configurations
"title": "ID",
"data": "id",
"visible": true // visablity of column
},
// ... (other columns)
{
"title": "Actions",
"data": "id",
"visible": actionCol,
"render": function (data, type, row) {
// Custom rendering function for the "Actions" column
return '<a class="showSomething" data-id="' + row.id + '">View</a>';
}
}],
processing: true,
serverSide: true,
"pageLength": 10,
dom: "Btip",
"buttons": [{
// "Excel" button configuration
"extend": 'excel',
exportOptions: {
columns: arrayToExport
},
"text": ' Excel',
"titleAttr": 'Excel',
"action": exportAll // newexportaction this action is to allow you exporting with server side without rendaring data
}],
});
// in your HTTP request you want to include these params
$_postData = [
'page' => $this->page == 0 ? 0 : $this->page / $this->length, // this equation is required to handle Yii2 Data provider Logic
'limit' => $this->length,
'export' => $this->export,
'order' => $this->order,
// add your custom params .....
];
return $this->asJson(
[
'data' => $_scoreData->data,
'draw' => $_scoreSearchForm->draw,
'recordsTotal' => $_scoreData->count,
'recordsFiltered' => $_scoreData->count
]);
public function actionYourEndPoint()
{
$searchModel = new SearchModel();
$dataProvider = $searchModel->search(Yii::$app->request->get());
return $this->asJson(
array(
'data' => $dataProvider['data'],
'count' => $dataProvider['count']
)
);
}
public function search($params)
{
$this->load($params, ''); // load your values into the model
$query = Data::find(); // Data model is your link to the database
$_order = json_decode($this->order);
if ($this->export == 'true') {
$dataProvider = new ActiveDataProvider([
'query' => $query
// we removed the page and pageSize keys to allow all data to be exported
]);
} else {
$_orderType = SORT_ASC;
if ($_order->dir == 'desc')
$_orderType = SORT_DESC;
$query->orderBy([$_order->attribute => $_orderType]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => $this->limit,
'page' => $this->page,
],
]);
}
return array(
'data' => $dataProvider->getModels(),
'count' => $dataProvider->getTotalCount()
);
}
このビヘイビアは、検証前にJSONから配列への属性の自動デコードを行い、エラー処理と検証失敗時の再エンコードを行います。これにより、「実際の」JSON文字列をさらに処理できます。
この拡張機能をインストールする推奨方法は、composerを使用することです。
以下のコマンドを実行するか、
composer require --prefer-dist eluhr/yii2-json-attribute-behavior "*"
または、
"eluhr/yii2-json-attribute-behavior": "*"
composer.json
ファイルのrequire
セクションに以下を追加します。
yii\base\Model
またはその派生クラスでは、ビヘイビアは次のように使用できます。
public function behaviors(): array
{
$behaviors = parent::behaviors();
$behaviors['json-attribute'] = [
'class' => eluhr\jsonAttributeBehavior\JsonAttributeBehavior::class,
'attributes' => [
'data_json'
]
];
return $behaviors;
}
このビヘイビアを使用することで、属性が文字列か配列かは関係ありません。ビヘイビアは常に、データをデータベースに保存する前に属性が配列であることを保証し、Yiiが残りの処理を行います。
このビヘイビアはi18nをサポートしています。設定ファイルにjson-attribute-behavior
カテゴリを追加することで、デフォルトのエラーメッセージを上書きできます。
composerで依存関係をインストールした後、次のコマンドでテストを実行できます。
make test
]]>IP2Proxy Yii拡張機能を使用すると、オープンなプロキシ、ウェブプロキシ、VPN匿名化ツール、TOR出口ノード、検索エンジンロボット、データセンター範囲、住宅用プロキシ、コンシューマープライバシーネットワーク、エンタープライズプライベートネットワークとして使用されているIPアドレスを照会できます。IP2Proxy BINデータファイルまたはウェブサービスからプロキシIPアドレスを検索します。開発者は、APIを使用して、Yiiで記述されたアプリケーションのすべてのIP2Proxy BINデータベースまたはウェブサービスを照会できます。
Yii2の場合
php composer.phar require ip2location/ip2proxy-yii
を実行して、プラグインをYii2フレームワークにダウンロードします。注記: BINデータベースは.BIN拡張子で終わるバイナリファイルを参照しますが、CSV形式ではありません。ダウンロードする適切なパッケージを選択してください。
use IP2ProxyYii\IP2Proxy_Yii;
// (required) Define IP2Proxy database path.
define('IP2PROXY_DATABASE', '/path/to/ip2proxy/database');
// (required) Define IP2Location.io API key.
define('IP2LOCATION_IO_API_KEY', 'your_api_key');
// (optional) Define Translation information. Refer to https://www.ip2location.io/ip2location-documentation for available languages.
define('IP2LOCATION_IO_LANGUAGE', 'en');
$IP2Proxy = new IP2Proxy_Yii();
$record = $IP2Proxy->get('1.0.241.135');
echo 'Result from BIN Database:<br>';
echo '<p><strong>IP Address: </strong>' . $record['ipAddress'] . '</p>';
echo '<p><strong>IP Number: </strong>' . $record['ipNumber'] . '</p>';
echo '<p><strong>IP Version: </strong>' . $record['ipVersion'] . '</p>';
echo '<p><strong>Country Code: </strong>' . $record['countryCode'] . '</p>';
echo '<p><strong>Country: </strong>' . $record['countryName'] . '</p>';
echo '<p><strong>State: </strong>' . $record['regionName'] . '</p>';
echo '<p><strong>City: </strong>' . $record['cityName'] . '</p>';
echo '<p><strong>Proxy Type: </strong>' . $record['proxyType'] . '</p>';
echo '<p><strong>Is Proxy: </strong>' . $record['isProxy'] . '</p>';
echo '<p><strong>ISP: </strong>' . $record['isp'] . '</p>';
echo '<p><strong>Domain: </strong>' . $record['domain'] . '</p>';
echo '<p><strong>Usage Type: </strong>' . $record['usageType'] . '</p>';
echo '<p><strong>ASN: </strong>' . $record['asn'] . '</p>';
echo '<p><strong>AS: </strong>' . $record['as'] . '</p>';
echo '<p><strong>Last Seen: </strong>' . $record['lastSeen'] . '</p>';
echo '<p><strong>Threat: </strong>' . $record['threat'] . '</p>';
echo '<p><strong>Provider: </strong>' . $record['provider'] . '</p>';
$record = $IP2Proxy->getWebService('1.0.241.135');
echo 'Result from Web service:<br>';
echo '<pre>';
print_r ($record);
echo '</pre>';
このライブラリは、機能するためにIP2Proxy BINデータファイルまたはIP2Proxy APIキーデータファイルが必要です。BINデータファイルは以下からダウンロードできます。
IP2Location.io IPジオロケーションAPIに登録して、無料のAPIキーを取得することもできます。
メール: support@ip2location.com
ウェブサイト: https://www.ip2location.com
]]>IP2Location Yii拡張機能を使用すると、IP2Locationデータベースを使用して、IPアドレスから国、地域、都市、座標、郵便番号、タイムゾーン、ISP、ドメイン名、接続タイプ、エリアコード、天気、MCC、MNC、モバイルブランド名、標高、使用タイプ、IPアドレスの種類、IAB広告カテゴリを見つけることができます。速度とメモリ使用量の最適化が図られています。開発者は、APIを使用して、Yiiで記述されたアプリケーションのすべてのIP2Location BINデータベースまたはウェブサービスを照会できます。
Yii2の場合
composer require ip2location/ip2location-yii
を実行して、拡張機能をYii2フレームワークにダウンロードします。注記: BINデータベースは.BIN拡張子で終わるバイナリファイルを参照しますが、CSV形式ではありません。ダウンロードする適切なパッケージを選択してください。
use IP2LocationYii\IP2Location_Yii;
// (required) Define IP2Location database path.
define('IP2LOCATION_DATABASE', '/path/to/ip2location/database');
// (required) Define IP2Location.io API key.
define('IP2LOCATION_IO_API_KEY', 'your_api_key');
// (optional) Define Translation information. Refer to https://www.ip2location.io/ip2location-documentation for available languages.
define('IP2LOCATION_IO_LANGUAGE', 'en');
// (optional) Define Translation information. Refer to https://www.ip2location.com/web-service/ip2location for available languages.
define('IP2LOCATION_LANGUAGE', 'en');
$IP2Location = new IP2Location_Yii();
$record = $IP2Location->get('8.8.8.8');
echo 'Result from BIN Database:<br>';
echo 'IP Address: ' . $record['ipAddress'] . '<br>';
echo 'IP Number: ' . $record['ipNumber'] . '<br>';
echo 'ISO Country Code: ' . $record['countryCode'] . '<br>';
echo 'Country Name: ' . $record['countryName'] . '<br>';
echo 'Region Name: ' . $record['regionName'] . '<br>';
echo 'City Name: ' . $record['cityName'] . '<br>';
echo 'Latitude: ' . $record['latitude'] . '<br>';
echo 'Longitude: ' . $record['longitude'] . '<br>';
echo 'ZIP Code: ' . $record['zipCode'] . '<br>';
echo 'Time Zone: ' . $record['timeZone'] . '<br>';
echo 'ISP Name: ' . $record['isp'] . '<br>';
echo 'Domain Name: ' . $record['domainName'] . '<br>';
echo 'Net Speed: ' . $record['netSpeed'] . '<br>';
echo 'IDD Code: ' . $record['iddCode'] . '<br>';
echo 'Area Code: ' . $record['areaCode'] . '<br>';
echo 'Weather Station Code: ' . $record['weatherStationCode'] . '<br>';
echo 'Weather Station Name: ' . $record['weatherStationName'] . '<br>';
echo 'MCC: ' . $record['mcc'] . '<br>';
echo 'MNC: ' . $record['mnc'] . '<br>';
echo 'Mobile Carrier Name: ' . $record['mobileCarrierName'] . '<br>';
echo 'Elevation: ' . $record['elevation'] . '<br>';
echo 'Usage Type: ' . $record['usageType'] . '<br>';
echo 'Address Type: ' . $record['addressType'] . '<br>';
echo 'Category: ' . $record['category'] . '<br>';
$record = $IP2Location->getWebService('8.8.8.8');
echo 'Result from Web service:<br>';
echo '<pre>';
print_r ($record);
echo '</pre>';
このライブラリは、機能するためにIP2Location BINデータファイルまたはIP2Location APIキーが必要です。BINデータファイルは以下からダウンロードできます。
IP2Location.io IPジオロケーションAPIに登録して、無料のAPIキーを取得することもできます。
メール: support@ip2location.com
ウェブサイト: https://www.ip2location.com
]]>期待通りにカルーセルが機能するように、次のCSSスタイルを使用してください。
.product_img_slide {
padding: 100px 0 0 0;
}
.product_img_slide > .carousel-inner > .carousel-item {
overflow: hidden;
max-height: 650px;
}
.carousel-inner {
position: relative;
width: 100%;
}
.product_img_slide > .carousel-indicators {
top: 0;
left: 0;
right: 0;
width: 100%;
bottom: auto;
margin: auto;
font-size: 0;
cursor: e-resize;
/* overflow-x: auto; */
text-align: left;
padding: 10px 5px;
/* overflow-y: hidden;*/
white-space: nowrap;
position: absolute;
}
.product_img_slide > .carousel-indicators li {
padding: 0;
width: 76px;
height: 76px;
margin: 0 5px;
text-indent: 0;
cursor: pointer;
background: transparent;
border: 3px solid #333331;
-webkit-border-radius: 0;
border-radius: 0;
-webkit-transition: all 0.7s cubic-bezier(0.22, 0.81, 0.01, 0.99);
transition: all 1s cubic-bezier(0.22, 0.81, 0.01, 0.99);
}
.product_img_slide > .carousel-indicators .active {
width: 76px;
border: 0;
height: 76px;
margin: 0 5px;
background: transparent;
border: 3px solid #c13c3d;
}
.product_img_slide > .carousel-indicators > li > img {
display: block;
/*width:114px;*/
height: 76px;
}
.product_img_slide .carousel-inner > .carousel-item > a > img, .carousel-inner > .carousel-item > img, .img-responsive, .thumbnail a > img, .thumbnail > img {
display: block;
max-width: 100%;
line-height: 1;
margin: auto;
}
.product_img_slide .carousel-control-prev {
top: 58%;
/*left: auto;*/
right: 76px;
opacity: 1;
width: 50px;
bottom: auto;
height: 50px;
font-size: 50px;
cursor: pointer;
font-weight: 700;
overflow: hidden;
line-height: 50px;
text-shadow: none;
text-align: center;
position: absolute;
background: transparent;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.6);
-webkit-box-shadow: none;
box-shadow: none;
-webkit-border-radius: 0;
border-radius: 0;
-webkit-transition: all 0.6s cubic-bezier(0.22, 0.81, 0.01, 0.99);
transition: all 0.6s cubic-bezier(0.22, 0.81, 0.01, 0.99);
}
.product_img_slide .carousel-control-next {
top: 58%;
left: auto;
right: 25px;
opacity: 1;
width: 50px;
bottom: auto;
height: 50px;
font-size: 50px;
cursor: pointer;
font-weight: 700;
overflow: hidden;
line-height: 50px;
text-shadow: none;
text-align: center;
position: absolute;
background: transparent;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.6);
-webkit-box-shadow: none;
box-shadow: none;
-webkit-border-radius: 0;
border-radius: 0;
-webkit-transition: all 0.6s cubic-bezier(0.22, 0.81, 0.01, 0.99);
transition: all 0.6s cubic-bezier(0.22, 0.81, 0.01, 0.99);
}
.product_img_slide .carousel-control-next:hover, .product_img_slide .carousel-control-prev:hover {
color: #c13c3d;
background: transparent;
}
ここでは、画像サムネイルをカルーセルのインジケーターとして表示するためのyii\bootstrap5\Carouselの拡張であるカルーセルウィジェットを示します。
ウィジェットコードを以下に示します。
<?php
namespace app\widgets;
use Yii;
use yii\bootstrap5\Html;
class Carousel extends \yii\bootstrap5\Carousel
{
public $thumbnails = [];
public function init()
{
parent::init();
Html::addCssClass($this->options, ['data-bs-ride' => 'carousel']);
if ($this->crossfade) {
Html::addCssClass($this->options, ['animation' => 'carousel-fade']);
}
}
public function renderIndicators(): string
{
if ($this->showIndicators === false){
return '';
}
$indicators = [];
for ($i = 0, $count = count($this->items); $i < $count; $i++){
$options = [
'data' => [
'bs-target' => '#' . $this->options['id'],
'bs-slide-to' => $i
],
'type' => 'button',
'thumb' => $this->thumbnails[$i]['thumb']
];
if ($i === 0){
Html::addCssClass($options, ['activate' => 'active']);
$options['aria']['current'] = 'true';
}
$indicators[] = Html::tag('li',Html::img($options['thumb']), $options);
}
return Html::tag('ol', implode("\n", $indicators), ['class' => ['carousel-indicators']]);
} }
上記のウィジェットをビューファイルで次のように使用できます。
<?php
$indicators = [
'0' =>[ 'thumb' => "https://placehold.co/150X150?text=A"],
'1' => ['thumb' => 'https://placehold.co/150X150?text=B'],
'2' => [ 'thumb' => 'https://placehold.co/150X150?text=C']
];
$items = [
[ 'content' =>Html::img('https://live.staticflickr.com/8333/8417172316_c44629715e_w.jpg')],
[ 'content' =>Html::img('https://live.staticflickr.com/3812/9428789546_3a6ba98c49_w.jpg')],
[ 'content' =>Html::img('https://live.staticflickr.com/8514/8468174902_a8b505a063_w.jpg')]
];
echo Carousel::widget([
'items' =>
$items,
'thumbnails' => $indicators,
'options' => [
'data-interval' => 3, 'data-bs-ride' => 'scroll','class' => 'carousel product_img_slide',
],
]);
]]>Yiiには、すぐに使える国際化(i18n)機能があります。マニュアルには、Yiiでi18nを使用するための設定方法に関する指示がありますが、bootstrapメニューに完全に統合する方法に関する情報はほとんどありません。このドキュメントでは、それを改善しようとします。
GitHubリポジトリには、言語フラグ、国旗の一部、言語コードとその言語名のリスト、Yiiがすぐに認識する言語のリストも含まれています。YouTubeに動画を近日公開予定です。
システムがi18nを使用するように設定されていることを確認してください。Yii2マニュアルより
Yiiは
PHP intl
拡張機能を使用して、yii\i18n\Formatter
クラスの日付と数値のフォーマット、およびyii\i18n\MessageFormatter
を使用したメッセージのフォーマットなど、ほとんどのI18N機能を提供しています。両方のクラスは、intl拡張機能がインストールされていない場合にフォールバックメカニズムを提供します。ただし、フォールバック実装は、英語のターゲット言語でのみ適切に機能します。そのため、I18Nが必要な場合は、intl
をインストールすることを強くお勧めします。
最初に、設定ファイルを作成する必要があります。
保存場所を決定します(例:./messages/
ディレクトリにcreate_i18n.php
という名前で)。プロジェクト内にディレクトリを作成し、プロジェクトのルートディレクトリから**ターミナル**(Windows:CMD)で次のコマンドを実行します。
./yii message/config-template ./messages/create_i18n.php
または、より詳細な制御のために
./yii message/config --languages=en-US --sourcePath=@app --messagePath=messages ./messages/create_i18n.php
新しく作成されたファイルで、翻訳する言語の配列を変更(または作成)します。
// array, required, list of language codes that the extracted messages
// should be translated to. For example, ['zh-CN', 'de'].
'languages' => [
'en-US',
'fr',
'pt'
],
必要に応じて、create_i18n.php
のルートディレクトリをmessagesディレクトリを指すように変更します - デフォルトはmessages
です。注記、上記のファイルがmessagesディレクトリにある場合(推奨)、この'messagePath' => __DIR__,
を変更しないでください。messages
のディレクトリを、例えば/config/
に変更する場合(良い考えではありません)、以下を使用できます。
// Root directory containing message translations.
'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'config',
必要な言語を編集した後、作成されたファイルは次のようになります。
<?php
return [
// string, required, root directory of all source files
'sourcePath' => __DIR__ . DIRECTORY_SEPARATOR . '..',
// array, required, list of language codes (in alphabetical order) that the extracted messages
// should be translated to. For example, ['zh-CN', 'de'].
'languages' => [
// to localise a particular language use the language code followed by the dialect in CAPS
'en-US', // USA English
'es',
'fr',
'it',
'pt',
],
/* 'languages' => [
'af', 'ar', 'az', 'be', 'bg', 'bs', 'ca', 'cs', 'da', 'de', 'el', 'es', 'et', 'fa', 'fi', 'fr', 'he', 'hi',
'pt-BR', 'ro', 'hr', 'hu', 'hy', 'id', 'it', 'ja', 'ka', 'kk', 'ko', 'kz', 'lt', 'lv', 'ms', 'nb-NO', 'nl',
'pl', 'pt', 'ru', 'sk', 'sl', 'sr', 'sr-Latn', 'sv', 'tg', 'th', 'tr', 'uk', 'uz', 'uz-Cy', 'vi', 'zh-CN',
'zh-TW'
], */
// string, the name of the function for translating messages.
// Defaults to 'Yii::t'. This is used as a mark to find the messages to be
// translated. You may use a string for single function name or an array for
// multiple function names.
'translator' => ['\Yii::t', 'Yii::t'],
// boolean, whether to sort messages by keys when merging new messages
// with the existing ones. Defaults to false, which means the new (untranslated)
// messages will be separated from the old (translated) ones.
'sort' => false,
// boolean, whether to remove messages that no longer appear in the source code.
// Defaults to false, which means these messages will NOT be removed.
'removeUnused' => false,
// boolean, whether to mark messages that no longer appear in the source code.
// Defaults to true, which means each of these messages will be enclosed with a pair of '@@' marks.
'markUnused' => true,
// array, list of patterns that specify which files (not directories) should be processed.
// If empty or not set, all files will be processed.
// See helpers/FileHelper::findFiles() for pattern matching rules.
// If a file/directory matches both a pattern in "only" and "except", it will NOT be processed.
'only' => ['*.php'],
// array, list of patterns that specify which files/directories should NOT be processed.
// If empty or not set, all files/directories will be processed.
// See helpers/FileHelper::findFiles() for pattern matching rules.
// If a file/directory matches both a pattern in "only" and "except", it will NOT be processed.
'except' => [
'.*',
'/.*',
'/messages',
'/migrations',
'/tests',
'/runtime',
'/vendor',
'/BaseYii.php',
],
// 'php' output format is for saving messages to php files.
'format' => 'php',
// Root directory containing message translations.
'messagePath' => __DIR__,
// boolean, whether the message file should be overwritten with the merged messages
'overwrite' => true,
/*
// File header used in generated messages files
'phpFileHeader' => '',
// PHPDoc used for array of messages with generated messages files
'phpDocBlock' => null,
*/
/*
// Message categories to ignore
'ignoreCategories' => [
'yii',
],
*/
/*
// 'db' output format is for saving messages to database.
'format' => 'db',
// Connection component to use. Optional.
'db' => 'db',
// Custom source message table. Optional.
// 'sourceMessageTable' => '{{%source_message}}',
// Custom name for translation message table. Optional.
// 'messageTable' => '{{%message}}',
*/
/*
// 'po' output format is for saving messages to gettext po files.
'format' => 'po',
// Root directory containing message translations.
'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages',
// Name of the file that will be used for translations.
'catalog' => 'messages',
// boolean, whether the message file should be overwritten with the merged messages
'overwrite' => true,
*/
];
/config/web.php
ファイルの編集 ¶web.php
ファイルの'id' => 'basic',
の下に以下を追加します。
'language' => 'en',
'sourceLanguage' => 'en',
注記:通常、英語から他の言語に翻訳する方が簡単で安価なため、常に'sourceLanguage' => 'en'
を使用する必要があります。sourceLanguage
が設定されていない場合、デフォルトは'en'
になります。
'components' => [...]
セクションに以下を追加します。
'i18n' => [
'translations' => [
'app*' => [
'class' => 'yii\i18n\PhpMessageSource', // Using text files (usually faster) for the translations
//'basePath' => '@app/messages', // Uncomment and change this if your folder is not called 'messages'
'sourceLanguage' => 'en',
'fileMap' => [
'app' => 'app.php',
'app/error' => 'error.php',
],
// Comment out in production version
// 'on missingTranslation' => ['app\components\TranslationEventHandler', 'handleMissingTranslation'],
],
],
],
次に、ビューファイルで翻訳するテキストをYiiに指示します。これは、コードにYii::t('app', '翻訳するテキスト')
を追加することで行います。
例として、/views/layouts/main.php
で、メニューラベルを次のように変更します。
'items' => [
// ['label' => 'Home', 'url' => ['/site/index']], // Orignal code
['label' => Yii::t('app', 'Home'), 'url' => ['/site/index']],
['label' => Yii::t('app', 'About'), 'url' => ['/site/about']],
['label' => Yii::t('app', 'Contact'), 'url' => ['/site/contact']],
Yii::$app->user->isGuest ? ['label' => Yii::t('app', 'Login'), 'url' => ['/site/login']] : '<li class="nav-item">'
. Html::beginForm(['/site/logout'])
. Html::submitButton(
// 'Logout (' . Yii::$app->user->identity->username . ')', // change this line as well to the following:
Yii::t('app', 'Logout ({username})'), ['username' => Yii::$app->user->identity->username]),
['class' => 'nav-link btn btn-link logout']
)
. Html::endForm()
. '</li>',
],
翻訳ファイルを作成するには、プロジェクトのルートディレクトリから**ターミナル**で次のコマンドを実行します。
./yii message ./messages/create_i18n.php
次に、メッセージを翻訳します。たとえば、フランス語の/messages/fr/app.php
では
'Home' => 'Accueil',
'About' => 'À propos',
...
これはいくつかの手順を踏みます。
各言語にはキー
と名前
が必要です。
キー
は、小文字のICU言語コードISO 639.1(オプションでISO 3166の大文字の国コード)です。例:
フランス語:
fr
またはカナダフランス語:fr-CA
ポルトガル語:
pt
またはブラジルポルトガル語:pt-BR
名前
はその言語での言語名です。例:フランス語の場合'Français'
、日本語の場合'日本語'
。ユーザーはブラウザの現在の言語を理解していない可能性があるため、これは重要です。
/config/params.php
で、必要な言語を含むlanguages
という名前の配列を作成します。例:
/* List of languages and their codes
*
* format:
* 'Language Code' => 'Language Name',
* e.g.
* 'fr' => 'Français',
*
* please use alphabetical order of language code
* Use the language name in the "user's" Language
* e.g.
* 'ja' => '日本の',
*/
'languages' => [
// 'da' => 'Danske',
// 'de' => 'Deutsche',
// 'en' => 'English', // NOT REQUIRED the sourceLanguage (i.e. the default)
'en-GB' => 'British English',
'en-US' => 'American English',
'es' => 'Español',
'fr' => 'Français',
'it' => 'Italiano',
// 'ja' => '日本の', // Japanese with the word "Japanese" in Kanji
// 'nl' => 'Nederlandse',
// 'no' => 'Norsk',
// 'pl' => 'Polski',
'pt' => 'Português',
// 'ru' => 'Русский',
// 'sw' => 'Svensk',
// 'zh' => '中国的',
],
デフォルトコントローラーである/controllers/SiteController.php
に、actionLanguage()
という名前のアクションを追加します。このアクションは言語を変更し、ブラウザがページリクエストとサイトへの再訪問で言語を「記憶」するようにCookieを設定します。
/**
* Called by the ajax handler to change the language and
* Sets a cookie based on the language selected
*
*/
public function actionLanguage()
{
$lang = Yii::$app->request->post('lang');
// If the language "key" is not NULL and exists in the languages array in params.php, change the language and set the cookie
if ($lang !== NULL && array_key_exists($lang, Yii::$app->params['languages']))
{
$expire = time() + (60 * 60 * 24 * 365); // 1 year - alter accordingly
Yii::$app->language = $lang;
$cookie = new yii\web\Cookie([
'name' => 'lang',
'value' => $lang,
'expire' => $expire,
]);
Yii::$app->getResponse()->getCookies()->add($cookie);
}
Yii::$app->end();
}
メソッドをPOST
に設定することを忘れないでください。behaviors()
内のactions
で、次のように'language' => ['post'],
を設定します。
'verbs' => [
'class' => VerbFilter::class,
'actions' => [
'logout' => ['post'],
'language' => ['post'],
],
],
各リクエストに対して正しい言語が提供されることを確認してください。
/components/ ディレクトリ
に、LanguageHandler.php
という名前のファイルを作成し、次のコードを追加します。
<?php
/*
* Copyright ©2023 JQL all rights reserved.
* http://www.jql.co.uk
*/
/*
Created on : 19-Nov-2023, 13:23:54
Author : John Lavelle
Title : LanguageHandler
*/
namespace app\components;
use yii\helpers\Html;
class LanguageHandler extends \yii\base\Behavior
{
public function events()
{
return [\yii\web\Application::EVENT_BEFORE_REQUEST => 'handleBeginRequest'];
}
public function handleBeginRequest($event)
{
if (\Yii::$app->getRequest()->getCookies()->has('lang') && array_key_exists(\Yii::$app->getRequest()->getCookies()->getValue('lang'), \Yii::$app->params['languages']))
{
// Get the language from the cookie if set
\Yii::$app->language = \Yii::$app->getRequest()->getCookies()->getValue('lang');
}
else
{
// Use the browser language - note: some systems use an underscore, if used, change it to a hyphen
\Yii::$app->language = str_replace('_', '-', HTML::encode(locale_accept_from_http($_SERVER['HTTP_ACCEPT_LANGUAGE'])));
}
}
}
/* End of file LanguageHandler.php */
/* Location: ./components/LanguageHandler.php */
/config/web.php
からLanguageHandler.php
を呼び出す ¶'params' => $params,
の直上または直下に次の行を追加することで、/config/web.php
からLanguageHandler.php
ファイルを「呼び出します」。
// Update the language on selection
'as beforeRequest' => [
'class' => 'app\components\LanguageHandler',
],
/views/layouts/main.php
に言語メニュー項目を追加する ¶main.php
はBootstrapを使用してメニューを作成します。ユーザーが言語を選択できるように、メニューに項目(ドロップダウン)を追加する必要があります。
main.php
の「uses」セクションにuse yii\helpers\Url;
を追加します。
echo Nav::widget([...])
の直上に、次のコードを追加します。
// Get the languages and their keys, also the current route
foreach (Yii::$app->params['languages'] as $key => $language)
{
$items[] = [
'label' => $language, // Language name in it's language - already translated
'url' => Url::to(['site/index']), // Route
'linkOptions' => ['id' => $key, 'class' => 'language'], // The language "key"
];
}
次のセクション
echo Nav::widget([...])`
と
'options' => ['class' => 'navbar-nav ms-auto'], // ms-auto aligns the menu right`
の間
'items' => [...]
に
'encodeLabels' => false, // Required to enter HTML into the labels
次のように追加します。
echo Nav::widget([
'options' => ['class' => 'navbar-nav ms-auto'], // ms-auto aligns the menu right
'encodeLabels' => false, // Required to enter HTML into the labels
'items' => [
['label' => Yii::t('app', 'Home'), 'url' => ['/site/index']],
...
次に、ドロップダウンを追加します。これは'items' => [...]
内の任意の場所に配置できます。
// Dropdown Nav Menu: https://yii.dokyumento.jp/doc/api/2.0/yii-widgets-menu
[
'label' => Yii::t('app', 'Language')),
'url' => ['#'],
'options' => ['class' => 'language', 'id' => 'languageTop'],
'encodeLabels' => false, // Optional but required to enter HTML into the labels for images
'items' => $items, // add the languages into the Dropdown
],
NavBarのmain.php
のコードは、次のようになります。
NavBar::begin([
'brandLabel' => Yii::$app->name, // set in /config/web.php
'brandUrl' => Yii::$app->homeUrl,
'options' => ['class' => 'navbar-expand-md navbar-dark bg-dark fixed-top']
]);
// Get the languages and their keys, also the current route
foreach (Yii::$app->params['languages'] as $key => $language)
{
$items[] = [
'label' => $language, // Language name in it's language
'url' => Url::to(['site/index']), // Current route so the page refreshes
'linkOptions' => ['id' => $key, 'class' => 'language'], // The language key
];
}
echo Nav::widget([
'options' => ['class' => 'navbar-nav ms-auto'], // ms-auto aligns the menu right
'encodeLabels' => false, // Required to enter HTML into the labels
'items' => [
['label' => Yii::t('app', 'Home'), 'url' => ['/site/index']],
['label' => Yii::t('app', 'About'), 'url' => ['/site/about']],
['label' => Yii::t('app', 'Contact'), 'url' => ['/site/contact']],
// Dropdown Nav Menu: https://yii.dokyumento.jp/doc/api/2.0/yii-widgets-menu
[
'label' => Yii::t('app', 'Language') ,
'url' => ['#'],
'options' => ['class' => 'language', 'id' => 'languageTop'],
'encodeLabels' => false, // Required to enter HTML into the labels
'items' => $items, // add the languages into the Dropdown
],
Yii::$app->user->isGuest ? ['label' => Yii::t('app', 'Login'), 'url' => ['/site/login']] : '<li class="nav-item">'
. Html::beginForm(['/site/logout'])
. Html::submitButton(
// 'Logout (' . Yii::$app->user->identity->username . ')',
Yii::t('app', 'Logout ({username})', ['username' => Yii::$app->user->identity->username]),
['class' => 'nav-link btn btn-link logout']
)
. Html::endForm()
. '</li>',
],
]);
NavBar::end();
言語フラグや画像を言語名の横に表示する必要がある場合は、このドキュメントの最後に記載されている「オプション項目」を参照してください。
言語アクションactionLanguage()
を呼び出すには、JavaScriptファイルでAjax呼び出しを行います。
/web/js/
にlanguage.js
という名前のファイルを作成します。
ファイルに次のコードを追加します。
/*
* Copyright ©2023 JQL all rights reserved.
* http://www.jql.co.uk
*/
/**
* Set the language
*
* @returns {undefined}
*/
$(function () {
$(document).on('click', '.language', function (event) {
event.preventDefault();
let lang = $(this).attr('id'); // Get the language key
/* if not the top level, set the language and reload the page */
if (lang !== 'languageTop') {
$.post(document.location.origin + '/site/language', {'lang': lang}, function (data) {
location.reload(true);
});
}
});
});
JavaScriptファイルをアセットに追加するには、プロジェクトディレクトリの/assets/AppAsset.php
を変更します。public $js = []
に'js/language.js'
を追加します。次のようになります。
public $js = [
'js/language.js',
];
これで、プロジェクトで国際化が機能するはずです。
以下はオプションですが、ユーザーと開発者の両方にとって役立つ場合があります。
Yiiは、Yii::t('app', '翻訳するテキスト')
ブロック内の特定のテキストに翻訳が存在するかどうかを確認できます。
2つの手順があります。
**A.** /config/web.php
で次の行のコメントを外します。
// 'on missingTranslation' => ['app\components\TranslationEventHandler', 'handleMissingTranslation'],
**B.** TranslationEventHandlerを作成します。
/components/
に、TranslationEventHandler.php
という名前のファイルを作成し、次のコードを追加します。
<?php
/**
* TranslationEventHandler
*
* @copyright © 2023, John Lavelle Created on : 14 Nov 2023, 16:05:32
*
*
* Author : John Lavelle
* Title : TranslationEventHandler
*/
// Change the Namespace (app, frontend, backend, console etc.) if necessary (default in Yii Basic is "app").
namespace app\components;
use yii\i18n\MissingTranslationEvent;
/**
* TranslationEventHandler
*
*
* @author John Lavelle
* @since 1.0 // Update version number
*/
class TranslationEventHandler
{
/**
* Adds a message to missing translations in Development Environment only
*
* @param MissingTranslationEvent $event
*/
public static function handleMissingTranslation(MissingTranslationEvent $event)
{
// Only check in the development environment
if (YII_ENV_DEV)
{
$event->translatedMessage = "@MISSING: {$event->category}.{$event->message} FOR LANGUAGE {$event->language} @";
}
}
}
翻訳がない場合、テキストは次のようなメッセージに置き換えられます。
@MISSING: app.Logout (John) FOR LANGUAGE fr @
ここでは、Yiiはフランス語の翻訳が
Yii::t('app', 'Logout ({username})', ['username' => Yii::$app->user->identity->username]),
これは非常に役立つ推奨事項であり、ユーザーが正しい言語を見つけるのに役立ちます。これにはいくつかの手順があります。
**a.** フラグの画像を作成します。
画像は幅25px、高さ15pxである必要があります。画像は、params.phpの言語配列内の言語キーと同じ名前である**必要があります**。たとえば、fr.png
またはen-US.png
です。画像の種類が「.png」でない場合は、以下の**b.**の手順でコードを正しいファイル拡張子に変更してください。
画像は/web/images/flags/
ディレクトリに配置します。
**b.** /views/layouts/main.php
のコードを次のように変更します。
<header id="header">
<?php
NavBar::begin([
'brandLabel' => Yii::$app->name,
'brandUrl' => Yii::$app->homeUrl,
'options' => ['class' => 'navbar-expand-md navbar-dark bg-dark fixed-top']
]);
// Get the languages and their keys, also the current route
foreach (Yii::$app->params['languages'] as $key => $language)
{
$items[] = [
// Display the image before the language name
'label' => Html::img('/images/flags/' . $key . '.png', ['alt' => 'flag ' . $language, 'class' => 'inline-block align-middle', 'title' => $language,]) . ' ' . $language, // Language name in it's language
'url' => Url::to(['site/index']), // Route
'linkOptions' => ['id' => $key, 'class' => 'language'], // The language key
];
}
echo Nav::widget([
'options' => ['class' => 'navbar-nav ms-auto'], // ms-auto aligns the menu right
'encodeLabels' => false, // Required to enter HTML into the labels
'items' => [
['label' => Yii::t('app', 'Home'), 'url' => ['/site/index']],
['label' => Yii::t('app', 'About'), 'url' => ['/site/about']],
['label' => Yii::t('app', 'Contact'), 'url' => ['/site/contact']],
// Dropdown Nav Menu: https://yii.dokyumento.jp/doc/api/2.0/yii-widgets-menu
[
// Display the current language "flag" after the Dropdown title (before the caret)
'label' => Yii::t('app', 'Language') . ' ' . Html::img('@web/images/flags/' . Yii::$app->language . '.png', ['class' => 'inline-block align-middle', 'title' => Yii::$app->language]),
'url' => ['#'],
'options' => ['class' => 'language', 'id' => 'languageTop'],
'encodeLabels' => false, // Required to enter HTML into the labels
'items' => $items, // add the languages into the Dropdown
],
Yii::$app->user->isGuest ? ['label' => Yii::t('app', 'Login'), 'url' => ['/site/login']] : '<li class="nav-item">'
. Html::beginForm(['/site/logout'])
. Html::submitButton(
// 'Logout (' . Yii::$app->user->identity->username . ')',
Yii::t('app', 'Logout ({username})', ['username' => Yii::$app->user->identity->username]),
['class' => 'nav-link btn btn-link logout']
)
. Html::endForm()
. '</li>',
],
]);
NavBar::end();
?>
</header>
以上です!楽しんでください…
詳細については、以下を参照してください。
このコードを使用する場合は、次のようにクレジットしてください。
国際化 (i18n) メニューコードを提供:JQL、https://visualaccounts.co.uk ©2023 JQL
ライセンス(BSD-3-Clause ライセンス)
著作権表示
国際化 (i18n) メニューコードを提供:JQL、https://visualaccounts.co.uk ©2023 JQL 無断複製を禁じます
ソースおよびバイナリ形式での再配布と使用は、以下の条件を満たす場合に限り許可されます。
ソースコードの再配布には、上記の著作権表示、この条件リスト、および以下の免責事項を保持する必要があります。
バイナリ形式での再配布には、ドキュメントおよび/または配布物に含まれるその他の資料に、上記の著作権表示、この条件リスト、および以下の免責事項を複製する必要があります。
John Lavelle、JQL、Visual Accounts、およびその貢献者の名前は、このソフトウェアから派生した製品を推奨または宣伝するために、特別な書面による許可なしに使用することはできません。
「JQL のすべてのコードとソフトウェア(およびその作成者のものも含む)は、いかなる種類の保証もなく「現状のまま」提供されます。法律で許可される最大限の範囲で、作成者と発行者およびその代理人は、明示的または黙示的を問わず、商品性および特定の目的への適合性の黙示的な保証を含むがこれらに限定されないすべての保証を明示的に否認します。コードに関して、作成者と発行者およびその代理人は、コードの使用から直接的または間接的に生じるいかなる損失または損害についても、作成者および/または発行者とその代理人がそのような損害の可能性について知らされていた場合であっても、責任を負いません。前述を制限することなく、作成者と発行者およびその代理人は、利益の損失、業務の中断、機器またはデータの損害、業務の中断、または直接的、間接的、特別、偶発的、結果的、その他の損害を含むがこれらに限定されないその他の商業的損害について責任を負いません。」
]]>これは、yii2 基本アプリケーションテンプレートを使用して作成されたアプリケーションテンプレートであり、私の拡張機能 slideradmin の使用方法を示しています。
バリデータを作成する方法は複数ありますが、ここでは正規表現またはJavaScript正規表現またはRegExpを使用してバリデータを作成します。この記事では、最も頻繁に使用される式について説明します。
**手順1:** 以下のように、またはValidatorのように、バリデータ用の新しいクラスを作成します。
最初の例である10桁の電話番号の検証を参照してください。
<?php
namespace common\validators;
use yii\validators\Validator;
class MobileValidator extends Validator {
public function validateAttribute($model, $attribute) {
if (isset($model->$attribute) and $model->$attribute != '') {
if (!preg_match('/^[123456789]\d{9}$/', $model->$attribute)) {
$this->addError($model, $attribute, 'In Valid Mobile / Phone number');
}
}
}
}
ここでは、必要に応じてさまざまな正規表現を記述できます。`
php preg_match('/^[123456789]\d{9}$/', $model-> $attribute) `
**手順2:** バリデータの使用方法
バリデータの使用方法をご存知の方も多いと思いますが、ここでは使用方法の例を示します。
モデルクラスに新しいルールを追加します。`
php [['mobile'],\common\validators\MobileValidator::class], [['mobile'], 'string', 'max' => 10],
So It's Very Simple to use a Custom Validator.
As I Told you Earlier that i show you some more Example for Using Regular Expression Validator Just Replace these string in preg_match.
1. Aadhar Number Validator
```php
preg_match('/^[2-9]{1}[0-9]{3}[0-9]{4}[0-9]{4}$/', $model->$attribute)
銀行口座番号バリデータ `
php preg_match("/^[0-9]{9,18}+$/", $model-> $attribute) `
銀行IFSCコードバリデータ `
php preg_match("/^[A-Z]{4}0[A-Z0-9]{6}$/", $model-> $attribute) `
パンカード番号バリデータ `
php preg_match('/^([a-zA-Z]){5}([0-9]){4}([a-zA-Z]){1}?$/', $model-> $attribute) `
郵便番号バリデータ `
php preg_match('/^[0-9]{6}+$/', $model-> $attribute) `
GSTINバリデータ `
php preg_match("/^([0][1-9]|[1-2][0-9]|[3][0-5])([a-zA-Z]{5}[0-9]{4}[a-zA-Z]{1}[1-9a-zA-Z]{1}[zZ]{1}[0-9a-zA-Z]{1})+$/", $model-> $attribute) `
これは他のタイプのカスタムバリデータです。
<?php
namespace common\validators;
use yii\validators\Validator;
/**
* Class Word500Validator
* @author Aayush Saini <aayushsaini9999@gmail.com>
*/
class Word500Validator extends Validator
{
public function validateAttribute($model, $attribute)
{
if ($model->$attribute != '') {
if (str_word_count($model->$attribute) > 500) {
$this->addError($model, $attribute, $model->getAttributeLabel($attribute) . ' length can not exceeded 500 words.');
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return $model->errors;
}
}
}
}
この記事を読んだ後、必要に応じてあらゆる種類のバリデータを作成できるようになっていると想定しています。
:) ご覧いただきありがとうございます。
]]>GridViewでフッターに列の合計を表示する `
PHP use yii\grid\DataColumn;
/**
@author shiv / class TSumColumn extends DataColumn { public function getDataCellValue($model, $key, $index) {
$value = parent::getDataCellValue($model, $key, $index);
if ( is_numeric($value))
{
$this->footer += $value;
}
return $value;
}
}
`
GridViewでフッターを有効にする必要があります。
echo GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'showFooter' => true,
また、列クラスを変更します。
[
'class' => TSumColumn::class,
'attribute' => 'amount'
],
グリッドのフッターに合計が表示されます。必要に応じて複数の列に適用できます。
]]>JSONをHTMLテーブルに直接表示するのに役立つ呼び出しがあります。
Json2Table::formatContent($json);
Json2Tableクラスのコードを次に示します。
============================================
/**
* Class convert Json to html table. It help view json data directly.
* @author shiv
*
*/
class Json2Table
{
public static function formatContent($content, $class = 'table table-bordered')
{
$html = "";
if ($content != null) {
$arr = json_decode(strip_tags($content), true);
if ($arr && is_array($arr)) {
$html .= self::arrayToHtmlTableRecursive($arr, $class);
}
}
return $html;
}
public static function arrayToHtmlTableRecursive($arr, $class = 'table table-bordered')
{
$str = "<table class='$class'><tbody>";
foreach ($arr as $key => $val) {
$str .= "<tr>";
$str .= "<td>$key</td>";
$str .= "<td>";
if (is_array($val)) {
if (! empty($val)) {
$str .= self::arrayToHtmlTableRecursive($val, $class);
}
} else {
$val = nl2br($val);
$str .= "<strong>$val</strong>";
}
$str .= "</td></tr>";
}
$str .= "</tbody></table>";
return $str;
}
}
]]>インドにはアドハー番号があり、入力として検証する必要がある場合があります。そこで、yii2用のバリデータを作成しました。
use yii\validators\Validator;
class TAadharNumberValidator extends Validator
{
public $regExPattern = '/^\d{4}\s\d{4}\s\d{4}$/';
public function validateAttribute($model, $attribute)
{
if (preg_match($this->regExPattern, $model->$attribute)) {
$model->addError($attribute, 'Not valid Aadhar Card Number');
}
}
}
]]>皆さん、こんにちは。この記事では、YII2の面接で面接官が最もよく尋ねる質問について、私の経験を共有しました。
これらは、面接を受ける際に面接官があなたに尋ねることができる最も一般的な質問です。
他に質問があれば、コメントで共有してください!!!!
]]>私のサイトの1つがスパムボットで溢れかえっており、その結果、Gmailから私のメールドメインに悪いスコアが付けられ、@gmail
アドレスにメールを送信できなくなりました。私のメールからでも、私のシステムからでも、私がホストしている他のドメインやウェブサイトからでも送信できませんでした…
私は1つのサイトからすべてのスパムボットの活動を削除し、Gmailサポートフォーラムを通じて決定への異議を申し立てましたが、それでも@gmail.com
のメールボックスを持つ顧客と連絡を取ることができず、ドメインのスコアを元の状態に戻す方法がないようです。
ほぼ2週間経ちましたが、私のドメインのスコアはhttps://postmaster.google.com/で悪いままです。
Googleさん、ありがとうございます :(
その結果、購入通知、ライセンス期限切れ通知、その他の通知を顧客に送信する方法を考え出す必要がありました。
私はPHPのYii2フレームワークを使用しており、それは非常に簡単でした。
通知を送信するには@gmail.comアカウントが必要です。重要なのは、アカウントを作成した後、セキュリティが低いアプリへのアクセスオプションを有効にする必要があることです。
これにより、Gmail SMTPサーバー経由でメールを送信できます。
Yii2フレームワークディレクトリで、設定ファイル/common/config/Main.php
(アドバンスドテーマを使用しています)を変更し、カスタムメール送信コンポーネント(任意の名前を付けることができます)を含めます。
<?php
return [
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
...
'components' => [
'mailerGmail' => [
'class' => 'yii\swiftmailer\Mailer',
'viewPath' => '@common/mail',
'useFileTransport' => false,
'transport' => [
'class' => 'Swift_SmtpTransport',
'host' => 'smtp.gmail.com',
'username' => 'gmail.helper.account',
'password' => 'PUT-YOUR-PASSWORD-HERE',
'port' => '587',
'encryption' => 'tls',
],
],
],
];
私はYii::$app->Custom
として登録されているコンポーネントの1つにヘルパー関数を追加しました。これは、配信メールのドメイン名に応じてデフォルトのメーラーインスタンスを返します。
また、メールに@gmail.com
文字列が含まれていない場合でも、GmailのMX
サーバーを使用してメール処理を行っている場合を検出するようにコードを更新しました。
検出は、PHPの組み込み関数getmxrr()
を使用してドメインのメールサーバーレコードをチェックすることによって行われ、失敗した場合は、Google DNSサービスAPIにリモートGET
クエリを送信してMX
レコードをチェックします。
////////////////////////////////////////////////////////////////////////////////
//
// get default mailer depending on the provided email address
//
////////////////////////////////////////////////////////////////////////////////
public function getMailer($email)
{
// detect if the email or domain is using Gmail to send emails
if (Yii::$app->params['forwardGmail'])
{
// detect @gmail.com domain first
if (str_ends_with($email, "@gmail.com"))
{
return Yii::$app->mailerGmail;
}
// extract domain name
$parts = explode('@', $email);
$domain = array_pop($parts);
// check DNS using local server requests to DNS
// if it fails query Google DNS service API (might have limits)
if (getmxrr($domain, $mx_records))
{
foreach($mx_records as $record)
{
if (stripos($record, "google.com") !== false || stripos($record, "googlemail.com") !== false)
{
return Yii::$app->mailerGmail;
}
}
// return default mailer (if there were records detected but NOT google)
return Yii::$app->mailer;
}
// make DNS request
$client = new Client();
$response = $client->createRequest()
->setMethod('GET')
->setUrl('https://dns.google.com/resolve')
->setData(['name' => $domain, 'type' => 'MX'])
->setOptions([
'timeout' => 5, // set timeout to 5 seconds for the case server is not responding
])
->send();
if ($response->isOk)
{
$parser = new JsonParser();
$data = $parser->parse($response);
if ($data && array_key_exists("Answer", $data))
{
foreach ($data["Answer"] as $key => $value)
{
if (array_key_exists("name", $value) && array_key_exists("data", $value))
{
if (stripos($value["name"], $domain) !== false)
{
if (stripos($value["data"], "google.com") !== false || stripos($value["data"], "googlemail.com") !== false)
{
return Yii::$app->mailerGmail;
}
}
}
}
}
}
}
// return default mailer
return Yii::$app->mailer;
}
ドメインが@gmail.com
で終わっている場合、またはドメインがGmailのメールシステムを使用している場合、mailerGmail
インスタンスが使用され、それ以外の場合はデフォルトのメール送信コンポーネントYii::$app->mailer
が使用されます。
/**
* Sends an email to the specified email address using the information collected by this model.
*
* @return boolean whether the email was sent
*/
public function sendEmail()
{
// find all active subscribers
$message = Yii::$app->Custom->getMailer($this->email)->compose();
$message->setTo([$this->email => $this->name]);
$message->setFrom([\Yii::$app->params['supportEmail'] => "Bartosz Wójcik"]);
$message->setSubject($this->subject);
$message->setTextBody($this->body);
$headers = $message->getSwiftMessage()->getHeaders();
// message ID header (hide admin panel)
$msgId = $headers->get('Message-ID');
$msgId->setId(md5(time()) . '@pelock.com');
$result = $message->send();
return $result;
}
これは一時的な解決策であり、この方法では大量メールを送信できないことに注意する必要があります。Gmailは新しいメールボックスにもいくつかの制限を課しています。
ドメインがその悪評の規模に達した場合、そこから抜け出す簡単な方法はないようです。Gmailのサポートフォーラムで読んだところによると、一部の人々は1ヶ月以上待つことでGmailがドメインのブロックを解除するのを待っているものの、結果も連絡もなく待っているようです。私のドメインは他のブロックされたRBLリスト(スパムリスト)には掲載されていません。Gmailだけがブロックしていますが、Googleの影響力の大きさを理解するには十分です。Googleは、修正する機会もなく、一瞬であなたのビジネスを台無しにする可能性があります…
]]>JWTはJSON Web Tokenの略です。たとえば、セッションの代わりに、APIと通信するブラウザー内のログインを維持するために使用されます。ブラウザーセッションはCSRFセキュリティの問題に対して脆弱であるためです。JWTは、OAuth認証メカニズムを設定するよりも複雑ではありません。
この概念は2つのトークンに依存しています。
このトークンは\sizeg\jwt\Jwt::class
を使用して生成されます。これはサーバー側に保存されません。そして、Authorization
ヘッダーを介して後続のすべてのAPIリクエストで送信されます。では、ユーザーはどのように識別されるのでしょうか?JWTの内容にはユーザーIDが含まれています。この値を盲目的に信頼します。
このトークンはログイン時にのみ生成され、user_refresh_token
テーブルに保存されます。ユーザーはデータベースに複数のRefreshTokenを持つことができます。
/auth/login
エンドポイントを介してユーザーが初めてログインする: ¶資格情報が正しい場合、actionLogin()
メソッドでは2つのことが発生します。
/auth/refresh-token
パスに制限されたhttpOnly
クッキーとして返されます。JWTはブラウザのlocalStorage
に保存され、それ以降はすべてのリクエストで送信する必要があります。RefreshTokenはクッキーにありますが、(httpOnly
であるため)Javascriptを介して読み取ったり、アクセスしたり、改ざんしたりすることはできません。
しばらくすると、JWTは最終的に期限切れになります。この場合、APIは401 - Unauthorized
を返す必要があります。アプリのHTTPクライアント(例:Axios)では、401ステータスを検出し、失敗したリクエストをキューに保存し、/auth/refresh-token
エンドポイントを呼び出すインターセプターを追加します。
呼び出されると、このエンドポイントはクッキーを介してRefreshTokenを受け取ります。その後、テーブルでこれが有効なRefreshTokenかどうか、関連付けられたユーザーIDは誰かを確認し、新しいJWTを生成してJSONとして返します。
HTTPクライアントは、この新しいJWTを受け取り、localStorage
で置き換え、リクエストキューを巡回し、失敗したすべてのリクエストを再生する必要があります。
/auth/sessions
エンドポイントを設定して、現在のユーザーのすべてのRefreshTokenを返すようにすると、接続されているすべてのデバイスのテーブルを表示できます。
その後、ユーザーは行を削除できます(つまり、テーブルから特定のRefreshTokenを削除できます)。侵害されたトークンの期限が切れた後(例:5分後)に更新が試行されると、失敗します。これが、JWTを非常に短命にする理由です。
これはJWTの設計上の目的です。信頼できるほど安全です。大規模な設定(例:Google)では、認証は別の認証サーバーによって処理されます。これは、トークンと引き換えにログイン/パスワードを受け入れる役割を担っています。
たとえば、Gmailでは、後で認証はまったく実行されません。GoogleはJWTを読み取り、JWTが期限切れでない限り、メールへのアクセスを提供します。期限切れの場合は、認証サーバーにリダイレクトされます。
これが、しばらく前にGoogle認証に障害が発生したとき、一部のユーザーは問題なくGmailを使用できたのに、他のユーザーはまったく接続できなかった理由です。有効なJWTと期限切れのJWT。
https
対応サイトが必要です。CREATE TABLE `user_refresh_tokens` (
`user_refresh_tokenID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`urf_userID` INT(10) UNSIGNED NOT NULL,
`urf_token` VARCHAR(1000) NOT NULL,
`urf_ip` VARCHAR(50) NOT NULL,
`urf_user_agent` VARCHAR(1000) NOT NULL,
`urf_created` DATETIME NOT NULL COMMENT 'UTC',
PRIMARY KEY (`user_refresh_tokenID`)
)
COMMENT='For JWT authentication process';
composer require sizeg/yii2-jwt
AuthController.php
というコントローラーを使用します。任意の名前を付けることができます。user_refresh_tokens
テーブルのActiveRecordモデルを作成します。クラス名app\models\UserRefreshToken
を使用します。
すべてのコントローラーでCSRF検証を無効にする
このプロパティを追加する:public $enableCsrfValidation = false;
/config/params.php
にJWTパラメーターを追加する'jwt' => [
'issuer' => 'https://api.example.com', //name of your project (for information only)
'audience' => 'https://frontend.example.com', //description of the audience, eg. the website using the authentication (for info only)
'id' => 'UNIQUE-JWT-IDENTIFIER', //a unique identifier for the JWT, typically a random string
'expire' => 300, //the short-lived JWT token is here set to expire after 5 min.
],
/components
にJwtValidationData
クラスを追加する<?php
namespace app\components;
use Yii;
class JwtValidationData extends \sizeg\jwt\JwtValidationData {
/**
* @inheritdoc
*/
public function init() {
$jwtParams = Yii::$app->params['jwt'];
$this->validationData->setIssuer($jwtParams['issuer']);
$this->validationData->setAudience($jwtParams['audience']);
$this->validationData->setId($jwtParams['id']);
parent::init();
}
}
/config/web.php
の設定に追加する $config = [
'components' => [
...
'jwt' => [
'class' => \sizeg\jwt\Jwt::class,
'key' => 'SECRET-KEY', //typically a long random string
'jwtValidationData' => \app\components\JwtValidationData::class,
],
...
],
];
AuthController.php
では、login
、refresh-token
、options
(ブラウザーがクロスサイトOPTIONSリクエストを送信する場合)など、認証を必要としないアクションを除外する必要があります。 public function behaviors() {
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => \sizeg\jwt\JwtHttpBearerAuth::class,
'except' => [
'login',
'refresh-token',
'options',
],
];
return $behaviors;
}
generateJwt()
メソッドとgenerateRefreshToken()
メソッドをAuthController.php
に追加します。ログイン/refresh-tokenアクションで使用します。ユーザーモデルのクラス名が異なる場合は調整してください。 private function generateJwt(\app\models\User $user) {
$jwt = Yii::$app->jwt;
$signer = $jwt->getSigner('HS256');
$key = $jwt->getKey();
$time = time();
$jwtParams = Yii::$app->params['jwt'];
return $jwt->getBuilder()
->issuedBy($jwtParams['issuer'])
->permittedFor($jwtParams['audience'])
->identifiedBy($jwtParams['id'], true)
->issuedAt($time)
->expiresAt($time + $jwtParams['expire'])
->withClaim('uid', $user->userID)
->getToken($signer, $key);
}
/**
* @throws yii\base\Exception
*/
private function generateRefreshToken(\app\models\User $user, \app\models\User $impersonator = null): \app\models\UserRefreshToken {
$refreshToken = Yii::$app->security->generateRandomString(200);
// TODO: Don't always regenerate - you could reuse existing one if user already has one with same IP and user agent
$userRefreshToken = new \app\models\UserRefreshToken([
'urf_userID' => $user->id,
'urf_token' => $refreshToken,
'urf_ip' => Yii::$app->request->userIP,
'urf_user_agent' => Yii::$app->request->userAgent,
'urf_created' => gmdate('Y-m-d H:i:s'),
]);
if (!$userRefreshToken->save()) {
throw new \yii\web\ServerErrorHttpException('Failed to save the refresh token: '. $userRefreshToken->getErrorSummary(true));
}
// Send the refresh-token to the user in a HttpOnly cookie that Javascript can never read and that's limited by path
Yii::$app->response->cookies->add(new \yii\web\Cookie([
'name' => 'refresh-token',
'value' => $refreshToken,
'httpOnly' => true,
'sameSite' => 'none',
'secure' => true,
'path' => '/v1/auth/refresh-token', //endpoint URI for renewing the JWT token using this refresh-token, or deleting refresh-token
]));
return $userRefreshToken;
}
AuthController.php
にログインアクションを追加する public function actionLogin() {
$model = new \app\models\LoginForm();
if ($model->load(Yii::$app->request->getBodyParams()) && $model->login()) {
$user = Yii::$app->user->identity;
$token = $this->generateJwt($user);
$this->generateRefreshToken($user);
return [
'user' => $user,
'token' => (string) $token,
];
} else {
return $model->getFirstErrors();
}
}
AuthController.php
にrefresh-tokenアクションを追加します。JWTが期限切れになった場合はPOST /auth/refresh-token
を、ユーザーがログアウトをリクエストした場合はDELETE /auth/refresh-token
を呼び出します(その後、クライアントのlocalStorage
からJWTトークンを削除します)。 public function actionRefreshToken() {
$refreshToken = Yii::$app->request->cookies->getValue('refresh-token', false);
if (!$refreshToken) {
return new \yii\web\UnauthorizedHttpException('No refresh token found.');
}
$userRefreshToken = \app\models\UserRefreshToken::findOne(['urf_token' => $refreshToken]);
if (Yii::$app->request->getMethod() == 'POST') {
// Getting new JWT after it has expired
if (!$userRefreshToken) {
return new \yii\web\UnauthorizedHttpException('The refresh token no longer exists.');
}
$user = \app\models\User::find() //adapt this to your needs
->where(['userID' => $userRefreshToken->urf_userID])
->andWhere(['not', ['usr_status' => 'inactive']])
->one();
if (!$user) {
$userRefreshToken->delete();
return new \yii\web\UnauthorizedHttpException('The user is inactive.');
}
$token = $this->generateJwt($user);
return [
'status' => 'ok',
'token' => (string) $token,
];
} elseif (Yii::$app->request->getMethod() == 'DELETE') {
// Logging out
if ($userRefreshToken && !$userRefreshToken->delete()) {
return new \yii\web\ServerErrorHttpException('Failed to delete the refresh token.');
}
return ['status' => 'ok'];
} else {
return new \yii\web\UnauthorizedHttpException('The user is inactive.');
}
}
findIdentityByAccessToken()
を調整して、JWTのuidクレームを使用して認証済みユーザーを検索します。 public static function findIdentityByAccessToken($token, $type = null) {
return static::find()
->where(['userID' => (string) $token->getClaim('uid') ])
->andWhere(['<>', 'usr_status', 'inactive']) //adapt this to your needs
->one();
}
afterSave()
内)に、ユーザーのすべてのRefreshTokenを削除することも忘れないでください。 public function afterSave($isInsert, $changedOldAttributes) {
// Purge the user tokens when the password is changed
if (array_key_exists('usr_password', $changedOldAttributes)) {
\app\models\UserRefreshToken::deleteAll(['urf_userID' => $this->userID]);
}
return parent::afterSave($isInsert, $changedOldAttributes);
}
user_refresh_tokens
のレコードを一覧表示し、ユーザーが選択したレコードを削除できるようにします。Axiosインターセプター(React Reduxを使用?)。
let isRefreshing = false;
let refreshSubscribers: QueuedApiCall[] = [];
const subscribeTokenRefresh = (cb: QueuedApiCall) =>
refreshSubscribers.push(cb);
const onRefreshed = (token: string) => {
console.log("refreshing ", refreshSubscribers.length, " subscribers");
refreshSubscribers.map(cb => cb(token));
refreshSubscribers = [];
};
api.interceptors.response.use(undefined,
error => {
const status = error.response ? error.response.status : false;
const originalRequest = error.config;
if (error.config.url === '/auth/refresh-token') {
console.log('REDIRECT TO LOGIN');
store.dispatch("logout").then(() => {
isRefreshing = false;
});
}
if (status === API_STATUS_UNAUTHORIZED) {
if (!isRefreshing) {
isRefreshing = true;
console.log('dispatching refresh');
store.dispatch("refreshToken").then(newToken => {
isRefreshing = false;
onRefreshed(newToken);
}).catch(() => {
isRefreshing = false;
});
}
return new Promise(resolve => {
subscribeTokenRefresh(token => {
// replace the expired token and retry
originalRequest.headers["Authorization"] = "Bearer " + token;
resolve(axios(originalRequest));
});
});
}
return Promise.reject(error);
}
);
Mehdi Achourさんに、このチュートリアルの多くの資料作成においてご協力いただきました。
]]>wikiではファイルごとに最大長があるため、記事は複数のファイルに分割されています。
翻訳の仕組みについては既に記述しました。ここでは、言語を切り替えてURLに保存する方法を示します。そこで、メインメニューに言語切り替えを追加しましょう。
echo Nav::widget([
'options' => ['class' => 'navbar-nav navbar-right'],
'items' => [
['label' => 'Language', 'items' => [
['label' => 'German' , 'url' => \yii\helpers\Url::current(['sys_lang' => 'de']) ],
['label' => 'English', 'url' => \yii\helpers\Url::current(['sys_lang' => 'en']) ],
],
]
新しいGETパラメータ「sys_lang」を処理し、新しい言語を保持するためにセッションに保存する必要があります。最適な方法は、すべてのコントローラーによって拡張されるBaseControllerを作成することです。その内容は次のようになります。
<?php
namespace app\controllers;
use yii\web\Controller;
class _BaseController extends Controller {
public function beforeAction($action) {
if (isset($_GET['sys_lang'])) {
switch ($_GET['sys_lang']) {
case 'de':
$_SESSION['sys_lang'] = 'de-DE';
break;
case 'en':
$_SESSION['sys_lang'] = 'en-US';
break;
}
}
if (!isset($_SESSION['sys_lang'])) {
$_SESSION['sys_lang'] = \Yii::$app->sourceLanguage;
}
\Yii::$app->language = $_SESSION['sys_lang'];
return true;
}
}
sys_langをドメイン名のすぐ後ろのURLに含めたい場合は、config/web.phpに次のURLルールを作成できます。
'components' => [
// ...
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
// https://yii.dokyumento.jp/doc/api/2.0/yii-web-urlmanager#$rules-detail
// https://stackoverflow.com/questions/2574181/yii-urlmanager-language-in-url
// https://yii.dokyumento.jp/wiki/294/seo-conform-multilingual-urls-language-selector-widget-i18n
'<sys_lang:[a-z]{2}>' => 'site',
'<sys_lang:[a-z]{2}>/<controller:\w+>' => '<controller>',
'<sys_lang:[a-z]{2}>/<controller:\w+>/<action:\w+>' => '<controller>/<action>',
],
],
],
これで、言語切り替えリンクは次のようなURLを生成します。http://myweb.com/en/site/index。ルールがない場合、リンクは次のようになります。http://myweb.com/site/index?sys_lang=en。したがって、このルールは双方向に機能します。URLが解析され、コントローラーが呼び出されるときにも、URLヘルパーを使用して新しいURLが作成されるときにも機能します。
大量の変更にはNotepad++を使用して正規表現を使用しています。Ctrl+Shift+Fを押すと、すべてのファイルで置換できます。
Yii::t()
Yii::t('text' , 'text' ) // NO
Yii::t('text','text') // YES
search: Yii::t\('([^']*)'[^']*'([^']*)'[^\)]*\)
replace with: Yii::t\('$1','$2'\)
URL(Notepad++で)
return $this->redirect('/controller/action')->send(); // NO
return $this->redirect(['controller/action'])->send(); // YES
search: ->redirect\(['][/]([^']*)[']\)
replace: ->redirect\(['$1']\)
====
return $this->redirect('controller/action')->send(); // NO
return $this->redirect(['controller/action'])->send(); // YES
search: ->redirect\((['][^']*['])\)
replace: ->redirect\([$1]\)
PHPショートタグ
search: (<\?)([^p=]) // <?if ...
replace: $1php $2 // <?php if ...
// note that sometimes <?xml can be found and it is valid, keep it
ビューの使用状況
search: render(Ajax|Partial)?\s*\(\s*['"]\s*[a-z0-9_\/]*(viewName)
VagrantとDockerはどちらも、指定したほぼ任意のOSまたはSW構成を使用して仮想マシンを作成しますが、ソースコードはローカルディスク上にあるため、自分のOSでIDEを使用して簡単に修正できます。
PHP開発だけでなく、他の状況でも使用できます。
これは何に役立つのか?…本番サーバーは特定の環境を実行しており、同じシステムで開発/テストを行いたいと考えています。さらに、ローカルにXAMPP、LAMP、その他のサーバーをインストールする必要はありません。仮想マシンを起動するだけで準備完了です。さらに、仮想システムの構成を他の同僚と共有して、全員が同一の環境で作業することもできます。ローカルで、異なるPHPバージョンなどを備えた多くの異なるOSシステムを実行することもできます。
VagrantとDockerは、composerやNPMと同じように機能します。利用可能なOSイメージやその他のSWのライブラリであり、いくつかの組み合わせを選択するだけです。構成全体は、Vagrantfileまたはdocker-compose.ymlという名前の1つのテキストファイルで定義されており、実行するにはいくつかのコマンドだけで済みます。デバッグも問題ありません。
情報:この章は、ScotchBoxでPHP 7.0を使用しています。PHP 7.4が必要な場合は、CognacBoxを使用する次の章を参照してください(テスト後に追加されます)。
基本的な概要とVagrant構成
Vagrantで使用可能なすべてのOSイメージのリストはこちら
Yiiデモアプリケーションには既にVagrantfileが含まれていますが、その設定は私には不明瞭です - あまりにもPROです。scotch/boxという名前のOSイメージを使用する簡略版を公開したいと思いました。非yii PHPプロジェクトでも使用できます。(いくつかの利点がありますが、欠点は無料版では古いPHPであることです)。
Vagrantfileは、デモプロジェクトのルートフォルダに格納されています。私のVagrantfileには、次のコマンドのみが含まれています。
Vagrant.configure("2") do |config|
config.vm.box = "scotch/box"
config.vm.network "private_network", ip: "11.22.33.44"
config.vm.hostname = "scotchbox"
config.vm.synced_folder ".", "/var/www/public", :mount_options => ["dmode=777", "fmode=777"]
config.vm.provision "shell", path: "./vagrant/vagrant.sh", privileged: false
end
# Virtual machine will be available on IP A.B.C.D (in our case 11.22.33.44, see above)
# Virtual can access your host machine on IP A.B.C.1 (this rule is given by Vagrant)
サーバーを少し強化したかったので、vagrant/vagrant.shファイルが必要です。次のものが含まれています。
# Composer:
# (In case of composer errors, it can help to delete the vendor-folder and composer.lock file)
cd /var/www/public/
composer install
# You can automatically import your SQL (root/root, dbname scotchbox)
#mysql -u root -proot scotchbox < /var/www/public/vagrant/db.sql
# You can run migrations:
#php /var/www/public/protected/yiic.php migrate --interactive=0
# You can create folder and set 777 rights:
#mkdir /var/www/public/assets
#sudo chmod -R 777 /var/www/public/assets
# You can copy a file:
#cp /var/www/public/from.php /var/www/public/to.php
# Installing Xdebug v2 (Xdebug v3 has renamed config params!):
sudo apt-get update
sudo apt-get install php-xdebug
# Configuring Xdebug in php.ini:
# If things do not work, disable your firewall and restart IDE. It might help.
echo "" | sudo tee -a /etc/php/7.0/apache2/php.ini
echo "[XDebug]" | sudo tee -a /etc/php/7.0/apache2/php.ini
echo "xdebug.remote_enable=1" | sudo tee -a /etc/php/7.0/apache2/php.ini
echo "xdebug.remote_port=9000" | sudo tee -a /etc/php/7.0/apache2/php.ini
echo "xdebug.remote_autostart=1" | sudo tee -a /etc/php/7.0/apache2/php.ini
echo "xdebug.remote_log=/var/www/public/xdebug.log" | sudo tee -a /etc/php/7.0/apache2/php.ini
echo "xdebug.remote_connect_back=1" | sudo tee -a /etc/php/7.0/apache2/php.ini
echo "xdebug.idekey=netbeans-xdebug" | sudo tee -a /etc/php/7.0/apache2/php.ini
# Important: Make sure that your IDE has identical settings: idekey and remote_port.
# NetBeans: Make sure your project is correctly setup. Right-click the project and select Properties / Run Cofigurations. "Project URL" and "Index file" must have correct values.
# Note:
# Use this if remote_connect_back does not work.
# IP must correspond to the Vagrantfile, only the last number must be 1
#echo "xdebug.remote_handler=dbgp" | sudo tee -a /etc/php/7.0/apache2/php.ini
#echo "xdebug.remote_host=11.22.33.1" | sudo tee -a /etc/php/7.0/apache2/php.ini
sudo service apache2 restart
…そのため、プロジェクトの両方のファイルを作成してください…
php.iniを手動で開き、このテキストを貼り付けたい場合は、ここからコピーできます。
// sudo nano /etc/php/7.0/apache2/php.ini
// (Xdebug v3 has renamed config params!)
[XDebug]
xdebug.remote_enable=1
xdebug.remote_port=9000
xdebug.remote_autostart=1
xdebug.remote_log=/var/www/public/xdebug.log
xdebug.remote_connect_back=1
xdebug.idekey=netbeans-xdebug
// Important: Make sure that your IDE has identical settings: idekey and remote_port.
// NetBeans: Make sure your project is correctly setup. Right-click the project and select Properties / Run Cofigurations. "Project URL" and "Index file" must have correct values.
PhpStormでデバッグするには、このビデオを確認してください。
PhpStorm経由でMySQLに接続するには、MilanGによるこのコメントを確認してください。
Vagrantのインストールと使用
まず、VagrantとVirtualBoxをインストールしてください。
注:残念ながら、最近ではVirtualBoxはM1チップを搭載したARMベースのMacでは動作しません。その場合はDockerを使用してください。
重要:「vagrant ssh」コマンドでパスワードが要求された場合は、「vagrant」と入力してください。
コマンドラインを開き、プロジェクトに移動すると、開始できます。
仮想マシンが実行されている場合は、これらも呼び出すことができます。
Linuxシェルでは、任意のコマンドを呼び出すことができます。
「scotch/box」ではPhpMyAdminではなく、Adminerを使用しています。これはシンプルなPHPスクリプトであり、インストールなしで実行されます。adminer.phpスクリプトをdocrootにコピーし、ブラウザからアクセスします。Yiiの構成と同じログインを使用します。サーバーはlocalhostになります。
注:高度なアプリケーションを示しています。基本的なアプリケーションはそれほど変わらないと思います。優れたDockerチュートリアルはこちらにあります。
Yiiプロジェクトは既にDockerに対応しています。開始するには、www.docker.comからDockerをインストールするだけで、このマニュアルに従うことができます。
注:initとcomposerは、必ずしもDocker経由ではなく、ローカルで呼び出すことができます。フォルダにファイルを追加するだけです。
これで、URLを開くことができます。
common/config/main-local.phpを開き、次のDB接続を設定します。
次のコマンドのいずれかを使用してマイグレーションを実行します。
フロントエンドに移動し、右上の隅にある「サインアップ」をクリックします。
2番目の方法は、DBでテーブルを直接修正することです。
これでアカウントが作成され、バックエンドにログインできます。
次のように、environmentセクションをdocker-compose.ymlに追加するだけです。
services:
frontend:
build: frontend
ports:
- 20080:80
volumes:
# Re-use local composer cache via host-volume
- ~/.composer-docker/cache:/root/.composer/cache:delegated
# Mount source-code for development
- ./:/app
environment:
PHP_ENABLE_XDEBUG: 1
XDEBUG_CONFIG: "client_port=9000 start_with_request=yes idekey=netbeans-xdebug log_level=1 log=/app/xdebug.log discover_client_host=1"
XDEBUG_MODE: "develop,debug"
これにより、適切にフォーマットされたvar_dump値を表示し、IDEでアプリケーションをデバッグできます。
注:IDEの設定に基づいてidekeyとclient_portを指定する必要があります。さらに、YiiプロジェクトもIDEで適切に構成されている必要があります。NetBeansでは、「プロパティ/実行構成」(プロジェクトを右クリック)で「プロジェクトURL」と「インデックスファイル」が正しいことを確認してください。
注2:xDebug2とxDebug3では設定が異なることに注意してください。詳細はこちらを参照してください。
これには約8時間費やしました。誰かが喜んでくれることを願っています:-) 残念ながら、この構成はdocker-compose.ymlにはありません。非常に便利だったでしょう。
"volumes"セクションにこの行を追加します。
- ./myphp.ini:/usr/local/etc/php/conf.d/custom.ini
Yiiアプリケーションのルートにmyphp.iniファイルを作成します。例えば、ファイルがロードされているかどうかをテストするために**html_errors=on**と**html_errors=off**を入力できます。Dockerを再起動し、PHPファイルで**phpinfo()**メソッドを使用して結果を確認してください。
コマンドラインでDockerプロジェクトのフォルダに移動し、以下のコマンドを実行します。
一覧の最後の列はNAMESです。1つを選択してその名前をコピーします。次に、以下のコマンドを実行します。
使用されているLinuxを調べるには、**cat /etc/os-release**を実行します。(または、他のコマンドについてはVagrantの章を参照してください)
php.iniの場所を特定するには、**php --ini**と入力します。見つけたら、次のようにyiiフォルダにコピーできます。
cp path/to/php.ini /app/myphp.ini
AdminLTEは利用可能な管理テーマの1つです。現在、2つのバージョンがあります。
* Yii2のBootstrap3からBootstrap4へのアップグレード: https://www.youtube.com/watch?v=W1xxvngjep8
AdminLTE <= 2.3、v2.4、v3.0のドキュメント。AdminLTEの一部の機能はサードパーティの依存関係のみであることに注意してください。例えば、地図など。
他にも多くの管理テーマがあります。
YiiプロジェクトにAdminLTEを統合するためのYii2拡張機能も多数あります。
Yii2デモアプリケーションと同じBootstrapを使用しているため、AdminLTE v2を選択し、実装に役立ついくつかの拡張機能をテストしました。
しかし、まずはYii2デモアプリケーションで拡張機能を使用せずにAdminLTE v2を使用する方法に関する簡単な情報から始めましょう。
v2.4の手動統合 - アセットファイルの作成
また、すべてのSCRIPTタグとLINKタグを削除します。後でAssetBundleを使用してそれらを追加します。
すべてのSCRIPTとLINKをリンクするためのアセットファイルを作成するだけです。
namespace app\assets;
use yii\web\AssetBundle;
class LteAsset extends AssetBundle
{
public $sourcePath = '@vendor/almasaeed2010/adminlte/';
public $jsOptions = ['position' => \yii\web\View::POS_HEAD]; // POS_END cause conflict with YiiAsset
public $css = [
'bower_components/font-awesome/css/font-awesome.min.css',
'https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic',
// etc
];
public $js = [
'bower_components/jquery-ui/jquery-ui.min.js',
// etc
];
public $depends = [
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset',
];
}
このエラーが表示される場合があります。「ヘッダーが既に送信されています」
これで完了です。AdminLTEのHTMLとJSを使用できます。そこで、代わりにそれを行ってくれる拡張機能を確認しましょう。
Insolita拡張機能
多くのUIアイテム(ボックス、タイル、コールアウト、アラート、チャットボックス)でうまく機能します。上記のメインレイアウトファイルとアセットバンドルを準備するだけです。2018年以降は更新されていません。
私のコメントについては、ウェブをご覧ください。多くのウィジェットの使い方を示しました。
ソースの不完全な部分
vendor\insolita\yii2-adminlte-widgets\LteConst.php
vendor\insolita\yii2-adminlte-widgets\CollapseBox.php
LteBox
<div class="overlay"><i class="fa fa-refresh fa-spin"></i></div>
Yiister
そのウェブですべてが説明されています。非常に便利です。http://adminlte.yiister.ruこの記事のアセットファイルだけが必要で、Yiisterをインストールします。残念ながら、2015年以降は更新されていません。メニュー、グリッドビュー、いくつかのボックス、Fleshalerts、コールアウト、エラーページのレンダリングのためのウィジェットを提供します。
dmstr/yii2-adminlte-asset
AdminLTEのウェブで公式に言及されています。メニューとアラートのみをレンダリングします。主にアセットファイルとGiiテンプレートを提供します。GiiテンプレートはGridViewのデザインを自動的に修正しますが、手動で行う方法を以下に示します。
その他の強化機能
AdminLTEはフォントSource Sans Proを使用しています。異なるフォントを使用する場合は、Googleフォントで選択し、次のようにレイアウトファイルを修正します。
<link href="https://fonts.googleapis.com/css2?family=Palanquin+Dark:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Palanquin Dark', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
h1,h2,h3,h4,h5,h6,
.h1,.h2,.h3,.h4,.h5,.h6 {
font-family: 'Palanquin Dark', sans-serif;
}
</style>
GridViewを正しく表示するには、次のHTMLコードでラップします。
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title"><i class="fa fa-table"></i> Grid caption</h3>
</div>
<div class="box-body"
... grid view ...
</div>
</div>
web/css/site.cssのグリフォンも変更できます。
a.asc:after {
content: "\e155";
}
a.desc:after {
content: "\e156";
}
基本的に以上です。これで、AdminLTEの使い方とGridViewの修正方法がわかりました。少なくともウィジェットをレンダリングするための拡張機能が1つ必要です(上記を参照)。
ウィジェットに関する公式のドキュメント、またはこの説明をご覧ください。この例を示していますが、3行追加しました。両方のタイプのウィジェットは次のようにコーディングできます。
namespace app\components;
use yii\base\Widget;
use yii\helpers\Html;
class HelloWidget extends Widget{
public $message;
public function init(){
parent::init();
if($this->message===null){
$this->message= 'Welcome User';
}else{
$this->message= 'Welcome '.$this->message;
}
// ob_start();
// ob_implicit_flush(false);
}
public function run(){
// $content = ob_get_clean();
return Html::encode($this->message); // . $content;
}
}
// This widget is called like this:
echo HelloWidget::widget(['message' => ' Yii2.0']);
// After uncommenting my 4 comments you can use this
HelloWidget::begin(['message' => ' Yii2.0']);
echo 'My content';
HelloWidget::end();
両方のデモアプリケーションの準備ができているため、テストの実行は簡単です。コマンドラインを使用し、プロジェクトに移動します。次に、次を入力します。
php ./vendor/bin/codecept run
これにより、単体テストと機能テストが実行されます。これらは、tests/unitフォルダとtests/functionalフォルダに定義されています。機能テストは非表示のブラウザで実行され、JavaScriptでは動作しません(たぶん)。複雑なJavaScriptをテストするには、受入テストが必要です。それらの実行方法は、README.mdファイルまたは両方のデモアプリケーションのドキュメントにあります。標準のChromeまたはFirefoxブラウザでこれらのテストを実行する場合は、Java JDKとselenium-server*.jarファイルが必要です。README.mdのリンクを参照してください。JARファイルを取得したら、プロジェクトに配置して実行します。
java -jar selenium-server-4.0.0.jar standalone
これで、テストを再実行できます。acceptance.suite.ymlファイルのWebDriverセクションにプロジェクトの動作しているURL(例:https:///yii-basic/web)があることを確認してください。これは環境によって異なります。ブラウザも指定してください。「browser: chrome」を設定するとうまく動作します。「WebDriverがインストールされていません」というエラーが発生した場合は、このComposerコマンドを実行する必要があります。
composer require codeception/module-webdriver --dev
PS:ChromeDriverというファイルもありますが、「codeception/module-webdriver」の代替手段なのか、いつ使用するのかはよくわかりません。まだ研究していません。
コードカバレッジを確認するには、ドキュメント(上記のリンク)の説明に従ってください。さらに、PHPにxDebugが含まれていることを確認してください!xDebug 2とxDebug 3の設定の違いに注意してください!xDebugがない場合、「使用可能なコードカバレッジドライバーがありません」というエラーが発生します。
Linuxでは成功していませんが、WindowsにWebサーバー(例:XAMPPサーバー)をインストールすると、「Microsoft Access Database Engine 2016 Redistributable」をインストールして*.mdbファイルを使用できます。
まず、PHPを備えたWebサーバーをインストールする必要があります。64ビット版と32ビット版のどちらをインストールする必要があるかを知る必要があります。おそらく64ビットでしょう。Microsoft Access Database Engine 2016 Redistributableのページ(または利用可能な新しいバージョン)にアクセスし、対応するパッケージ(32ビット版と64ビット版)をインストールします。
注:同じビットバージョンのMS Accessが既にインストールされている場合は、エンジンをインストールする必要がない可能性があります。
次に、DB接続で次のDSN文字列を使用できます。(このコードはconfig/db.phpファイルに属しています)
<?php
$file = "C:\\xampp\\htdocs\\Database1.mdb";
return [
'class' => 'yii\db\Connection',
'dsn' => "odbc:DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};Dbq=$file;Uid=;Pwd=;",
'username' => '',
'password' => '',
'charset' => 'utf8',
//'schemaMap' => [
// 'odbc'=> [
// 'class'=>'yii\db\pgsql\Schema',
// 'defaultSchema' => 'public' //specify your schema here
// ]
//],
// Schema cache options (for production environment)
//'enableSchemaCache' => true,
//'schemaCacheDuration' => 60,
//'schemaCache' => 'cache',
];
次に、これを使用してテーブルをクエリします。
$data = Yii::$app->db->createCommand("SELECT * FROM TableX")->queryAll();
var_dump($data);
注:異なるビットバージョンのMS AccessがPHPに既にインストールされている場合、正しいビットバージョンのエンジンをインストールできません。その場合は、MS Accessをアンインストールする必要があります。
注2:MDBファイルの内容がわからない場合は、GoogleドキュメントでMDB、ACCDBビューアとリーダーを推奨され、それは機能しました。
注3:Windows 10には、次のようにプリインストールされたアプリケーションがあります。
必要なものを開き、「システムDSN」タブに移動して「追加」をクリックします。使用可能なドライバーが表示されます - **DSN文字列で使用できるのはこれらのドライバーのみです!**
「SQL Server」しか表示されない場合は、プラットフォーム用のドライバーを含むAccess Engine(またはMS Access)をインストールする必要があります。「Microsoft Access Driver (*.mdb, *.accdb)」という名前のドライバーが必要です。
私の場合、エンジンは次の64ビットドライバーを追加しました。
Linuxはどうですか?
MS Accessドライバーも必要ですが、Microsoftは提供していません。サードパーティ製のMdbToolsやEasySoftがありますが、不完全なものや高価なものがあります。Unix ODBCもあります。
JavaにはJava JDBC、Jackcess、Ucanaccessがあります。
Dockerはどうでしょうか?私の知る限り、Linux上でWindowsイメージを実行することはできないため、この場合、WindowsのODBCの利点を使用することはできません。Windows上でLinuxイメージを使用することはできますが、仮想LinuxからODBCドライバにアクセスする方法はないと思います。試してみる必要がありますが、まだテストしていません。
Yii2のマイグレーションでCSVをDBにインポートしたい場合、この「マイグレーションベースクラス」を作成し、実際のマイグレーションの親として使用できます。その後、batchInsertCsv()メソッドを使用できます。
<?php
namespace app\components;
use yii\db\Migration;
class BaseMigration extends Migration
{
/**
* @param $filename Example: DIR_ROOT . DIRECTORY_SEPARATOR . "file.csv"
* @param $table The target table name
* @param $csvToSqlColMapping [csvColName => sqlColName] (if $containsHeaderRow = true) or [csvColIndex => sqlColName] (if $containsHeaderRow = false)
* @param bool $containsHeaderRow If the header with CSV col names is present
* @param int $batchSize How many rows will be inserted in each batch
* @throws Exception
*/
public function batchInsertCsv($filename, $table, $csvToSqlColMapping, $containsHeaderRow = false, $batchSize = 10000, $separator = ';')
{
if (!file_exists($filename)) {
throw new \Exception("File " . $filename . " not found");
}
// If you see number 1 in first inserted row and column, most likely BOM causes this.
// Some Textfiles begin with 239 187 191 (EF BB BF in hex)
// bite order mark https://en.wikipedia.org/wiki/Byte_order_mark
// Let's trim it on the first row.
$bom = pack('H*', 'EFBBBF');
$handle = fopen($filename, "r");
$lineNumber = 1;
$header = [];
$rows = [];
$sqlColNames = array_values($csvToSqlColMapping);
$batch = 0;
if ($containsHeaderRow) {
if (($raw_string = fgets($handle)) !== false) {
$header = str_getcsv(trim($raw_string, $bom), $separator);
}
}
// Iterate over every line of the file
while (($raw_string = fgets($handle)) !== false) {
$dataArray = str_getcsv(trim($raw_string, $bom), $separator);
if ($containsHeaderRow) {
$dataArray = array_combine($header, $dataArray);
}
$tmp = [];
foreach ($csvToSqlColMapping as $csvCol => $sqlCol) {
$tmp[] = trim($dataArray[$csvCol]);
}
$rows[] = $tmp;
$lineNumber++;
$batch++;
if ($batch >= $batchSize) {
$this->batchInsert($table, $sqlColNames, $rows);
$rows = [];
$batch = 0;
}
}
fclose($handle);
$this->batchInsert($table, $sqlColNames, $rows);
}
}
]]>\yii\mail\BaseMailer::useFileTransport は素晴らしいツールです。これを有効にすると、このメーラーを通して送信されたすべてのメールは(デフォルトで)@runtime/mail
に保存され、送信されなくなります。これにより、開発者は結果を検査できます。
しかし、受信トレイで実際にメールを受信したい場合はどうなりますか?すべてのメールが1つのアカウントに送信される予定の場合、問題は発生しません。パラメーターとして設定し、params-local.php
(高度なアプリケーションテンプレートを想定)で変更します。
アプリが異なるアカウントにメールを送信し、replyTo、cc、bccフィールドを使用する必要がある場合、大きな問題が発生します。以前のアプローチでは、多くのif(YII_DEBUG)
を使用せずに解決するのはほぼ不可能です。
さて、次に解決策があります。
'useFileTransport' => true,
'fileTransportCallback' => function (\yii\mail\MailerInterface $mailer, \yii\mail\MessageInterface $message) {
$message->attachContent(json_encode([
'to' => $message->getTo(),
'cc' => $message->getCc(),
'bcc' => $message->getBcc(),
'replyTo' => $message->getReplyTo(),
]), ['fileName' => 'metadata.json', 'contentType' => 'application/json'])
->setTo('debug@mydomain.com') // account to receive all the emails
->setCc(null)
->setBcc(null)
->setReplyTo(null);
$mailer->useFileTransport = false;
$mailer->send($message);
$mailer->useFileTransport = true;
return $mailer->generateMessageFileName();
}
動作方法は?fileTransportCallback
は、@runtime/mail
に保存されたメールの作成に使用されるファイル名を指定するコールバックです。これはメール送信プロセスを「傍受」するため、目的のために使用できます。
useFileTransport
を無効にします。useFileTransport
を有効にします。このようにして、指定されたアカウントですべてのメールを受信し、@runtime/mail
にも保存されます。
Yii2アプリケーションでメールを確認するための非常にシンプルなヘルパーです。
元の投稿先:https://glpzzz.github.io/2020/10/02/yii2-redirect-all-emails.html
]]>多くのエラーが発生し、Yii2で複数の画像APIを実行する方法が分からなかった後、今日やっと解決しました。
これはフォーラムで質問した内容であり、私の場合は機能します。https://forum.yiiframework.com/t/multiple-file-uploading-api-in-yii2/130519
**複数ファイルアップロード**のために、このコードをモデルに実装します。
public function rules()
{
return [
[['post_id', 'media'], 'required'],
[['post_id'], 'integer'],
[['media'], 'file', 'maxFiles' => 10],//here is my file field
[['created_at'], 'string', 'max' => 25],
[['post_id'], 'exist', 'skipOnError' => true, 'targetClass' => Post::className(), 'targetAttribute' => ['post_id' => 'id']],
];
}
モデルには、拡張機能またはskipOnEmptyメソッドを追加することもできます。
そして、これは複数のファイルアップロードコードを実行したコントローラーアクションです。
public function actionMultiple(){
$model = new Media;
$model->post_id = '2';
if (Yii::$app->request->ispost) {
$model->media = UploadedFile::getInstances($model, 'media');
if ($model->media) {
foreach ($model->media as $value) {
$model = new Media;
$model->post_id = '2';
$BasePath = Yii::$app->basePath.'/../images/post_images';
$filename = time().'-'.$value->baseName.'.'.$value->extension;
$model->media = $filename;
if ($model->save()) {
$value->saveAs($BasePath.$filename);
}
}
return array('status' => true, 'message' => 'Image Saved');
}
}
return array('status' => true, 'data' => $model);
}
ご質問があれば、対応いたします。
]]>ロギングはアプリケーションの非常に重要な機能です。常に何が起こっているかを知らせてくれます。デフォルトでは、Yii2の基本アプリケーションと高度なアプリケーションには、\yii\log\FileTargetターゲットのみが構成されています。
アプリからのメッセージを含むメールを受信するには、ログコンポーネントをファイルトランスポートの代わりに(またはそれに加えて)メール(またはTelegram、Slack)トランスポートに設定します。
'components' => [
// ...
'log' => [
'targets' => [
[
'class' => 'yii\log\EmailTarget',
'mailer' => 'mailer',
'levels' => ['error', 'warning'],
'message' => [
'from' => ['log@example.com'],
'to' => ['developer1@example.com', 'developer2@example.com'],
'subject' => 'Log message',
],
],
],
],
// ...
],
\yii\log\EmailTargetコンポーネントは、メッセージをログに記録する別の方法であり、この場合は、EmailTarget
構成のmailer
属性で指定されたアプリケーションのmailer
コンポーネントを介してメールで送信します。メッセージのプロパティと、このターゲットを介して送信する必要があるメッセージのレベルを指定することもできます。
メール以外のプラットフォームを介してメッセージを受信したい場合は、ログターゲットを表す他のコンポーネントがあります。
または、\yii\log\Targetをサブクラス化して独自に実装することもできます。
]]>https://schema.orgは、検索エンジンやその他のアプリケーションで使用するために、Webページに構造化されたデータを埋め込むことができるマークアップシステムです。JSON-LDを使用して、Yii2ベースのWebサイトのページにSchema.orgを追加する方法を見てみましょう。
基本的に、ページに次のようなものを埋め込む必要があります。
<script type="application/ld+json">
{
"@context": "http://schema.org/",
"@type": "Movie",
"name": "Avatar",
"director":
{
"@type": "Person",
"name": "James Cameron",
"birthDate": "1954-08-16"
},
"genre": "Science fiction",
"trailer": "../movies/avatar-theatrical-trailer.html"
}
</script>
しかし、Yii2でこのようなスクリプトを書きたくないので、他の、よりPHP的な方法を試してみましょう。
レイアウトでは、Webサイトの一般的なマークアップを定義できます。そのため、@app/views/layouts/main.php
ファイルの先頭に次のスニペットを追加します。
<?= \yii\helpers\Html::script(isset($this->params['schema'])
? $this->params['schema']
: \yii\helpers\Json::encode([
'@context' => 'https://schema.org',
'@type' => 'WebSite',
'name' => Yii::$app->name,
'image' => $this->image,
'url' => Yi::$app->homeUrl,
'descriptions' => $this->description,
'author' => [
'@type' => 'Organization',
'name' => Yii::$app->name,
'url' => 'https://www.hogarencuba.com',
'telephone' => '+5352381595',
]
]), [
'type' => 'application/ld+json',
]) ?>
ここでは、必要なtype
オプションを使用してスクリプトを含めるHtml::script($content, $options)と、JSONを生成するJson::encode($value, $options)を使用しています。また、他のページからのマークアップの上書きを許可するために、schema
というページパラメーターを使用しています。たとえば、@app/views/real-estate/view.php
では、次を使用しています。
$this->params['schema'] = \yii\helpers\Json::encode([
'@context' => 'https://schema.org',
'@type' => 'Product',
'name' => $model->title,
'description' => $model->description,
'image' => array_map(function ($item) {
return $item->url;
}, $model->images),
'category' => $model->type->description_es,
'productID' => $model->code,
'identifier' => $model->code,
'sku' => $model->code,
'url' => \yii\helpers\Url::current(),
'brand' => [
'@type' => 'Organization',
'name' => Yii::$app->name,
'url' => 'https://www.hogarencuba.com',
'telephone' => '+5352381595',
],
'offers' => [
'@type' => 'Offer',
'availability' => 'InStock',
'url' => \yii\helpers\Url::current(),
'priceCurrency' => 'CUC',
'price' => $model->price,
'priceValidUntil' => date('Y-m-d', strtotime(date("Y-m-d", time()) . " + 365 day")),
'itemCondition' => 'https://schema.org/UsedCondition',
'sku' => $model->code,
'identifier' => $model->code,
'image' => $model->images[0],
'category' => $model->type->description_es,
'offeredBy' => [
'@type' => 'Organization',
'name' => Yii::$app->name,
'url' => 'https://www.hogarencuba.com',
'telephone' => '+5352381595',
]
]
]);
ここでは、このページのスキーマをより複雑なマークアップ(オファー付きの製品)で再定義しています。
このようにして、Webサイトのすべてのページにschema.orgマークアップが定義されます。レイアウトにはデフォルトがあり、他のページでは$this->params['schema']
に値を設定して再定義できます。
OpenGraphとTwitter Cardsは、Webページを記述し、FacebookとTwitterでそれぞれより理解しやすくするための2つのメタデータセットです。
単純なWebページに多くのメタタグを追加する必要があるため、TaggedViewを使用しましょう。
このコンポーネントはyii\web\View
をオーバーライドし、それに属性を追加して、各ビューで値を設定できるようにします。通常、ページタイトルは次のように設定します。
$this->title = $model->title;
さて、**TaggedView**を使用すると、次のように設定できます。
$this->title = $model->title;
$this->description = $model->abstract;
$this->image = $model->image;
$this->keywords = ['foo', 'bar'];
これにより、このページの適切なOpenGraph、Twitter Card、HTMLメタディスクリプションタグが生成されます。
また、各ページで使用できるコンポーネント構成で各タグのデフォルト値を定義し、前の例のように再定義した場合にのみオーバーライドされます。
'components' => [
//...
'view' => [
'class' => 'daxslab\taggedview\View',
'site_name' => '',
'author' => '',
'locale' => '',
'generator' => '',
'updated_time' => '',
],
//...
]
これらのプロパティの一部には、デフォルト値が割り当てられています。たとえば、site_name
はデフォルトでYii::$app->name
を取得します。
Webサイトでの使用の結果
<title>¿Deseas comprar o vender una casa en Cuba? | HogarEnCuba, para comprar y vender casas en Cuba</title>
<meta name="author" content="Daxslab (https://www.daxslab.com)">
<meta name="description" content="Hay 580 casas...">
<meta name="generator" content="Yii2 PHP Framework (https://yii.dokyumento.jp)">
<meta name="keywords" content="HogarEnCuba, ...">
<meta name="robots" content="follow">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:description" content="Hay 580 casas...">
<meta name="twitter:image" content="https://www.hogarencuba.com/images/main-identifier_es.png">
<meta name="twitter:site" content="HogarEnCuba">
<meta name="twitter:title" content="¿Deseas comprar o vender una casa en Cuba?">
<meta name="twitter:type" content="website">
<meta name="twitter:url" content="https://www.hogarencuba.com/">
<meta property="og:description" content="Hay 580 casas...">
<meta property="og:image" content="https://www.hogarencuba.com/images/main-identifier_es.png">
<meta property="og:locale" content="es">
<meta property="og:site_name" content="HogarEnCuba">
<meta property="og:title" content="¿Deseas comprar o vender una casa en Cuba?">
<meta property="og:type" content="website">
<meta property="og:updated_time" content="10 sept. 2020 9:43:00">
]]>wikiではファイルごとに最大長があるため、記事は複数のファイルに分割されています。
PHPにMSSQLドライバが必要です。プログラムで、それらをリストしたり、次のように存在を確認したりできます。
var_dump(\PDO::getAvailableDrivers());
if (in_array('sqlsrv', \PDO::getAvailableDrivers())) {
// ... MsSQL driver is available, do something
}
システムに基づいて、異なるドライバをダウンロードする必要があります。違いはx64対x86とThreadSafe対nonThreadSafeです。Windowsでは常にThreadSafeを使用します。説明。
最新のPHPドライバはこちらにあります。
古いPHPドライバはこちらにあります。
ドライバをダウンロードして解凍したら、DLLファイルを選択して「php/ext」フォルダに配置します。Windowsでは、たとえば「C:\xampp\php\ext」にある場合があります。
注:場合によっては、これらのOBDCドライバも必要になる場合がありますが、いつ必要になるかはわかりません。
これで、php.iniファイルを修正する必要があります。Windowsでは、「C:\xampp\php\php.ini」に配置されている場合があります。それを開き、「extension」という単語で始まる行を探して、そこに次の行を貼り付けます。
extension={filename.dll}
// Example:
extension=php_pdo_sqlsrv_74_ts_x64.dll
Apacheを再起動してphpinfo() Webページにアクセスします。「pdo_sqlsrv」セクションが表示されるはずです。XAMPPを使用している場合、このURLにある可能性があります。https:///dashboard/phpinfo.php。
次に、Yii2構成にMSSQL DBへの接続を追加するだけです。私の場合、データベースはリモートだったので、2番目のDB接続を作成する必要がありました。次の章で、その方法を読んでください。
2番目のデータベースの追加は、yii-configで次のように行います。
'db' => $db, // the original DB
'db2'=>[
'class' => 'yii\db\Connection',
'driverName' => 'sqlsrv',
// I was not able to specify database like this:
// 'dsn' => 'sqlsrv:Server={serverName};Database={dbName}',
'dsn' => 'sqlsrv:Server={serverName}',
'username' => '{username}',
'password' => '{pwd}',
'charset' => 'utf8',
],
以上です。これで、次のようにDBをテストできます。
$result = Yii::$app->db2->createCommand('SELECT * FROM {tblname}')->queryAll();
var_dump($result);
MSSQLでは、テーブル名が長くなることがあります。例:CATEGORY.SCHEMA.TBL_NAME
最初のテストモデルは次のようになります(MyMsModel.phpファイル)。
namespace app\models;
use Yii;
use yii\helpers\ArrayHelper;
class MyMsModel extends \yii\db\ActiveRecord
{
public static function getDb()
{
return \Yii::$app->db2; // or Yii::$app->get('db2');
}
public static function tableName()
{
return 'CATEGORY.SCHEMA.TBL_NAME'; // or SCHEMA.TBL_NAME
}
}
使用方法
$result = MyMsModel::find()->limit(2)->all();
var_dump($result);
2番目のデータベースを追加したら(上記参照)、Giiのモデルジェネレーターに移動します。そこで、yii-configで接続名に設定したもの(上記の例では"db2")にDB接続を変更し、テーブル名をSCHEMA.TBL_NAMEの形式で設定します。MSSQLサーバーに複数のデータベースがある場合、そのうちの1つがメインDBとして設定されます。これは使用されると思います。DBの変更には成功していません。DBはDSN文字列で設定できますが、私の場合は効果がありませんでした。
以前の章では、Yii 1でのPhpExcelの使い方を示しました。今回、Yii 2でも必要になり、非常に簡単でした。
注:PhpExcelは非推奨となり、PhpSpreadsheetに置き換えられました。
// 1) Command line:
// This downloads everything to folder "vendor"
composer require phpoffice/phpspreadsheet --prefer-source
// --prefer-source ... also documentation and samples are downloaded
// ... adds cca 40MB and 1400 files
// ... only for devel system
// 2) PHP:
// Now you can directly use the package without any configuration:
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Uncomment following rows if you want to set col width:
//$sheet->getColumnDimension('A')->setAutoSize(false);
//$sheet->getColumnDimension('A')->setWidth("50");
$sheet->setCellValue('A1', 'Hello World !');
$writer = new Xlsx($spreadsheet);
// You can save the file on the server:
// $writer->save('hello_world.xlsx');
// Or you can send the file directly to the browser so user can download it:
// header('Content-Type: application/vnd.ms-excel'); // This is probably for older XLS files.
header('Content-Type: application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); // This is for XLSX files (they are basically zip files).
header('Content-Disposition: attachment;filename="filename.xlsx"');
header('Cache-Control: max-age=0');
$writer->save('php://output');
exit();
DbCreatorによるXLSXをブラウザに送信する方法に関するアイデアに感謝します。しかし、exit()やdie()を呼び出すべきではありません。リンクを参照してください。
以下は、Yii2のrenderPhpFile()メソッドを起源とする私のアイデアです。
ob_start();
ob_implicit_flush(false);
$writer->save('php://output');
$file = ob_get_clean();
return \Yii::$app->response->sendContentAsFile($file, 'file.xlsx',[
'mimeType' => 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'inline' => false
]);
これも私の場合、機能しました。
$tmpFileName = uniqid('file_').'.xlsx';
$writer->save($tmpFileName);
header('Content-Type: application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="filename.xlsx"');
header('Cache-Control: max-age=0');
echo file_get_contents($tmpFileName);
unlink($tmpFileName);
exit();
注:しかし、exit()やdie()を呼び出すべきではありません。「DbCreator」のリンクを参照してください。
他のPDF作成ツールについては、このガイドの第1部を参照してください。
TCPDFは2002年(だと思います)に作成され、現在(2020年)は最新のPHPアプリケーションに書き換えられています。両方について説明しますが、古いバージョンから始めましょう。
古いバージョンのTCPDF
GitHubからダウンロードしてフォルダに保存します。
{projectPath}/_tcpdf
web/index.phpに以下を追加します。
require_once('../_tcpdf/tcpdf.php');
これで、任意の例を使用してTCPDFをテストできます。例えば:https://tcpdf.org/examples/example_001/
注:コンストラクタをバックスラッシュ付きで呼び出す必要があります。
$pdf = new \TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
注:テキストは複数のメソッドを使用して印刷されます - 詳細についてはtcpdf.phpファイルを参照してください。
注:ファイルをUTF8 BOMなし形式で保存して、PDFでアクセント文字が正しく表示されるようにします。
新しいTTFフォントのインポートは、このように行います。
// this command creates filed in folder _tcpdf\fonts. Use the filename as the fontname in other commands.
$fontname = \TCPDF_FONTS::addTTFfont("path to TTF file", 'TrueTypeUnicode', '', 96);
これで、PHPでこのように使用できます。
$pdf->SetFont($fontname, '', 24, '', true);
またはHTMLで
<font size="9" face="fontName" style="color: rgb(128, 128, 128);">ABC</font>
レンダリングは、このように行います。
$pdf->writeHTML($html);
注:ページ番号と総ページ数をフッターに印刷する場合、例3に示されているように、writeHTML()はgetAliasNumPage()とgetAliasNbPages()メソッドを正しく解釈できませんでした。Text()レンダリングメソッドを使用し、数値をこのように配置する必要がありました。
$this->writeHTML($footerHtmlTable);
$this->SetTextColor('128'); // I have gray pageNr
$this->Text(185, 279, 'Page ' . $this->getAliasNumPage() . '/' . $this->getAliasNbPages());
$this->SetTextColor('0'); // returning black color
新しいバージョンのTCPDF
…作成中…
PDF請求書を生成する場合、多くの数値が含まれ、小数点以下がない場合は整数として印刷する方が見栄えが良く、スペースも節約できます。例えば、24.00よりも24の方が優れています。そこで、このようなフォーマッタを作成しました。元のインスピレーションと方法は、ここで見つけました。
私のフォーマッタは次のようになります。
<?php
namespace app\myHelpers;
class MyFormatter extends \yii\i18n\Formatter {
public function asDecimalOrInteger($value) {
$intStr = (string) (int) $value; // 24.56 => "24" or 24 => "24"
if ($intStr === (string) $value) {
// If input was integer, we are comparing strings "24" and "24"
return $this->asInteger($value);
}
if (( $intStr . '.00' === (string) $value)) {
// If the input was decimal, but decimals were all zeros, it is an integer.
return $this->asInteger($value);
}
// All other situations
$decimal = $this->asDecimal($value);
// Here I trim also the trailing zero.
// Disadvantage is that String is returned, but in PDF it is not important
return rtrim((string)$decimal, "0");
}
}
使い方は簡単です。上記のリンクを参照して、karpy47にいいね!を送るか、下記を参照してください。
// file config/web.php
'components' => [
'formatter' => [
'class' => 'app\myHelpers\MyFormatter',
],
],
Yii全体でフォーマッタは1つだけで、それを拡張できます。つまり、さらにメソッドを追加しても、残りのフォーマッタはそのままなので、ドキュメントに記載されている他のすべてのメソッドを使用できます。
…DBにMySQLビューを追加し、そのためのモデルを作成して、「ParentSearch」モデルで基底クラスとして使用することで、簡単に実現できます。
請求書とその明細書のリストで説明しましょう。請求書は"invoice"テーブル(モデルInvoice)、明細書は"invoice_item"テーブル(モデルInvoiceItem)にあります。これらを結合し、価格(金額)のSUMでソートおよびフィルタリングする必要があります。PHPで計算を避けるために、MySQLビューを用意すれば、DBがそれを行ってくれます。
CREATE VIEW v_invoice AS
SELECT invoice.*,
SUM(invoice_item.units * invoice_item.price_per_unit) as amount,
COUNT(invoice_item.id) as items
FROM invoice
LEFT JOIN invoice_item
ON (invoice.id = invoice_item.id_invoice)
GROUP BY invoice.id
注:ここでは、LEFT JOINでCOUNT(*)を使用しない方が良い理由について説明しています。
これは技術的には元のテーブル"invoice"を"v_invoice"に複製し、"amount"と"items"という2つの計算された列を追加します。これで、このビューをテーブル(読み取り専用)として簡単に使用し、GridViewに表示できます。既に"invoice"テーブルのGridViewがある場合、変更はごくわずかです。このモデルを作成します。
<?php
namespace app\models;
class v_Invoice extends Invoice
{
public static function primaryKey()
{
// here is specified which column(s) create the fictive primary key in the mysql-view
return ['id'];
}
public static function tableName()
{
return 'v_invoice';
}
}
…そして、InvoiceSearchモデルで、Invoiceをv_Invoiceに置き換え(2箇所だと思います)、これらの新しい列に対するルールを追加します。例:
public function rules()
{
return [
// ...
[['amount'], 'number'], // decimal
[['items'], 'integer'],
];
}
amountまたはitemsでフィルタリングする場合は、search()メソッドに条件を追加します。
if (trim($this->amount)!=='') {
$query->andFilterWhere([
'amount' => $this->amount
]);
}
GridViewでは、"amount"列と"items"列をネイティブ列として使用できます。フィルタリングとソートが機能します。
危険:関連する列による検索とソートの方法を以下で説明します。MySQLを別のテーブルと結合する必要がある場合、これは機能しなくなる可能性があります。
このアプローチが目標達成に最も簡単な方法だと信じています。利点は、MySQLビューはsearch()メソッドが呼び出された場合、つまり請求書のリストの場合にのみ使用されることです。元のInvoiceモデルを使用するため、Webの他の部分は影響を受けません。しかし、Invoiceモデルから特別なメソッドが必要な場合は、v_Invoiceにもあります。データが保存または変更された場合は、常に元のテーブル"invoice"を変更する必要があります。
請求書のテーブルと会社のテーブルがあるとします。それらは関連しており、請求書のリストと各行に対応する会社名を表示したいとします。この列でフィルタリングおよびソートしたいとします。
あなたのGridView
<?= GridView::widget([
// ...
'columns' => [
// ...
[
'attribute'=>'company_name',
'value'=>'companyRelation.name',
],
あなたのInvoiceSearchモデル
class InvoiceSearch extends Invoice
{
public $company_name;
// ...
public function rules() {
return [
// ...
[['company_name'], 'safe'],
];
}
// ...
public function search($params) {
// ...
// You must use joinWith() in order to have both tables in one JOIN - then you can call WHERE and ORDER BY on the 2nd table.
// Explanation here:
// https://stackoverflow.com/questions/25600048/what-is-the-difference-between-with-and-joinwith-in-yii2-and-when-to-use-them
$query = Invoice::find()->joinWith('companyRelation');
// Appending new sortable column:
$sort = $dataProvider->getSort();
$sort->attributes['company_name'] = [
'asc' => ['table.column' => SORT_ASC],
'desc' => ['table.column' => SORT_DESC],
'label' => 'Some label',
'default' => SORT_ASC
];
// ...
if (trim($this->company_name)!=='') {
$query->andFilterWhere(['like', 'table.column', $this->company_name]);
}
}
Yii v1のチュートリアルでは、ヘッダーを手動で送信してからexit()を呼び出す方法を紹介しました。しかし、exit()やdie()を呼び出すのは良い考えではないため、Yii v2でより良い方法を発見しました。 安全な(秘密の)ファイルダウンロードの章を参照してください。
動機:base64を使用して文字列にエンコードされたPDFファイルを受け取る場合があります。例えば、FedEx、DPD、その他の配送会社からのバーコード付きラベルで、ユーザーにラベルを表示する必要があります。
私の場合、このアルゴリズムが機能しました。
$pdfBase64 = 'JVBERi0xLjQ ... Y0CiUlRU9GCg==';
// First I create a fictive stream in a temporary file
// Read more about PHP wrappers:
// https://www.php.net/manual/en/wrappers.php.php
$stream = fopen('php://temp','r+');
// Decoded base64 is written into the stream
fwrite($stream, base64_decode($pdfBase64));
// And the stream is rewound back to the start so others can read it
rewind($stream);
// This row sets "Content-Type" header to none. Below I set it manually do application/pdf.
Yii::$app->response->format = Yii::$app->response::FORMAT_RAW;
Yii::$app->response->headers->set('Content-Type', 'application/pdf');
// This row will download the file. If you do not use the line, the file will be displayed in the browser.
// Details here:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#Downloads
// Yii::$app->response->headers->set('Content-Disposition','attachment; filename="hello.pdf"');
// Here is used the temporary stream
Yii::$app->response->stream = $stream;
// You can call following line, but you don't have to. Method send() is called automatically when current action ends:
// Details here:
// https://yii.dokyumento.jp/doc/api/2.0/yii-web-response#sendContentAsFile()-detail
// return Yii::$app->response->send();
注:必要に応じて、さらにヘッダーを追加できます。以前の記事(上記リンク)を確認してください。
]]>注:Pantahubは、Linuxファームウェアを共有およびデプロイできる唯一の場所です。Pantahubはこちらでサインアップできます。http://www.pantahub.com
ダウンロードをクリック:https://pantavisor-ci.s3.amazonaws.com/pv-initial-devices/tags/012-rc2/162943661/rpi3_initial_stable.img.xz
$ unxz rpi3_initial_stable.img.xz
を実行します。
注:pvrは、Pantahubプラットフォームを介してデバイスとやり取りするために使用できるCLIツールです。
注:pvrを使用すると、gitツリーのように簡単にファームウェアとプロジェクトを共有できます。
注:ダウンロード後、pvrバイナリをbinフォルダに移動します。
Linux(AMD64):ダウンロード
Linux(ARM32v6):ダウンロード
Darwin(AMD64):ダウンロード
pvr clone; pvr commit; pvr post
GitHubのソースコードからインストール:$ go get gitlab.com/pantacor/pvr $ go build -o ~/bin/pvr gitlab.com/pantacor/pvr
注:GitHubのソースコードからpvrをビルドするには、「GOLANG」がシステムにインストールされている必要があります。
$ pvr scan
を実行します ¶
$ pvr claim -c merely-regular-gorilla https://api.pantahub.com:443/devices/5f1b9c44e193a Amazon Prime Videoで今すぐ視聴 5000afa9901
$ pvr clone https://pvr.pantahub.com/sirinibin/presently_learning_pelican/0 presently_learning_pelican
これで、デバイスにYii2アプリをデプロイする準備ができました。
`$ cd presently_learning_pelican`
>sirinibin/yii2-basic-arm32v7:latest
は、ARM32アーキテクチャ搭載のデバイス向けに作成されたDockerイメージです。>> カスタムのYii2アプリに合わせてDockerイメージをカスタマイズできます。(Amazon Prime Videoでの視聴は現時点では関係ありません)
$ pvr app add yii2 --from=sirinibin/yii2-basic-arm32v7:latest
$ pvr add . $ pvr commit $ pvr post
ステータス1
ステータス2
ステータス3
ステータス4
WebブラウザでデバイスのIPアドレス( http://10.42.0.231/myapp1/web/ )にアクセスします。
]]>完了です!
Yii2 - Bootstrap3からBootstrap4への変換
この記事は、変換プロセスが比較的容易である一方で、いくつかの小さな問題が存在するため書かれました。これらの問題は解決するのが難しくありませんが、問題の所在がすぐに明らかになるわけではありません。
1 - Bootstrap4のインストール 私の好みは、composerを使用することです。プロジェクトのルートにあるcomposer.jsonを変更します。
その行をコピーし、新しい行を作成します。
"yiisoft/yii2-bootstrap" : "~2.0.6", "yiisoft/yii2-bootstrap4" : "^2.0.8",
yii\bootstrap\
という文字列を検索してyii\bootstrap4\
に変更することをお勧めします。ただし、注意が必要です。IDEではバックスラッシュをエスケープする必要がある場合があります。たとえば、Eclipseでは文字列を含むすべてのファイルを簡単に検索できますが、検索文字列には二重のバックスラッシュを使用する必要があります。一方、置換文字列は単一のバックスラッシュのままにする必要があります。NavBar::begin
で始まる行を探し、そのクラスオプションを確認します。私の場合、元のものは'class' => 'navbar-inverse navbar-fixed-top'
でした。これですべての結果、元のNavbarと非常によく似たNavbarを与えるクラス行に変更しました。
//Bootstrap3: 'class' => 'navbar-inverse navbar-fixed-top',
//Changed for Bootstrap4:
'class' => 'navbar navbar-expand-md navbar-light bg-dark',
パンくずリスト
**注記 - 2020年3月**: パンくずリストに関するこのセクション全体は、もはや問題ではない可能性があります。チュートリアルとしてこのセクションを残していますが、変更を加える前に、ユーザーコメントでDavideが言っていることを読んでください。
これでNavbarが修正されました。次に、パンくずリストが正しくないことに気づきました。パス要素を区切るスラッシュがなくなっていました。多くのデバッグに備えて、Bootstrapサイトに少しインスピレーションを求めて行きました。それ以上調べる必要はありませんでした。Bootstrap 4では、パンくずリストの各要素に「breadcrumb-item」クラスを付ける必要があります。問題を理解するためにvendors/yiisoft/yii2/widgets/Breadcrumbs.phpを少し調べてから、itemTemplateとactiveItemTemplateを変更するだけでよいことがわかりました。もちろん、これらはYii2フレームワークの一部であるため、このファイルを変更したくありません。変更すると、いつか更新され、変更がすべて失われる可能性があります。これらの属性はどちらも公開されているため、クラスの外側から変更でき、最も簡単な場所はfrontend/views/main.phpです: `
html
<div class="container">
<?= Breadcrumbs::widget([
'itemTemplate' => "\n\t<li class=\"breadcrumb-item\"><i>{link}</i></li>\n", // template for all links
'activeItemTemplate' => "\t<li class=\"breadcrumb-item active\">{link}</li>\n", // template for the active link
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
]) ?>
<?= Alert::widget() ?>
<?= $content ?>
</div>```
**データグリッドのアクションカラム** 私のページの1つは、giiによって生成されたデータグリッドでした。各行には、行の表示、編集、または削除をクリックできるボタンのセットがあります。Bootstrap 4では、ActionColumnが表示されなくなりました。ページソースを見ると、存在していましたが、表示もクリックもできませんでした。移行ガイドを見ると、Bootstrap 3にはアイコンが含まれていますが、Bootstrap 4には含まれていないことがわかりました。Yii2フォーラムで尋ねられた質問から多くの助けを得ました。最終的に、composer.jsonのrequireセクションに "fortawesome/font-awesome": "^5.12.1"という行を含めることによってFontAwesome 5のローカルコピーを取得し、必要なアイコンを選択することで解決しました。これを行う方法を理解するのに多くの時間を費やしましたが、終わってみると、その単純さにほとんど拍子抜けしました。データフォームで実行した操作は次のとおりです。
['class' => 'yii\grid\ActionColumn',
'buttons' => [
'update' => function($url,$model) {
return Html::a('<i class="fas fa-edit"></i>', $url, [
'title' => Yii::t('app', 'update')
]);
},
'view' => function($url,$model) {
return Html::a('<i class="fas fa-eye"></i>', $url, [
'title' => Yii::t('app', 'view')
]);
},
'delete' => function($url,$model) {
return Html::a('<i class="fas fa-trash"></i>', $url, [
'title' => Yii::t('app', 'delete')
]);
}
]
],
機能テスト
視覚的にはそれ以上何も見えず、少なくとも明らかなものは何もなかったので、テストスイートを実行しました。これらはすべて以前はパスしていましたが、現在はいくつか失敗しました。その1つはコンタクトフォームだったので、それを個別に実行すると、エラーメッセージが見つからないため失敗しているとテストで通知されました。
1) ContactCest: Check contact submit no data
Test ../frontend/tests/functional/ContactCest.php:checkContactSubmitNoData
Step See "Name cannot be blank",".help-block"
Fail Element located either by name, CSS or XPath element with '.help-block' was not found.
一方、私はフォームでエラーメッセージを見ることができたので、ブラウザのページソースを使用して、cssクラスが「help-block」ではなく「invalid-feedback」に変更されていることを発見しました。簡単です。frontend/tests/_support/FunctionalTester.phpで、期待されるcssクラスを変更しました。
public function seeValidationError($message)
{
$this->see($message, '.invalid-feedback');
}
もちろん、この短い抜粋は単なる例です。同じことをいくつかの場所で実行する必要がありましたが、すべて簡単に検索して解決できました。
その後、テストを実行しても他の問題は指摘されませんでしたが、他の問題が**ない**という意味ではないと予想しています。現時点ではすべて正常に動作しているように見えますが、さらに多くの問題が隠れていると予想しています。しかし、何らかの理由で、それらの問題はもはやそれほど困難には思えなくなりました。
]]>