Live News for Yii Framework News, fresh extensions and wiki articles about Yii framework. Mon, 06 Jul 2020 08:39:21 +0000 Zend_Feed_Writer 2 (http://framework.zend.com) https://www.yiiframework.com/ [extension] stmswitcher/yii2-ldap-auth Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/stmswitcher/yii2-ldap-auth https://www.yiiframework.com/extension/stmswitcher/yii2-ldap-auth stmswitcher stmswitcher

Yii2 LDAP Auth

Simple extension to handle auth over LDAP in Yii 2 applications.

This extension intended for applications that rely only on LDAP authentication and does not support access tokens.

Installation

composer require "stmswitcher/yii2-ldap-auth"

Example of configuration and a use case

Considering yii2-app-basic:

Configure the component in your configuration file and change user identity class
'components' => [
    ...
    'ldapAuth' => [
        'class' => '\stmswitcher\Yii2LdapAuth\LdapAuth',
        'host' => 'your-ldap-hostname',
        'baseDn' => 'dc=work,dc=group',
        'searchUserName' => '<username for a search user>',
        'searchUserPassword' => '<password for a search user>',

        // optional parameters and their default values
        'ldapVersion' => 3,             // LDAP version
        'protocol' => 'ldaps://',       // Protocol to use           
        'followReferrals' => false,     // If connector should follow referrals
        'port' => 636,                  // Port to connect to
        'loginAttribute' => 'uid',      // Identifying user attribute to look up for
        'ldapObjectClass' => 'person',  // Class of user objects to look up for
        'timeout' => 10,                // Operation timeout, seconds
        'connectTimeout' => 5,          // Connect timeout, seconds
    ],
    ...
    
    'user' => [
        'identityClass' => '\stmswitcher\Yii2LdapAuth\Model\LdapUser',
    ],
    ...
]
Update methods in LoginForm class
use stmswitcher\Yii2LdapAuth\Model\LdapUser;

...

public function validatePassword($attribute, $params)
{
    if (!$this->hasErrors()) {
        $user = LdapUser::findIdentity($this->username);

        if (!$user || !Yii::$app->ldapAuth->authenticate($user->getDn(), $this->password) {
            $this->addError($attribute, 'Incorrect username or password.');
        }
    }
}

...

public function login()
{
    if ($this->validate()) {
        return Yii::$app->user->login(
            LdapUser::findIdentity($this->username),
            $this->rememberMe
                ? 3600*24*30 : 0
        );
    }
    return false;
}
Verify that user belongs to LDAP group

If you need also need to check if user is a member of certain LDAP group, use one more parameter for the authenticate function: `php Yii::$app->ldapAuth->authenticate($user->getDn(), $this->password, 'cn=auth-user-group') `

Now you can login with LDAP credentials to your application.

]]>
0
[extension] aki/yii2-app-nuxt Sun, 28 Jun 2020 17:19:46 +0000 https://www.yiiframework.com/extension/aki/yii2-app-nuxt https://www.yiiframework.com/extension/aki/yii2-app-nuxt akbarjoudi akbarjoudi

993323

Yii 2 Basic Project Template Nuxtjs

  1. DIRECTORY STRUCTURE
  2. REQUIREMENTS
  3. INSTALLATION
  4. Installation
  5. Usage

demo Yii2 Nuxtjs

Yii 2 Basic Project Template is a skeleton Yii 2 application best for rapidly creating small projects.

The template contains the basic features including user login/logout and a contact page. It includes all commonly used configurations that would allow you to focus on adding new features to your application.

Latest Stable Version Total Downloads Build Status

DIRECTORY STRUCTURE

  assets/             contains assets definition
  client/             contain nuxtjs project
  commands/           contains console commands (controllers)
  config/             contains application configurations
  controllers/        contains Web controller classes
  mail/               contains view files for e-mails
  models/             contains model classes
  runtime/            contains files generated during runtime
  tests/              contains various tests for the basic application
  vendor/             contains dependent 3rd-party packages
  views/              contains view files for the Web application
  web/                contains the entry script and Web resources

REQUIREMENTS

The minimum requirement by this project template that your Web server supports PHP 5.6.0.

INSTALLATION

Install via Composer

If you do not have Composer, you may install it by following the instructions at getcomposer.org.

You can then install this project template using the following command:

composer create-project aki/yii2-app-nuxt basic

Now you should be able to access the application through the following URL, assuming basic is the directory directly under the Web root.

Installation

  • composer create-project aki/yii2-app-nuxt basic
  • php yii migrate
  • npm install

Usage

Development
npm run dev

You can access your application at http://localhost:3000.

Production
npm run build
]]>
0
[extension] himiklab/yii2-handsontable-widget Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/himiklab/yii2-handsontable-widget https://www.yiiframework.com/extension/himiklab/yii2-handsontable-widget nata90 nata90

Handsontable Widget for Yii2

  1. Installation
  2. Usage

A minimalist Excel-like grid widget for Yii2 based on Handsontable.

Packagist Packagist license

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require --prefer-dist "himiklab/yii2-handsontable-widget" "*"

or add

"himiklab/yii2-handsontable-widget" : "*"

to the require section of your application's composer.json file.

Usage

use himiklab\handsontable\HandsontableWidget;

<?= HandsontableWidget::widget([
    'settings' => [
        'data' => [
            ['A1', 'B1', 'C1'],
            ['A2', 'B2', 'C2'],
        ],
        'colHeaders' => true,
        'rowHeaders' => true,
    ]
]) ?>

or with ActiveRecord

in view: `php use himiklab\handsontable\HandsontableWidget;

<?= HandsontableWidget::widget([

'requestUrl' => 'hts',
'isRemoteChange' => true,

]); ?> `

in controller: `php use app\models\Page; use himiklab\handsontable\actions\HandsontableActiveAction;

public function actions() {

return [
    'hts' => [
        'class' => HandsontableActiveAction::className(),
        'model' => Page::className(),
        'isChangeable' => true,
    ],
];

} `

]]>
0
[news] Yii 2 Composer Installer 2.0.10 Wed, 24 Jun 2020 00:07:35 +0000 https://www.yiiframework.com/news/292/yii-2-composer-installer-2-0-10 https://www.yiiframework.com/news/292/yii-2-composer-installer-2-0-10 samdark samdark

Yii 2 Composer plugin that handles many extension-related things behind the scenes, was updated to support parallel unzipping introduced in upcoming Composer 2.

Update should not require any specific actions to be performed except issuing composer update command.

]]>
0
[extension] itstructure/yii2-field-widgets Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/itstructure/yii2-field-widgets https://www.yiiframework.com/extension/itstructure/yii2-field-widgets itstructure itstructure

Yii2 Field widgets

  1. Introduction
  2. Dependencies
  3. Installation
  4. Usage
  5. License

Introduction

Latest Stable Version Latest Unstable Version License Total Downloads Build Status Scrutinizer Code Quality

This is form field's widgets for the yii2 framework with the next field types:

  • text
  • textarea
  • ckeditor
  • ckeditorAdmin
  • file
  • checkbox
  • dropdown
  • password

Dependencies

  • php >= 7.1
  • composer

Installation

Base install

Via composer:

composer require "itstructure/yii2-field-widgets": "~1.2.3"

or in section require of composer.json file set the following:

"require": {
    "itstructure/yii2-field-widgets": "~1.2.3"
}

and command composer install, if you install yii2 project extensions first,

or command composer update, if all yii2 project extensions are already installed.

If you are testing this package from local server directory

In application composer.json file set the repository, like in example:

"repositories": [
    {
        "type": "path",
        "url": "../yii2-field-widgets",
        "options": {
            "symlink": true
        }
    }
],

Here,

yii2-field-widgets - directory name, which has the same directory level like application and contains yii2 field widgets package.

Then run command:

composer require itstructure/yii2-field-widgets:dev-master --prefer-source

Usage

Requirements

That widgets are designed to work in form with an active model, which is inherited from yii\db\ActiveRecord.

Single mode

To use this mode, don't set languageModel. That will be null.

Multilanguage mode

All fields will be with a language postfix:

title_en

description_en

title_ru

description_ru, e t. c.

For this mode it's necessary to have Language model with some of languages records.

Example:

$form = ActiveForm::begin();
echo Fields::widget([
    'fields' => [
        [
            'name' => 'title',
            'type' => FieldType::FIELD_TYPE_TEXT,
        ],
        [
            'name' => 'description',
            'type' => FieldType::FIELD_TYPE_CKEDITOR_ADMIN,
            'preset' => 'full',
            'options' => [
                'filebrowserBrowseUrl' => '/ckfinder/ckfinder.html',
                'filebrowserImageBrowseUrl' => '/ckfinder/ckfinder.html?type=Images',
                'filebrowserUploadUrl' => '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Files',
                'filebrowserImageUploadUrl' => '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Images',
                'filebrowserWindowWidth' => '1000',
                'filebrowserWindowHeight' => '700',
            ]
        ],
    ],
    'model'         => $model,
    'form'          => $form,
    'languageModel' => new Language()
]) ?>
echo Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
ActiveForm::end();

License

Copyright © 2018-2020 Andrey Girnik girnikandrey@gmail.com.

Licensed under the MIT license. See LICENSE.txt for details.

]]>
0
[extension] itstructure/yii2-ckeditor Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/itstructure/yii2-ckeditor https://www.yiiframework.com/extension/itstructure/yii2-ckeditor itstructure itstructure

Yii2 CKEditor widget

  1. Introduction
  2. Dependencies
  3. Installation
  4. Usage
  5. License

Introduction

Latest Stable Version Latest Unstable Version License Total Downloads Build Status Scrutinizer Code Quality

This is a CKEditor widget for the Yii2 framework with CKEditor template.

Dependencies

  • php >= 7.1
  • composer

Installation

Via composer:

composer require "itstructure/yii2-ckeditor": "~1.1.3"

or in section require of composer.json file set the following:

"require": {
    "itstructure/yii2-ckeditor": "~1.1.3"
}

and command composer install, if you install yii2 project extensions first,

or command composer update, if all yii2 project extensions are already installed.

Usage

Example of using in application with an active model and ckfinder:

echo $this->form->field($this->model, $this->getFieldName())
    ->widget(
        CKEditor::className(),
        [
            'preset' => 'custom',
            'clientOptions' => [
                'toolbarGroups' => [
                    [
                        'name' => 'undo'
                    ],
                    [
                        'name' => 'basicstyles',
                        'groups' => ['basicstyles', 'cleanup']
                    ],
                    [
                        'name' => 'colors'
                    ],
                    [
                        'name' => 'links',
                        'groups' => ['links', 'insert']
                    ],
                    [
                        'name' => 'others',
                        'groups' => ['others', 'about']
                    ],
                ],
                'filebrowserBrowseUrl' => '/ckfinder/ckfinder.html',
                'filebrowserImageBrowseUrl' => '/ckfinder/ckfinder.html?type=Images',
                'filebrowserUploadUrl' => '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Files',
                'filebrowserImageUploadUrl' => '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Images',
                'filebrowserWindowWidth' => '1000',
                'filebrowserWindowHeight' => '700',
                'allowedContent' => true,
                'language' => 'en',
            ]
        ]
    );

preset option can be:

  • basic
  • full
  • standard

License

Copyright © 2018-2020 Andrey Girnik girnikandrey@gmail.com.

Licensed under the MIT license. See LICENSE.txt for details.

]]>
0
[extension] itstructure/yii2-multi-format-uploader Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/itstructure/yii2-multi-format-uploader https://www.yiiframework.com/extension/itstructure/yii2-multi-format-uploader itstructure itstructure

Yii2 Multi format uploader module

  1. Introduction
  2. Dependencies
  3. Installation
  4. Usage
  5. License

Introduction

Latest Stable Version Latest Unstable Version License Total Downloads Build Status Scrutinizer Code Quality

MFUploader -- Module for the Yii2 Framework to upload some type of files to local or S3 Amazon storage. MFUploader module provides the following options:

  • Upload files to local storage.
  • Upload files to remote Amazon S3 storage.
  • Support file formats: image, audio, video, application, text.
  • Link uploaded files with external application owners (pages, articles, posts e.t.c...).
  • Manage internal albums: imageAlbum, audioAlbum, videoAlbum, applicationAlbum, textAlbum, otherAlbum.
  • Link uploaded files with internal albums (owners).
  • Link internal albums with the external owners (pages, articles, posts e.t.c...).

To work with image binary content it uses Yii2 imagine. To work with Amazon it uses AWS SDK.

Addition module description you can see in my Personal site.

Dependencies

  • php >= 7.1
  • composer

Installation

Base install

Via composer:

composer require itstructure/yii2-multi-format-uploader ~2.2.2

or in section require of composer.json file set the following:

"require": {
    "itstructure/yii2-multi-format-uploader": "~2.2.2"
}

and command composer install, if you install yii2 project extensions first,

or command composer update, if all yii2 project extensions are already installed.

If you are testing this package from local server directory

In application composer.json file set the repository, like in example:

"repositories": [
    {
        "type": "path",
        "url": "../yii2-multi-format-uploader",
        "options": {
            "symlink": true
        }
    }
],

Here,

yii2-multi-format-uploader - directory name, which has the same directory level like application and contains yii2 multi format uploader package.

Then run command:

composer require itstructure/yii2-multi-format-uploader:dev-master --prefer-source

Apply module migrations

For that make next, if not already done:

  • Define mfuploader module in application console config file:

    `php use Itstructure\MFUploader\Module as MFUModule; `

    `php 'modules' => [

      'mfuploader' => [
          'class' => MFUModule::class,
      ],
    

    ], `

  • In file yii, that is located in application root directory, set @mfuploader alias:

    `php $application = new yii\console\Application($config);

    \Yii::setAlias('@mfuploader', $application->getModule('mfuploader')->getBasePath());

    $exitCode = $application->run(); `

    OR

    `php use Itstructure\MFUploader\Module as MFUModule; `

    `php $application = new yii\console\Application($config);

    \Yii::setAlias('@mfuploader', MFUModule::getBaseDir());

    $exitCode = $application->run(); `

  • Run command in console:

    yii migrate --migrationPath=@mfuploader/migrations

    The data base structure:

    Data base structure

Usage

Main properties

The name of module: mfuploader

The namespace for used classes: Itstructure\MFUploader.

The alias to access in to module root directory: @mfuploader.

Application config

Base application config must be like in example below:

use Itstructure\MFUploader\Module as MFUModule;
use Itstructure\MFUploader\controllers\ManagerController;
use Itstructure\MFUploader\controllers\upload\{
    LocalUploadController,
    S3UploadController
};
use Itstructure\MFUploader\controllers\album\{
    ImageAlbumController,
    AudioAlbumController,
    VideoAlbumController,
    ApplicationAlbumController,
    TextAlbumController,
    OtherAlbumController
};
use Itstructure\MFUploader\components\{
    LocalUploadComponent,
    S3UploadComponent
};
'modules' => [
    'mfuploader' => [
        'class' => MFUModule::class, // REQUIRED
        'layout' => ...path to layout..., // NOT REQUIRED
        'view' => ...view component config..., // NOT REQUIRED
        'controllerMap' => [ // CUSTOM
            'upload/local-upload' => LocalUploadController::class,
            'upload/s3-upload' => S3UploadController::class,
            'managers' => ManagerController::class,
            'image-album' => ImageAlbumController::class,
            'audio-album' => AudioAlbumController::class,
            'video-album' => VideoAlbumController::class,
            'application-album' => ApplicationAlbumController::class,
            'text-album' => TextAlbumController::class,
            'other-album' => OtherAlbumController::class,
        ],
        'accessRoles' => ['admin', 'manager', e.t.c...], // CUSTOM
        'defaultStorageType' => MFUModule::STORAGE_TYPE_S3, // CUSTOM
        'components' => [ // CUSTOM
            'local-upload-component' => [ // CUSTOM
                'class' => LocalUploadComponent::class,
                'checkExtensionByMimeType' => false,
                'uploadRoot' => dirname($_SERVER['SCRIPT_FILENAME'])
            ],
            's3-upload-component' => [ // CUSTOM
                'class' => S3UploadComponent::class,
                'checkExtensionByMimeType' => false,
                'credentials' => require __DIR__ . '/aws-credentials.php',
                'region' => 'us-west-2',
                's3DefaultBucket' => 'bucketname',
            ],
        ],
        'publicBaseUrl' => 'http://your-site-address.com'
    ],
],

Here publicBaseUrl for example can be a project domain (http://your-site-address.com). If we have url in mediafiles table \uploads\images\imagealbum\0d\3890\a947f268d6f9fd02d8a0d7e147da922c.jpg, and we use getViewUrl() function from a mediafile model for local files, then for example a result link to image for src attribute of <img> tag will be:

http://your-site-address.com/uploads/images/imagealbum/0d/3890/a947f268d6f9fd02d8a0d7e147da922c.jpg

Note: It is not necessary to configure two components: local-upload-component and s3-upload-component. You must configure minimum one component, according with the defaultStorageType.

File field for the view template

File field must be rendered by FileSetter widget.

It is necessary to send an ID or URL of uploaded mediafile to you application controller and set in model. An after that to link mediafile ID with owner.

Example (for image files):

use Itstructure\MFUploader\widgets\FileSetter;
use Itstructure\MFUploader\Module as MFUModule;
use Itstructure\MFUploader\interfaces\UploadModelInterface;
Container to display selected image.
<div class="media">
    <div id="mediafile-container">
    </div>
    <div class="media-body">
        <h4 id="title-container" class="media-heading"></h4>
        <div id="description-container"></div>
    </div>
</div>
echo FileSetter::widget([
    'model' => $model,
    'attribute' => UploadModelInterface::FILE_TYPE_IMAGE,
    'neededFileType' => UploadModelInterface::FILE_TYPE_IMAGE,
    'buttonName' => MFUModule::t('main', 'Set image'),
    'options' => [
        'id' => Html::getInputId($model, UploadModelInterface::FILE_TYPE_IMAGE)
    ],
    'mediafileContainer' => '#mediafile-container',
    'titleContainer' => '#title-container',
    'descriptionContainer' => '#description-container',
    'owner' => 'post', // can be page, article, catalog e.t.c...
    'ownerId' => {current owner id, post id, page id e.t.c.},
    'ownerAttribute' => UploadModelInterface::FILE_TYPE_IMAGE,
    'subDir' => 'post' // can be page, article, catalog e.t.c...
]);
Configure upload components
Base attributes in BaseUploadComponent

Note: BaseUploadComponent - the base class, which LocalUploadComponent and S3UploadComponent extend.

To understand the principles of configure the base attributes, see public attributes in components\BaseUploadComponent class. But it may be difficult to configure thumbsConfig and thumbFilenameTemplate. Attribute thumbsConfig takes the values according with the next chain:

  1. From the default config file config/thumbs-config.php in module init() function.
  2. Can be rewrited by custom during module thumbsConfig attribute.
  3. Previous config result inserted in to the LocalUploadComponent and S3UploadComponent module components.
  4. Configuration thumbsConfig in module components can be rewrited by custom during component thumbsConfig attribute.
Attributes in LocalUploadComponent

That is very simple. See public attributes in components\LocalUploadComponent class.

Attributes in S3UploadComponent

See public attributes in components\S3UploadComponent class. But there are important attributes:

  • credentials. Attribute to access in to Amazon web services account. Can be: array, callable, and other. See https://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/credentials.html.
  • s3DefaultBucket - bucket, which you use to upload files as default.
  • s3Buckets - buckets to upload files depending on the owner. Not required.

    config example: `php 's3Buckets' => [

    'post' => 'bucketWithPosts',
    'page' => 'bucketWithPages',
    'imageAlbum' => 'bucketWithImages',
    'audioAlbum' => 'bucketWithAudio',
    

    ] `

For more information of S3 configuration, see the next links:

Link entities with owners

To link entities (mediafiles and albums) with owners (pages, articles, posts e.t.c...) there is the abstract class behaviors\Behavior.

You must use child classes: behaviors\BehaviorMediafile and behaviors\BehaviorAlbum.

Explanation by example.

You have a catalog model which extends an ActiveRecord yii2 class.

Use a behaviors\BehaviorMediafile and behaviors\BehaviorAlbum in behaviors of your catalog model to link mediafiles and albums with catalog after: insert active record, update active record, delete active record:

use Itstructure\MFUploader\behaviors\{BehaviorMediafile, BehaviorAlbum};
use Itstructure\MFUploader\interfaces\UploadModelInterface;
use Itstructure\MFUploader\models\album\Album;
public $thumbnail;

public $imageAlbum = [];

public function behaviors()
{
    return ArrayHelper::merge(parent::behaviors(), [
        'mediafile' => [
            'class' => BehaviorMediafile::class,
            'name' => static::tableName(),
            'attributes' => [
                UploadModelInterface::FILE_TYPE_THUMB,
            ],
        ],
        'albums' => [
            'class' => BehaviorAlbum::class,
            'name' => static::tableName(),
            'attributes' => [
                Album::ALBUM_TYPE_IMAGE,
            ],
        ],
    ]);
}

Note: This block should be used in conjunction with the FileSetter widget inside the view form template!

Here the following happens:

  • If you use a "thumbnail" name of attribute param in FileSetter widget, the uploaded mediafile (thumbnail) will be linked with catalog after insert new catalog data to the data base.
  • If you use an "imageAlbum" (by Album::ALBUM_TYPE_IMAGE) name of the name attribute of dropdown or checkbox html field, the last created image album will be linked with catalog after insert new catalog data to the data base.

That works according with the next data base example structure:

Table "mediafiles" - record with id=20 now inserted.

| id  | ... |      created_at     |      updated_at     |
|-----|-----|---------------------|---------------------|
| ... | ... |         ...         |         ...         |
| 20  | ... | 2018-05-06 21:35:04 | 2018-05-06 21:35:10 |

Table "catalog" - record with id=10 inserted after create mediafile (id=20).

| id  | ... |      created_at     |      updated_at     |
|-----|-----|---------------------|---------------------|
| ... | ... |         ...         |         ...         |
| 10  | ... | 2018-05-06 21:35:20 | 2018-05-06 21:35:25 |

Table "owners_mediafiles"

| mediafileId | ownerId |   owner   |  ownerAttribute  |
|-------------|---------|-----------|------------------|
|     ...     |   ...   |    ...    |        ...       |
|     20      |    10   |  catalog  |     thumbnail    |

Table "albums"

| id  |     type    | ... |      created_at     |      updated_at     |
|-----|-------------|-----|---------------------|---------------------|
| ... |     ...     | ... |         ...         |         ...         |
|  5  | imageAlbum  | ... | 2018-05-06 21:30:00 | 2018-05-06 21:30:05 |

Table "owners_albums"

| albumId | ownerId |   owner   |  ownerAttribute  |
|---------|---------|-----------|------------------|
|   ...   |   ...   |    ...    |        ...       |
|    5    |    10   |  catalog  |    imageAlbum    |

Note: If you set in FileSetter widget the attributes: owner, ownerId, ownerAttribute - linking entity with owner will be done automatically during ajax request in uploadmanager.

Integrated album controllers

There are already integrated album controllers in the namespace: Itstructure\MFUploader\controllers\album. Controllers provide the work with the next types of albums: imageAlbum, audioAlbum, videoAlbum, applicationAlbum, textAlbum, otherAlbum.

For that controllers there are already the models and view templates.

To work with that, you must set just the routes in application configuration.

License

Copyright © 2018-2020 Andrey Girnik girnikandrey@gmail.com.

Licensed under the MIT license. See LICENSE.txt for details.

]]>
0
[extension] yii2-multi-format-uploader Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/yii2-multi-format-uploader https://www.yiiframework.com/extension/yii2-multi-format-uploader itstructure itstructure
  1. Requirements
  2. Installation
  3. Usage
  4. Resources

...overview of the extension...

Requirements

...requirements of using this extension (e.g. Yii 2.0 or above)...

Installation

...how to install the extension (e.g. composer install extensionname)...

Usage

...how to use this extension...

...can use code blocks like the following...

$model=new User;
$model->save();

Resources

DELETE THIS SECTION IF YOU DO NOT HAVE IT

...external resources for this extension...

  • [Project page](URL to your project page)
  • [Try out a demo](URL to your project demo page)
]]>
0
[extension] itstructure/yii2-admin-module Sun, 21 Jun 2020 17:15:24 +0000 https://www.yiiframework.com/extension/itstructure/yii2-admin-module https://www.yiiframework.com/extension/itstructure/yii2-admin-module itstructure itstructure

Yii2 Admin module

  1. Introduction
  2. Dependencies
  3. Installation
  4. Usage
  5. License

Latest Stable Version Latest Unstable Version License Total Downloads Build Status Scrutinizer Code Quality

Introduction

Yii2AdminModule -- Module for the Yii2 framework with AdminLTE template, which provides the following options:

  • Use this module as base administrator dashboard to manage site content with the ability to extend it by children application CRUD's
  • Work in multilanguage mode for content
  • Work in multilanguage mode for dashboard

Module functional block scheme:

Admin module scheme

Note!!!

As this module gives you a freedom in the architecture of your application child classes, pay attention:

  • There are no the next specific entity model classes, which are as examples in documentation: Catalog, CatalogLanguage - you have to create them personally in your application.
  • There are no the next specific controller class, which is as example in documentation: CatalogController - you have to create it personally in your application.
  • There are not routes - you have to create that according with your application controller map.

Base project example, which uses this admin module for multilanguage content and dashboard: yii2-template-multilanguage.

Base project example, which uses this admin module for simple data: yii2-template-simple.

Addition module description you can see in my Personal site.

Dependencies

  • php >= 7.1
  • composer

Installation

Via composer:

composer require itstructure/yii2-admin-module ~1.8.1

or in section require of composer.json file set the following:

"require": {
    "itstructure/yii2-admin-module": "~1.8.1"
}

and command composer install, if you install yii2 project extensions first,

or command composer update, if all yii2 project extensions are already installed.

Usage

Main properties

The name of module: admin

The namespace for used classes: Itstructure\AdminModule.

The alias to access in to module root directory: @admin.

The main module template layout is in: @admin/views/layouts/main-admin.php.

Application config

Base application config must be like in example below:

use Itstructure\AdminModule\Module;
use Itstructure\AdminModule\components\AdminView;
'modules' => [
    'admin' => [
        'class' => Module::class,
        'viewPath' => '@app/views/admin',
        'controllerMap' => [
            'catalog' => CatalogController::class,
        ],
        'accessRoles' => ['admin', 'manager'],
        'components' => [
            'view' => [
                'class' => AdminView::class,
                'skin' => AdminView::SKIN_GREEN_LIGHT,
                'bodyLayout' => AdminView::LAYOUT_SIDEBAR_MINI,
                'mainMenuConfig' => require __DIR__ . '/main-menu.php'
                'extraAssets' => require __DIR__ . '/extra-assets.php',
            ],
        ],
    ],
],

Here,

viewPath - view template location for CRUDs, which you will create in application.

CatalogController - example controller.

accessRoles - an array of roles, that are allowed to access. Bu default ['@'].

in components the view component contains required parameter class. Such parameters as skin, bodyLayout - dashboard style, can be set custom.

Parameter mainMenuConfig - is the sidebar admin menu, which must be an array:

[
    'menuItems' => [
        'catalog' => [
            'title' => 'Catalog',
            'icon' => 'fa fa-database',
            'url' => '#',
            'subItems' => [
                'subitem' => [
                    'title' => 'Subcatalog',
                    'icon' => 'fa fa-link',
                    'url' => '/admin/catalog',
                ]
            ]
        ],
        'otheritem' => [
            'title' => 'Other Item',
            'icon' => 'fa fa-database',
            'url' => '/admin/otheritem',
        ],
    ],
]

Parameter extraAssets - extra assets, can be like in examples:

  • Like a class names

    `php [

      FirstExtraAsset::class,
      SecondExtraAsset::class,
    

    ] `

  • Or like an array config

    `php [

      [
          'class' => FirstExtraAsset::class,
      ],
      [
          'class' => SecondExtraAsset::class,
      ],
    

    ] `

Base controllers

In Yii2AdminModule there are two controllers, which can be used by child application controllers:

  • AdminController

    When using this controller, a class AdminView will be loaded with its assets. This is done using getView() method, which in turn is taken from module class with getting the view component.

  • CommonAdminController

    This controller extends AdminController. There are already created universal basic methods for child application controllers:

    • actionIndex();
    • actionView($id);
    • actionCreate();
    • actionUpdate($id);
    • actionDelete($id);

    In order for the child application controller to work with the CommonAdminController, it is necessary to define the functions in child controller:

    • getModelName(); Sets the name of main model class.
    • getSearchModelName(); - Sets the name of main search model class.

    In CommonAdminController there are the next interesting options:

    • viewCreated - to view the record after it's creation instead all record list.
    • additionFields - array of addition fields with heir values for the view template.
    • additionAttributes - array of addition attributes with their values for current model, which can be set in model except for those that are sent from the form. Example: information about the uploaded file, which was uploaded separate from the main send from the form.
Integrated controllers

In Yii2AdminModule there is one integrated controller:

  • LanguageController - to manage just languages for application data.
Multilanguage mode for dashboard tools

Multilanguage mode just for dashboard you can set by language parameter in app configuration: en-US, ru-RU e.t.c.

Multilanguage mode for data

There is an opportunity to set modes by application configuration, using parameter:

  • isMultilanguage - work in multilanguage mode just for content.

    When true, the sidebar link "Languages" in main menu will appear automatically.

And application configuration will take an appearance:

use Itstructure\AdminModule\Module;
use Itstructure\AdminModule\components\AdminView;
'modules' => [
    'admin' => [
        'class' => Module::class,
        'viewPath' => '@app/views/admin',
        'controllerMap' => [
            'catalog' => CatalogController::class,
        ],
        ...
        ...
        'isMultilanguage' => true,
    ],
],

In multilanguage mode, such a standard is adopted:

  1. All multilanguage fields will be with a language postfix:

    title_en

    description_en, e t. c.

  2. Single and multilanguage fields will be rendered in view template by spectial fields widget.

  3. All fields values after form sending will be loaded to the MultilanguageValidateModel by MultilanguageValidateComponent in CommonAdminController.

  4. Then MultilanguageValidateModel will load validated values in to the main model.

  5. In main model the field values will be processed by MultilanguageTrait to separate their between own main model and translate model.

  6. As a model in view template, it will the MultilanguageValidateModel, that is set by MultilanguageValidateComponent in CommonAdminController.

  7. Rules configuration for multilanguage fields validation need to be set in individual component config.

  8. Rules configuration for single fields validation need to be set in the main model.

  9. All the above configurations will be combined in MultilanguageValidateModel for general process.

To use this mode it's necessary:

  1. Set module parameter isMultilanguage on true in application config section mdules -> admin.

  2. Apply module migration: migrations/multilanguage/m171202_104405_create_language_table

    For that make next, if not already done:

    • Define admin module in application console config file:

      `php use Itstructure\AdminModule\Module; `

      `php 'modules' => [

        'admin' => [
            'class' => Module::class,
        ],
      

      ], `

    • In file yii, that is located in application root directory, set @admin alias:

      `php $application = new yii\console\Application($config);

      \Yii::setAlias('@admin', $application->getModule('admin')->getBasePath());

      $exitCode = $application->run(); `

      OR

      `php use Itstructure\AdminModule\Module; `

      `php $application = new yii\console\Application($config);

      \Yii::setAlias('@admin', Module::getBaseDir());

      $exitCode = $application->run(); `

    • Run command in console:

      yii migrate --migrationPath=@admin/migrations/multilanguage

    • Check if the sidebar link to manage languages is appeared.

  3. Application migrations must be extended from Itstructure\AdminModule\components\MultilanguageMigration

    It's necessary to automatically creation main table and translate table.

  4. Data base tables will have a structure, like in example:

    Main table "catalog"

     | id | order |      created_at     |      updated_at     |
     |----|-------|---------------------|---------------------|
     | 1  |   2   | 2018-01-14 18:06:33 | 2018-01-14 18:06:33 |
     | 2  |   1   | 2018-01-14 18:10:00 | 2018-01-14 18:10:00 |
     | 3  |   3   | 2018-01-14 19:05:15 | 2018-01-14 19:05:15 |
    

    Translate table "catalog_language"

     | catalog_id | language_id |   title   |      description     |      created_at     |      updated_at     |
     |------------|-------------|-----------|----------------------|---------------------|---------------------|
     |      1     |      1      | Catalog 1 |     Description 1    | 2018-01-14 18:06:33 | 2018-01-14 18:06:33 |
     |      1     |      2      | Каталог 1 |     Описание 1       | 2018-01-14 18:06:33 | 2018-01-14 18:06:33 |
     |      2     |      1      | Catalog 2 |     Description 2    | 2018-01-14 18:10:00 | 2018-01-14 18:10:00 |
     |      3     |      1      | Catalog 3 |     Description 3    | 2018-01-14 19:05:15 | 2018-01-14 19:05:15 |
     |      3     |      2      | Каталог 3 |     Описание 3       | 2018-01-14 19:05:15 | 2018-01-14 19:05:15 |
    

    Language table "language"

     | id | locale | shortName |  name   | default |      created_at     |      updated_at     |
     |----|--------|-----------|---------|---------|---------------------|---------------------|
     | 1  | en-US  |    en     | English |    1    | 2018-01-14 18:06:33 | 2018-01-14 18:06:33 |
     | 2  | ru-RU  |    ru     | Русский |    0    | 2018-01-14 18:10:00 | 2018-01-14 18:10:00 |
    

    Here,

    Not multilanguage fields: id, order.

    Multilanguage fields: title, description.

  5. For all described tables above, it is necessary to create CRUD model classes, like in exemple:

    Catalog - main model

    CatalogLanguage - translate model

    Language - already exists in module!

  6. In child application admin controller define function getModelName(), like in example:

    `php protected function getModelName():string {

     return Catalog::class;
    

    } `

  7. In child application admin controller define function getSearchModelName(), like in example:

    `php protected function getSearchModelName():string {

     return CatalogSearch::class;
    

    } `

  8. In main model, use trait, like in example:

    `php use Itstructure\AdminModule\models\MultilanguageTrait;

    class Catalog extends ActiveRecord {

     use MultilanguageTrait;
    

    ... `

    Using this trait, the multilanguage fields values, that are set like for example title_en, description_en, will be redirected to the translate model automatically.

    Not multilanguage fields, such as order e. t. c., will be set in to the main model automatically.

  9. To display form fields in view templates use special widget (installed by composer.json dependency):

    `php use Itstructure\FieldWidgets\Fields.php; `

    as in example:

    `php $form = ActiveForm::begin(); `

    `php echo Fields::widget([

     'fields' => [
         [
             'name' => 'title',
             'type' => FieldType::FIELD_TYPE_TEXT,
         ],
         [
             'name' => 'description',
             'type' => FieldType::FIELD_TYPE_TEXT_AREA,
         ],
     ],
     'model'         => $model,
     'form'          => $form,
     'languageModel' => new Language()
    

    ]) `

    `php Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) `

    `php ActiveForm::end(); `

    Here,

    • Language - is a class in Itstructure\AdminModule\models\Language.php

    • This widget (when for example two languages ru and en) will parse the form fields so:

      title_en

      title_ru

      description_en

      description_ru

    • if do not set the attribute languageModel, form fields will not be multilanguage and will be single.

    • $model - will be set automatically in CommonAdminController as object of Itstructure\AdminModule\models\MultilanguageValidateModel, in which the main model can be set after define her class name by method getModelName().

  10. Configure the multilanguage component multilanguage-validate-component for admin module with rules for validation multilanguage data in application config, like in example:

    use Itstructure\AdminModule\Module;
    use Itstructure\AdminModule\components\AdminView;
    use Itstructure\AdminModule\components\MultilanguageValidateComponent;
    
    'modules' => [
        'admin' => [
            'class' => Module::class,
            'viewPath' => '@app/views/admin',
            'controllerMap' => [
                'catalog' => CatalogController::class,
                ...
            ],
            'isMultilanguage' => true,
            'components' => [
                'view' => [
                    'class' => AdminView::class,
                    'skin' => AdminView::SKIN_GREEN_LIGHT,
                    'bodyLayout' => AdminView::LAYOUT_SIDEBAR_MINI,
                    'mainMenuConfig' => require __DIR__ . '/main-menu.php'
                ],
                'multilanguage-validate-component' => [
                
                    /**
                     * Component class.
                     */
                    'class' => MultilanguageValidateComponent::class,
                
                    /**
                     * List of models.
                     * Each model is identified by the name of the table.
                     * In the config attributes of each model, you need to specify:
                     * Dynamic (translated fields) dynamicFields.
                     * Field dynamicFields needs to have: 'name' - field name.
                     * Field dynamicFields (not necessary) may have 'rules'.
                     */
                    'models' => [
                        Catalog::tableName() => [
                            'dynamicFields' => [
                                [
                                    'name' => 'title',
                                    'rules' => [
                                        [
                                            'required',
                                            'message' => 'Field "{attribute}" must not be empty.'
                                        ],
                                        [
                                            'string',
                                            'max' => 255,
                                        ],
                                        [
                                            'unique',
                                        ]
                                    ]
                                ],
                                [
                                    'name' => 'description',
                                    'rules' => [
                                        [
                                            'required',
                                            'message' => 'Field "{attribute}" must not be empty.'
                                        ],
                                        [
                                            'string',
                                        ]
                                    ]
                                ],
                            ],
                        ]
                    ]
                ]
            ],
        ]
    ]
    

Useful feature:

After release 1.6.2 in MultilanguageValidateModel class the mainModelAttributes() method checking is added to check its presence in main model. This may be necessary when you need to validate fields that are not in the database table. These fields can be set in this method, in addition to the main fields. The method must return an array.

License

Copyright © 2018-2020 Andrey Girnik girnikandrey@gmail.com.

Licensed under the MIT license. See LICENSE.txt for details.

]]>
0
[news] Aliases 1.0.0 released Mon, 08 Jun 2020 15:29:17 +0000 https://www.yiiframework.com/news/291/aliases-1-0-0-released https://www.yiiframework.com/news/291/aliases-1-0-0-released samdark samdark

First version of yiisoft/aliases standalone package was released.

The package aim is to store path aliases, i.e. short name representing a long path (a file path, a URL, etc.). Path alias value may have another value as its part. For example, @vendor may store path to vendor directory while @bin may store @vendor/bin.

More description and usage examples are available in readme.

The code has 100% test coverage and 100% mutation score i.e. it is very stable.

]]>
0
[news] ElasticSearch extension 2.0.7 released Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/news/290/elasticsearch-extension-2-0-7-released https://www.yiiframework.com/news/290/elasticsearch-extension-2-0-7-released samdark samdark

We are very pleased to announce the release of ElastiSearch extension version 2.0.7. This release fixes a couple of bugs and adds PHP forward compatibility to the legacy 2.0.x branch that is compatible with ElasticSeach from 1.6.0 to 1.7.6.

See the CHANGELOG for details.

]]>
0
[extension] simpleforyou/yii2-and-vue Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/simpleforyou/yii2-and-vue https://www.yiiframework.com/extension/simpleforyou/yii2-and-vue mthoer mthoer
  1. Yii2 and Vue Project
  2. Directory Structure
  3. Controllers
  4. Views
  5. web/vue
  6. How to use for production

Yii2 and Vue Project

You can use this template to create a website with yii2 and vuejs.

Directory Structure

  assets/             contains assets definition (NOT NEEDED)
  commands/           contains console commands (controllers)
  config/             contains application configurations
  controllers/        contains Web controller classes
  mail/               contains view files for e-mails
  models/             contains model classes
  runtime/            contains files generated during runtime
  tests/              contains various tests for the basic application
  vendor/             contains dependent 3rd-party packages
  views/              contains view files for the Web application
  web/                contains the entry script and Web resurces
  src/                contains helper classes
  web/vue             contains the entry vue script part (Do not upload this folder to your web server)

Controllers

The use of the controllers is now different. Before, the controllers rendered a page or template. Now the controllers only return data because the views are changed by vue.

Views

Due to the changes only 2 files are needed now. The main layout and an index page. In both files the VueRenderer will be called which contains the js files and css files

web/vue

this folder is the vue project.

How to use for production

  1. run the build script in web/vue/package.json. After the build process is complete a new folder will be created under web the prod folder. This folder contains all the files built for vue.

  2. Upload the prod folder to your web folder on your server.

]]>
0
[news] ElasticSearch extension 2.0.6 released Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/news/289/elasticsearch-extension-2-0-6-released https://www.yiiframework.com/news/289/elasticsearch-extension-2-0-6-released samdark samdark

We are very pleased to announce the release of ElastiSearch extension version 2.0.6. This release fixes a couple of bugs and adds forward compatibility to the legacy 2.0.x branch that is compatible with ElasticSeach from 1.6.0 to 1.7.6.

See the CHANGELOG for details.

]]>
0
[extension] mallka/anti-crawl Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/mallka/anti-crawl https://www.yiiframework.com/extension/mallka/anti-crawl mallka mallka

Anti web crawl,

  1. Feature
  2. Install
  3. How to use

Feature

  • reject dev tool
  • run a javascript command if chome headless deteactd.
  • get browser fingerprint and post to SOME URL

Install

The preferred way to install this extension is through composer.

Either run

php composer.phar require --prefer-dist mallka/anti-crawl "dev-master"

or add

"mallka/anti-crawl": "dev-master"

to the require section of your composer.json file.

How to use

1. In view file:
<?= \mallka\anticrawl\Anti::widget([

        //the url of upload fingerprint,it will not fetch fingerprint if not set
        'uploadFingerUrl'=>Url::to(['/anticrawl/anti-log/create']), 
        
        //run js command if chrome headless detected.default is alert 
        'homelessJsCmd'=>'window.location.href="xxxxx";',  
         
                                   ]);?>
2.Create some action for collect data
//sample action ,please create table first.
<?php

	use Yii;
	

	class AntiLogController extends \yii\web\Controller
	{
		public function actionCreate()
		{
			$model = new AntiLog();
			$model->loadDefaultValues();
			$model->ip = Yii::$app->request->getUserIP();
			$model->url =Yii::$app->request->getReferrer();
			$model->finger = Yii::$app->request->post('fingerPrint');
			$model->finger_time = Yii::$app->request->post('executeTime',0);
			$model->finger_detail = Yii::$app->request->post('detail',0);
			$model->create_at=time();
			$model->user_id = Yii::$app->user->getId();
			$model->save();
			return;
		}

	}


]]>
0
[extension] vasadibt/yii2-protector Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/vasadibt/yii2-protector https://www.yiiframework.com/extension/vasadibt/yii2-protector vasadibt vasadibt

Yii2 Spam protector

  1. Installation
  2. Configuration
  3. Usage
  4. Features

Hide emails and phone numbers from spam robots

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require --prefer-dist vasadibt/yii2-protector "*"

or add

"vasadibt/yii2-protector": "*"

to the require section of your composer.json file.

Configuration

A simple example to integrate module.

'bootstrap' => [
    // ...
    'protector',
],
'components' => [
    // ...
    'protector' => [
         'class' => '\vasadibt\protector\ProtectData',
    ],
],

Usage

Once the extension is installed, simply use it in your code by by html format:

<h2>Contact</h2>

<a href="tel:[[protect:0036 70 1111 222]]">[[protect:0036 70 1111 222]]</a>

<a href="mailto:[[protect:info@mycompany.com]]">[[protect:info@mycompany.com]]</a>

<h3>Write to me<span class="email">[[protect:info@company.com]]</span></h3>

Or use php helpers

<?= Yii::$app->protector->tel('0036 70 1111 222') ?>

<?= Yii::$app->protector->mailto('info@company.com') ?>

<span><?= Yii::$app->protector->protect('0036 70 1111 222') ?></span>

Features

Auto detect mode

If you want to use Auto Detect mode then turn on:

'protector' => [
    'class' => '\vasadibt\protector\ProtectData',
    'enableAutoDetect' => 'true',
    'autoDetectPatterns' => [ // Optional you can use custom regex list  
        "/[-0-9a-zA-Z.+_]+@[-0-9a-zA-Z.+_]+.[a-zA-Z]{2,4}/U",
        // etc ... 
    ],
    'autoDetectMatchCallback' => function($match){ // Optional, if you want to validate match  
        return $match != 'bad@match.com';
    },
],
Plugin turn off

If you want to disable plugin then set enable false:

<?php Yii::$app->protector->enable = false; ?>
Plugin debug

If you want to view key value paired generated list than enable debug mode and show javascript protectorDebug variable in html inspector:

<?php Yii::$app->protector->debug = true; ?>
console.log(protectorDebug);
]]>
0
[extension] codespede/simple-multi-threader Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/codespede/simple-multi-threader https://www.yiiframework.com/extension/codespede/simple-multi-threader mhs135 mhs135

Simple Multi Threader

  1. Installation
  2. How to use
  3. Configurable Options
  4. Making it compatible with the platform you use
  5. Debugging

A simple PHP Multithreader extension which is ready for use without any setups or configurations.

  • No need to integrate any Queues or similar implementations
  • Write the code you want to process in the background in a closure(anonymous function) along with arguments(if any) and provide it to the extension. That's all.
  • Works with Core PHP(Normal PHP), Laravel and Yii2 out of the box.
  • Can work with any PHP Framework/Platform by just adding the required bootstrap code(See here).
  • Compatible with PHP installations in Windows and *nix(Unix like)

Installation

The preferred way to install this extension is through composer.

Either run

composer require codespede/simple-multi-threader

or add

"codespede/simple-multi-threader": "*"

to the require section of your composer.json.

How to use

If you want to run some code in the background, just provide it in a closure(anonymous function) to an object of the class Threader as below:

$threader = new cs\simplemultithreader\Threader(['arguments' => ['myObject' => new \stdClass, 'myArray' => ['a', 'b', 'c']]]);
$jobId = $threader->thread(function($arguments){
	//your code here..
 	//you can access any of the above given $arguments here like $arguments['myObject'] or $arguments['myArray']
});

That's all, the Threader will create the required job files in the specified $jobsDir and will start a PHP process in the background executing your code. The main thread(code above and after $threader->thread...) will run without waiting for the sub-thread(code in the closure) to finish. You will also get the started job's id(an md5 string) as the return value of the method thread which you can use for debugging.

Any data returned from the closure function will be logged to a file with name smt_<jobId>.log in the default directory specified for logs: smt-logs.

For examples, click here

Configurable Options

$arguments - Arguments for the closure - type: mixed, default = null

$jobsDir - Directory(auto-created if not existing) where temporary job files will be stored - type: string, default = "smt-jobs"

$logsDir - Directory(auto-created if not existing) where log files will be stored - type: string, default = "smt-logs"

$nohup - Whether to ignore the HUP (hangup) signal in Unix based systems - type: boolean, default = true

$helperClass - Fully qualified class name of the Helper to be used. - type: string, default = "cs\\simplemultithreader\\CommandHelper"

Any option above can be used to configure the Threader in the below way: $threader = new cs\simplemultithreader\Threader(['jobsDir' => 'MyJobsDir', 'helperClass' => '\namespace\of\MyHelperClass']); or like: $threader = new cs\simplemultithreader\Threader; $threader->arguments = ['myObject' => new \stdClass, 'myArray' => ['a', 'b', 'c']]; $threader->jobsDir = 'MyJobsDir'; ...

Making it compatible with the platform you use

Even if your application is not built either on Core PHP, Laravel or Yii2 which are already supported, you can make it compatible with this extension by the below steps:

There are two ways to accomplish this:

  1. Easiest way - Insert the bootstrap code of your Platform before your code in the closure.
    $jobId = $threader->thread(function($arguments){
       //bootstrap code here..
       //your actual code here..
    });
    
  2. Recommended way - Extend $helperClass with your own Helper class and include the corresponding bootstrap function in it as below:

    Suppose the platform you use is WordPress. Create a function in the extended Class as below: public function executeWordpressBootStrap(){ //code required to bootstrap Wordpress.. } and override the getPlatform method to just return the string 'wordpress'(as WordPress is the only platform you use in that app) as below: ` public function getPlatform(){

    return 'wordpress';
    

    } ` That's all! executeWordpressBootStrap will be executed before executing your code allowing Wordpress's native functions and usages in your code inside the Closure.

    By doing this just once, the extension is now ready for executing any code(normal and platform related) anywhere in your application. You're also getting the freedom to override any additional logic defined in the CommandHelper class.

    Pull Requests for supporting additional platforms are always welcome!

Debugging

Whenever an exception gets thrown from your code provided in the closure, it will be logged with a filename: smt_<jobId>_error.log. You can find the details of the error including the Stack Trace inside it.

]]>
0
[extension] tecnickcom/tcpdf Mon, 04 May 2020 06:44:38 +0000 https://www.yiiframework.com/extension/tecnickcom/tcpdf https://www.yiiframework.com/extension/tecnickcom/tcpdf iteranq iteranq

TCPDF

  1. IMPORTANT
  2. Description
  3. Developer(s) Contact

PHP PDF Library

Donate via PayPal Please consider supporting this project by making a donation via PayPal

IMPORTANT

A new version of this library is under development at https://github.com/tecnickcom/tc-lib-pdf and as a consequence this version will not receive any additional development or support. This version should be considered obsolete, new projects should use the new version as soon it will become stable.

Description

PHP library for generating PDF documents on-the-fly.

Main Features:
  • no external libraries are required for the basic functions;
  • all standard page formats, custom page formats, custom margins and units of measure;
  • UTF-8 Unicode and Right-To-Left languages;
  • TrueTypeUnicode, OpenTypeUnicode v1, TrueType, OpenType v1, Type1 and CID-0 fonts;
  • font subsetting;
  • methods to publish some XHTML + CSS code, Javascript and Forms;
  • images, graphic (geometric figures) and transformation methods;
  • supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/script/formats.php)
  • 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extension, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417;
  • JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;
  • automatic page header and footer management;
  • document encryption up to 256 bit and digital signature certifications;
  • transactions to UNDO commands;
  • PDF annotations, including links, text and file attachments;
  • text rendering modes (fill, stroke and clipping);
  • multiple columns mode;
  • no-write page regions;
  • bookmarks, named destinations and table of content;
  • text hyphenation;
  • text stretching and spacing (tracking);
  • automatic page break, line break and text alignments including justification;
  • automatic page numbering and page groups;
  • move and delete pages;
  • page compression (requires php-zlib extension);
  • XOBject Templates;
  • Layers and object visibility.
  • PDF/A-1b support.
Third party fonts:

This library may include third party font files released with different licenses.

All the PHP files on the fonts directory are subject to the general TCPDF license (GNU-LGPLv3), they do not contain any binary data but just a description of the general properties of a particular font. These files can be also generated on the fly using the font utilities and TCPDF methods.

All the original binary TTF font files have been renamed for compatibility with TCPDF and compressed using the gzcompress PHP function that uses the ZLIB data format (.z files).

The binary files (.z) that begins with the prefix "free" have been extracted from the GNU FreeFont collection (GNU-GPLv3). The binary files (.z) that begins with the prefix "pdfa" have been derived from the GNU FreeFont, so they are subject to the same license. For the details of Copyright, License and other information, please check the files inside the directory fonts/freefont-20120503 Link : http://www.gnu.org/software/freefont/

The binary files (.z) that begins with the prefix "dejavu" have been extracted from the DejaVu fonts 2.33 (Bitstream) collection. For the details of Copyright, License and other information, please check the files inside the directory fonts/dejavu-fonts-ttf-2.33 Link : http://dejavu-fonts.org

The binary files (.z) that begins with the prefix "ae" have been extracted from the Arabeyes.org collection (GNU-GPLv2). Link : http://projects.arabeyes.org/

ICC profile:

TCPDF includes the sRGB.icc profile from the icc-profiles-free Debian package: https://packages.debian.org/source/stable/icc-profiles-free

Developer(s) Contact

]]>
0
[news] Injector 1.0.0 released Sat, 02 May 2020 23:59:33 +0000 https://www.yiiframework.com/news/288/injector-1-0-0-released https://www.yiiframework.com/news/288/injector-1-0-0-released samdark samdark

Injector can invoke methods or create objects resolving their dependencies from a PSR-11 container via autowiring. It can:

  • Invoke callable or create an object of a given class.
  • Resolve dependencies by parameter types using the given PSR-11 container.
  • Pass concrete dependency instances by type.
  • Pass arguments by name.

As one of the central parts of Yii 3 it is what does all the auto-wiring into action handlers and widgets.

Since is a framework-independent package, it can be used in any PHP project.

Check it out

]]>
0
[news] Redis extension 2.0.13 released Sat, 02 May 2020 12:05:57 +0000 https://www.yiiframework.com/news/287/redis-extension-2-0-13-released https://www.yiiframework.com/news/287/redis-extension-2-0-13-released samdark samdark

We are very pleased to announce the release of Redis extension version 2.0.13.

This version adds Redis 5.0 stream commands.

See the CHANGELOG for details.

]]>
0
[news] Gii extension 2.2.1 released Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/news/286/gii-extension-2-2-1-released https://www.yiiframework.com/news/286/gii-extension-2-2-1-released samdark samdark

We are very pleased to announce the release of Gii extension version 2.2.1.

This release allows the usage of anonymous generators.

See the CHANGELOG for details.

]]>
0
[news] ApiDoc extension version 2.1.4 released Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/news/285/apidoc-extension-version-2-1-4-released https://www.yiiframework.com/news/285/apidoc-extension-version-2-1-4-released samdark samdark

We are very pleased to announce the release of the ApiDoc extension version 2.1.4.

This release add support for @property and @method annotations.

See the CHANGELOG for a full list of changes.

]]>
0
[news] Shell extension 2.0.4 released Sat, 02 May 2020 11:46:01 +0000 https://www.yiiframework.com/news/284/shell-extension-2-0-4-released https://www.yiiframework.com/news/284/shell-extension-2-0-4-released samdark samdark

We are very pleased to announce the release of Shell extension version 2.0.4. This version allows installing psy/psysh ~0.10.3.

]]>
0
[news] Yii 2.0.35 Mon, 04 May 2020 11:05:11 +0000 https://www.yiiframework.com/news/283/yii-2-0-35 https://www.yiiframework.com/news/283/yii-2-0-35 samdark samdark

We are very pleased to announce the release of Yii Framework version 2.0.35. Please refer to the instructions at https://www.yiiframework.com/download/ to install or upgrade to this version.

Version 2.0.35 fixes 13 issues and adds some small enhancements:

  • Query::withQuery() method that can be used for CTE.
  • yii\i18n\Formatter::$currencyDecimalSeparator to allow setting custom symbols for currency decimal in IntlNumberFormatter.
  • SameSite for cookies now works on PHP < 7.3.

Thanks to all Yii community members who contribute to the framework, translators who keep documentation translations up to date and community members who answer questions at forums.

There are many active Yii communities so if you need help or want to share your experience, feel free to join them.

A complete list of changes can be found in the CHANGELOG.

]]>
0
[extension] djidji/yii2-default-url-rule Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/djidji/yii2-default-url-rule https://www.yiiframework.com/extension/djidji/yii2-default-url-rule djidji01 djidji01

UrlRule for default basic logic in parsing and creating pretty Url.

  1. Conventions
  2. Installation
  3. Usage
  4. Examples

Parsing and creating url with arguments pretified as well instead of in query string format. Route and argument segments of url are separated by the UrlManager->routeParam

Conventions

  1. Argument is prettified only if its name is declared as action method parameter otherwise it stay in query string format;

  2. Arguments are orderer by the way corresponding action method parameters are declared;

  3. Optional Argument nth binds to its corresponding action method parameter only if all arguments --nth are explicitly passed. --nth argments are inserted at their respective places in created url if were nor explicit;

  4. Arguments less than required or more than action method parameters show 404

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require --prefer-dist djidji/yii2-default-url-rule "*"

or add

"djidji/yii2-default-url-rule": "*"

to the require section of your composer.json file.

Usage

Once the extension is installed, simply use it in your code by declaring in rules of Urlmanager :

'rules' => [
    // ...other url rules...
    [
        'class' => 'djidji\DefaultUrlRule',
        // complete file path to save routes.
        'routesFile' => '@app/config/routes.php'
    ],
]

Examples

class PostController extends Controller
{
    public function actionIndex($category,$year=2015,$tag='')
    {

    }
    public function actionView($id)
    {

    }
}
To create Url
  • Url::to(['post/index', 'year' => 2014, 'category' => 'php']) creates /index.php/post/index/r/php/2014 ;

  • Url::to(['post/index', 'category' => 'php']) creates /index.php/post/index/r/php;

  • Url::to(['post/index', 'category' => 'php','tag'=>'programming']) creates /index.php/post/index/r/php/2015/programming. default value of parameter 'year' is inserted at its place;

  • Url::to(['post/index','year' => 2014]) result to false because the argument for required $category parameter is not passed;

  • Url::to(['post/view', 'id' => 100]) creates /index.php/post/view/r/100 ;

  • Url::to(['post/view', 'id' => 100, 'source' => 'ad']) creates creates /index.php/post/view/r/100?source=ad. Because "source" argument is not declared as actionView methoth, it is appended as a query parameter in the created URL.

To parse Url
  • /index.php/post/index result to false because actionIndex has required firs parameter that need argments to be passed.

  • /index.php/post/index/r/php is parsed to ['post/index', ['category' => 'php']] ;

  • /index.php/post/index/r/php/2015/programming is parsed to ['post/index', ['category' => 'php','tag'=>'programming','year'=>2015]] ;

  • /index.php/post/index/r/php/programming is parsed to ['post/index', ['category' => 'php','year'=>'programming']] ;

  • /index.php/post/view/r/100?source=ad is parsed to ['post/view', ['id' => 100]];

  • /index.php/post/view/r/100/ad?source=ad result to false because actionView method expect one argment instead of two.

]]>
0
[extension] hail812/yii2-adminlte3 Sat, 25 Apr 2020 05:52:06 +0000 https://www.yiiframework.com/extension/hail812/yii2-adminlte3 https://www.yiiframework.com/extension/hail812/yii2-adminlte3 hail812 hail812

hail812/yii2-adminlte3

  1. Installation
  2. Usage
  3. AdminLTE Plugins

adminlte3 for yii2

home

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require --prefer-dist hail812/yii2-adminlte3 "*"

or add

"hail812/yii2-adminlte3": "*"

to the require section of your composer.json file.

Usage

Once the extension is installed, you can config the path mappings of the view component:

'components' => [
    'view' => [
         'theme' => [
             'pathMap' => [
                '@app/views' => '@vendor/hail812/yii2-adminlte3/src/views'
             ],
         ],
    ],
],

Copy files from @vendor/hail812/yii2-adminlte3/src/views to @app/views, then edit.

simply use:

<?= \hail812\adminlte3\widgets\Alert::widget([
    'type' => 'success',
    'body' => '<h3>Congratulations!</h3>'
]) ?>

AdminLTE Plugins

AdminLTE Plugins are not included in AdminLteAsset, if you want to use any of them you can add it dynamically with PluginAsset. For example:

/* @var $this \yii\web\View */

\hail812\adminlte3\assets\PluginAsset::register($this)->add('sweetalert2');

before this, maybe you should edit params.php:

return [
    'adminEmail' => 'admin@example.com',
    'hail812/yii2-adminlte3' => [
        'pluginMap' => [
            'sweetalert2' => [
                'css' => 'sweetalert2-theme-bootstrap-4/bootstrap-4.min.css',
                'js' => 'sweetalert2/sweetalert2.min.js'
            ]
        ]
    ]
];

or

/* @var $this \yii\web\View */

$bundle = \hail812\adminlte3\assets\PluginAsset::register($this);
$bundle->css[] = 'sweetalert2-theme-bootstrap-4/bootstrap-4.min.css';
$bundle->js[] = 'sweetalert2/sweetalert2.min.js';
]]>
0
[extension] ahmadasjad/yii2-widgets Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/ahmadasjad/yii2-widgets https://www.yiiframework.com/extension/ahmadasjad/yii2-widgets ahmadasjad ahmadasjad

yii2-widgets

Plus-Minus-Input

Installation
composer require ahmadasjad/yii2-widgets
Usage Example
<?php
echo \ahmadasjad\yii2Widgets\PlusMinusInput::widget([
    'name' => 'name_test',
    'plugin_options' => ['parser'=>'parseFloat', 'step'=>0.5]
]);
?>

Options to customize the plugin in plugin_options param:

[
    'val_min' => 0,
    'val_max' => 1000,
    'step' => 1,
    'parser' => 'parseInt',
    'container' => ['class' => 'input-group', 'id' => 'your-custom-id-container'],
    'plus' => ['id' => 'your-custom-id-plus', 'class' => 'btn btn-success', 'label' => '+',],
    'minus' => ['id' => 'your-custom-id-minus', 'class' => 'btn btn-danger', 'label' => '-',],
]

val_min Minimum value allowed for input

val_max Maximum value allowed for input

step How much value to be increased/decreased on press of plus/minus button

parser Javascript function to parse the input data. For example parseInt, parseFloat, etc.

plus configuration for plus button minus configuration for plus button

]]>
0
[news] Yii 2 Composer Installer 2.0.9 Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/news/282/yii-2-composer-installer-2-0-9 https://www.yiiframework.com/news/282/yii-2-composer-installer-2-0-9 samdark samdark

Yii 2 Composer plugin that handles many extension-related things behind the scenes, was updated to support upcoming Composer 2.

Update should not require any specific actions to be performed except issuing composer update command.

]]>
0
[extension] roopz/yii2-imap Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/roopz/yii2-imap https://www.yiiframework.com/extension/roopz/yii2-imap yiioverflow yiioverflow

Authour of Yii2-Imap is looking for a full-time job (Remote). More about my profile - https://roopz.club

yii2 Imap

This library can be used to read mails from IMAP server using PHP and Yii2.

Installation by composer

{
    "require": {
       "roopz/yii2-imap": "dev-master"
    }
}

Or $ composer require roopz/yii2-imap "dev-master"

Config example
'imap' => [
    'class' => '',
    'connection' => [
        'imapPath' => '',
        'imapLogin' => '',
        'imapPassword' => '',
        'serverEncoding' => 'utf-8',
        'searchEncoding' => 'US-ASCII',//MIME character set to use when searching strings. Not mandatory
        'attachmentsDir' => '/tmp',
        'decodeMimeStr' => false
    ]
]

Authour of Yii2-Imap is looking for a full-time job (Remote). More about my profile - Click here

Contribute

Feel free to contribute. If you have ideas for examples, add them to the repo and send in a pull request.

Apreciate

Dont forget to Leave me a "star" if you like it. Enjoy coding!

]]>
0
[extension] hoaaah/yii2-startbootstrap-sb-admin-2-asset Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/hoaaah/yii2-startbootstrap-sb-admin-2-asset https://www.yiiframework.com/extension/hoaaah/yii2-startbootstrap-sb-admin-2-asset hoAAah hoAAah

yii2-startbootstrap-sb-admin-2-asset

  1. Preview
  2. Requirement
  3. Installation
  4. Usage
  5. Menu Widget
  6. Card Widget
  7. TODO
  8. Creator

This packages contans an Asset Bundle of Startbootstrap SB Admin 2 for Yii2.

Startbootstrap SB Admin 2 is a responsive Bootstrap 4 created by Start Bootstrap.

Preview

SB Admin 2 Preview

Launch Live Preview

Start Bootstrap was created by and is maintained by David Miller, Owner of Blackrock Digital.

Start Bootstrap is based on the Bootstrap framework created by Mark Otto and Jacob Thorton.

Requirement

This Asset Bundle need Bootstrap 4. Since Yii2 used Bootstrap 3 by default, you must install and change every Bootstrap 3 Asset to Bootstrap 4. You can read this tutorial to migrate to Yii2 Bootstrap 4 :

Installation

The preferred way to install this extension is through composer.

Either run

composer require hoaaah/yii2-startbootstrap-sb-admin-2-asset:dev-master

or add

"hoaaah/yii2-startbootstrap-sb-admin-2-asset": "*"

to the require section of your composer.json file.

Usage

You can see views-examples folder for example use of this library

Menu Widget

You can use Menu widget in your sidebar. This widget optimize for Startbootstrap SB-Admin 2 template. This widget, like SB-Admin 2, only support 2 level menu.

You can see example use of this widget in views-exampale/views/layout/sidebar.php. `php use hoaaah\sbadmin2\widgets\Menu; echo Menu::widget([

'options' => [
    'ulClass' => "navbar-nav bg-gradient-primary sidebar sidebar-dark accordion",
    'ulId' => "accordionSidebar"
], //  optional
'brand' => [
    'url' => ['/'],
    'content' => <<<HTML
        <div class="sidebar-brand-icon rotate-n-15">
        <i class="fas fa-laugh-wink"></i>
        </div>
        <div class="sidebar-brand-text mx-3">SB Admin <sup>2</sup></div>        

HTML

],
'items' => [
    [
        'label' => 'Menu 1',
        'url' => ['/menu1'], //  Array format of Url to, will be not used if have an items
        'icon' => 'fas fa-fw fa-tachometer-alt', // optional, default to "fa fa-circle-o
        'visible' => true, // optional, default to true
        // 'options' => [
        //     'liClass' => 'nav-item',
        // ] // optional
    ],
    [
        'type' => 'divider', // divider or sidebar, if not set then link menu
        // 'label' => '', // if sidebar we will set this, if divider then no
    ],
    [
        'label' => 'Menu 2',
        // 'icon' => 'fa fa-menu', // optional, default to "fa fa-circle-o
        'visible' => true, // optional, default to true
        // 'subMenuTitle' => 'Menu 2 Item', // optional only when have submenutitle, if not exist will not have subMenuTitle
        'items' => [
            [
                'label' => 'Menu 2 Sub 1',
                'url' => ['/menu21'], //  Array format of Url to, will be not used if have an items
            ],
            [
                'label' => 'Menu 2 Sub 2',
                'url' => ['/menu22'], //  Array format of Url to, will be not used if have an items
            ],
        ]
    ],

    [
        'label' => 'Menu 3',
        'visible' => true, // optional, default to true
        // 'subMenuTitle' => 'Menu 3 Item', // optional only when have submenutitle, if not exist will not have subMenuTitle
        'items' => [
            [
                'label' => 'Menu 3 Sub 1',
                'url' => ['/menu21'], //  Array format of Url to, will be not used if have an items
            ],
            [
                'label' => 'Menu 3 Sub 2',
                'url' => ['/menu22'], //  Array format of Url to, will be not used if have an items
                'linkOptions' => [
                   'onClick' => 'alert("This is onClick")',
                ]
            ],
        ]
    ],
]

]); ` As you can see in above example, this Widget consist of two primary method.

Method Explanation
options Optional method. in this metod you will set any costumization of this Menu widget. It consist of ulClass method and ulId method
-- ulClass Set your <ul> class of this menu. default to "navbar-nav bg-gradient-primary sidebar sidebar-dark accordion"
-- ulId Set your <ul> id of this menu. default to "accordionSidebar"
items Required method. You must set this method in your widget. You can set items inside this method and it will create sub-menu items. Items and Sub Menu Item method use the same method, except for type method.
-- type Optional parameter, there are 3 category in this params. They are menu, divider, and sidebar. Default value of this params are menu
-- label Required parameter. This param will give label to your menu
-- icon Optional parameter. Will use font-awesome icon, so the value of this param will use fa class. Default to fas fa-circle
-- url Required parameter. Use Array value, like array on \yii\helpers\Url::to($array). If there are items parameter set, url will be ignored
-- visible Optional paremeter. Determined the visibility of menu. Value of visible are boolean. Default to true
-- linkOptions Optional paremeter. This param use array, give any options param to a tag, such as onClick or other options in link.

Card Widget

You can use card widget. This widget will create bootstrap card, optimize for this template

Example use of card are like below code `php use hoaaah\sbadmin2\widgets\Card; echo Card::widget([

'type' => 'cardBorder',
'label' => 'Label',
'sLabel' => '1000',
'icon' => 'fas fa-calendar',
'options' => [
    'colSizeClass' => 'col-md-3',
    'borderColor' => 'primary',
]

]); ` As you can see in above example, this Widget consist of some method.

Method Explanation
type Optional method. In this method you set type of your card. This widget support this type of card: cardBorder, ..... Default value of type is cardBorder
label Required method. In this method you set primary label of your card
sLabel Required method. In this method you set secondary label of your card
icon Required method. In this method you set icon of your card
options options method. Set options, available options are colSizeClass, borderColor
-- colSizeClass Set your col-size, value of this method are bootstrap col-size
-- borderColor Set your borderColor, value of this method are bootstrap color

TODO

Todo Widget

  • [x] Menu
  • [x] CardBorder
  • [x] CardBox
  • [ ] HeaderMenu
  • [ ] HeaderColor based on Params
  • [ ] Etc

Creator

This asset wrapper was created by and is maintained by hoaaah.

]]>
0
[extension] hoaaah/yii2-rest-api-template Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/hoaaah/yii2-rest-api-template https://www.yiiframework.com/extension/hoaaah/yii2-rest-api-template hoAAah hoAAah

yii2.png

Yii 2 REST API Template

  1. Yii2 REST API Template
  2. Supported Authentication
  3. Global Configuration of AuthMethods and RateLimiter
  4. Auth Scenario
  5. Access Token Management
  6. API versioning

Yii2 REST API Template

This is a a REST API TEMPLATE with Yii2. This template use Yii2-Micro approach so it will be lightweight and easy to deploy.

Installation

  1. Yii2 REST API Template
  2. Supported Authentication
  3. Global Configuration of AuthMethods and RateLimiter
  4. Auth Scenario
  5. Access Token Management
  6. API versioning

The preferred way to install this template is through composer.

Either run

composer create-project --prefer-dist hoaaah/yii2-rest-api-template [app_name]

Setup your database configuration from config/db.php. Create your database because this template will not create it for you :)

<?php
return [
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=your_db_name',
            'username' => 'root',
            'password' => '',
            'charset' => 'utf8',
        ],
    ],
];

Then run migration to create table in selected database.

yii migrate

Directory Structure

  1. Yii2 REST API Template
  2. Supported Authentication
  3. Global Configuration of AuthMethods and RateLimiter
  4. Auth Scenario
  5. Access Token Management
  6. API versioning

Since this template use MicroFramework approach, directory structure might be a little bit different from Yii2.

  config/             contains application configurations
  controllers/        contains Web controller classes
  migration/          contains list of your migration files
  models/             contains model classes
  modules/            contains your rest-api versioning (based on modules)
  vendor/             contains dependent 3rd-party packages
  web/                contains the entry script and Web resources

This template use modules as versioning pattern. Every version of API saved in a module. This template already have v1 module, so it means if consumer want to use v1 API, it can access https://your-api-url/v1/endpoint.

API Scenario

  1. Yii2 REST API Template
  2. Supported Authentication
  3. Global Configuration of AuthMethods and RateLimiter
  4. Auth Scenario
  5. Access Token Management
  6. API versioning

Supported Authentication

This template support 3 most used authentication. (Actually it's not me who make it, Yii2 already support it all :D ).

  1. HTTP Basic Auth: the access token is sent as the username. This should only be used when an access token can be safely stored on the API consumer side. For example, the API consumer is a program running on a server.
  2. Query parameter: the access token is sent as a query parameter in the API URL, e.g., https://example.com/users?access-token=xxxxxxxx. Because most Web servers will keep query parameters in server logs, this approach should be mainly used to serve JSONP requests which cannot use HTTP headers to send access tokens.
  3. OAuth 2: the access token is obtained by the consumer from an authorization server and sent to the API server via HTTP Bearer Tokens, according to the OAuth2 protocol.

Global Configuration of AuthMethods and RateLimiter

This template provide global configuration to set your application supported authMethods. You can find global configuration from app\config\params.php. Set your supported authMethods and RateLimiter from this file.

return [
    'useHttpBasicAuth' => true,
    'useHttpBearerAuth' => true,
    'useQueryParamAuth' => true,
    'useRateLimiter' => false,
];

Example use in behaviors looks like this

use app\helpers\BehaviorsFromParamsHelper;
use yii\rest\ActiveController;

class PostController extends ActiveController
{
    public $modelClass = 'app\models\Post';

    public function behaviors()
    {
        $behaviors = parent::behaviors();
        $behaviors = BehaviorsFromParamsHelper::behaviors($behaviors);
        // if you need other behaviors method use like this
        // $behaviors['otherMethods'] = $value;
        return $behaviors;
    }
}
Ratelimiter

To enable your ratelimiter configuration, please follow official guide from Yii documentation.

Auth Scenario

This template already have basic endpoint that you can use to start your REST-API. Such as:

Endpoint Type Usage
https://YOUR-API-URL/ GETlist all post created
https://YOUR-API-URL/view?id={id} GETView a post
https://YOUR-API-URL/login POST Login with username and password
https://YOUR-API-URL/signup POST Signup with username, email and password
https://YOUR-API-URL/v1/post GET List all post created
https://YOUR-API-URL/v1/post/create POST Create a new post (title, body)
https://YOUR-API-URL/v1/post/update?id={id} PUT / PATCH Update a post (title, body)
https://YOUR-API-URL/v1/post/delete?id={id} DELETE Delete a post
https://YOUR-API-URL/v1/post/view?id={id} GET View a post

Access Token Management

This application manage token via access_token table. Access Token have certain expiration based on $tokenExpiration value. Default Token Expiration are in seconds.

public $tokenExpiration = 60 * 24 * 365; // in seconds

In certain case you want to make a token expire before given tokenExpiration. Use `expireThisToken()method to achieve it.php $accessToken = AccessToken::findOne(['token' => $token]); $accessToken->expireThisToken(); `

Or you want to make all tokens from certain user expire, use `makeAllUserTokenExpiredByUserId($userId)method to achieve it.php $user = Yii::$app->user->identity; // or User::findOne($id) AccessToken::makeAllUserTokenExpiredByUserId($user->id); `

API versioning

This template give you versioning scenario based on module application. In Yii2 a module are self-contained software units that consist of model, views, controllers and other supporting components. This template already have v1 module, it means all of endpoint for API v1 created in this module. When you publish a new API version (that break backward compatibility / BBC), you can create a new module. For more information create a module, you can visit this Yii2 Guide on Creating Module.

TODO

  1. Yii2 REST API Template
  2. Supported Authentication
  3. Global Configuration of AuthMethods and RateLimiter
  4. Auth Scenario
  5. Access Token Management
  6. API versioning

Feel free to contribute if you have any idea.

  • [x] Rest API Template
  • [x] Login and signup in SiteController
  • [x] Example of versioning and Blog Scenario
  • [x] Authentication Type from params
  • [x] Rate Limit from params
  • [x] Change auth_key for every login
  • [x] Auth_key have expiration
  • [x] each auth_key have application token

Creator

  1. Yii2 REST API Template
  2. Supported Authentication
  3. Global Configuration of AuthMethods and RateLimiter
  4. Auth Scenario
  5. Access Token Management
  6. API versioning

This Template was created by and is maintained by Heru Arief Wijaya.

]]>
0
[extension] hoaaah/yii2-ajaxcrud-bs4 Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/extension/hoaaah/yii2-ajaxcrud-bs4 https://www.yiiframework.com/extension/hoaaah/yii2-ajaxcrud-bs4 hoAAah hoAAah

yii2-ajaxcrud

  1. Features
  2. Installation
  3. Usage

This is not my work, this repository created by johitvn, but because I (and maybe some other people) need to optimize this Gii Template with BS4, so I create new repository to accomodate it. If johitvn already merge my PR, I will delete this repository.

Latest Stable Version License Total Downloads

Gii CRUD template for Single Page Ajax Administration for yii2

yii2 ajaxcrud extension screenshot

Features

  • Create, read, update, delete in onpage with Ajax
  • Bulk delete suport
  • Pjax widget suport
  • Export function(pdf,html,text,csv,excel,json)

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require --prefer-dist hoaaah/yii2-ajaxcrud-bs4 "*"

or add

"hoaaah/yii2-ajaxcrud-bs4": "*"

to the require section of your composer.json file.

Usage

For first you must enable Gii module Read more about Gii code generation tool

Because this extension used kartik-v/yii2-grid extensions so we must config gridview module before

Let 's add into modules config in your main config file ``php 'modules' => [

'gridview' =>  [
    'class' => '\kartik\grid\Module'
]       

] ``

You can then access Gii through the following URL:

http://localhost/path/to/index.php?r=gii

and you can see Ajax CRUD Generator

Other Links Free download wordpress theme Free download html template

]]>
0
[news] Yii 2.0.34 Thu, 26 Mar 2020 21:02:55 +0000 https://www.yiiframework.com/news/281/yii-2-0-34 https://www.yiiframework.com/news/281/yii-2-0-34 samdark samdark

We are very pleased to announce the release of Yii Framework version 2.0.34. Please refer to the instructions at https://www.yiiframework.com/download/ to install or upgrade to this version.

Version 2.0.34 is a minor release of Yii 2.0 that fixes regressions introduced by 2.0.33.

Thanks to all Yii community members who contribute to the framework, translators who keep documentation translations up to date and community members who answer questions at forums.

There are many active Yii communities so if you need help or want to share your experience, feel free to join them.

A complete list of changes can be found in the CHANGELOG.

]]>
0
[news] Gii extension 2.2.0 released Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/news/280/gii-extension-2-2-0-released https://www.yiiframework.com/news/280/gii-extension-2-2-0-released samdark samdark

We are very pleased to announce the release of Gii extension version 2.2.0.

This release adds support for via() junction relations in model generator.

See the CHANGELOG for details.

]]>
0
[news] Yii 2.0.33 Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/news/279/yii-2-0-33 https://www.yiiframework.com/news/279/yii-2-0-33 samdark samdark

We are very pleased to announce the release of Yii Framework version 2.0.33. Please refer to the instructions at https://www.yiiframework.com/download/ to install or upgrade to this version.

Version 2.0.33 is a minor release of Yii 2.0 that fixes 22 issues and makes 4 enhancements:

  • Allow yii\data\ArrayDataProvider to control the sort flags for sortModels through
    yii\data\Sort::sortFlags property.
  • Use Instance::ensure() to initialize UrlManager::$cache.
  • Add StringValidator::$strict that can be turned off to allow any scalars.
  • Actions can now have bool typed params bound.

Thanks to all Yii community members who contribute to the framework, translators who keep documentation translations up to date and community members who answer questions at forums.

There are many active Yii communities so if you need help or want to share your experience, feel free to join them.

A complete list of changes can be found in the CHANGELOG.

]]>
0
[news] Redis extension 2.0.12 released Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/news/278/redis-extension-2-0-12-released https://www.yiiframework.com/news/278/redis-extension-2-0-12-released samdark samdark

We are very pleased to announce the release of Redis extension version 2.0.12.

This version fixes some bugs, adds ability to connect via SSL, and increases frequency of lock tries on mutex timeout.

See the CHANGELOG for details.

]]>
0
[news] Twig extension 2.4.0 released Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/news/277/twig-extension-2-4-0-released https://www.yiiframework.com/news/277/twig-extension-2-4-0-released samdark samdark

We are very pleased to announce two releases of Twig extension.

Version 2.4.0 upgrades Twig to 3.x and adds a built-in function for message translation:

{{ t('app', 'Hello, world!') }}
]]>
0
[news] Friendly exception 1.0.0 released Wed, 04 Mar 2020 20:27:15 +0000 https://www.yiiframework.com/news/276/friendly-exception-1-0-0-released https://www.yiiframework.com/news/276/friendly-exception-1-0-0-released samdark samdark

First Yii 3 package is released. Friendly Exception is an exception interface that provides a friendly name and a possible solution. Error handlers may consider the interface to render additional information right at the error screen.

The package is framework-independent so could be used with any PHP code.

Check it out

]]>
0
[news] Shell extension 2.0.3 released Tue, 03 Mar 2020 20:23:24 +0000 https://www.yiiframework.com/news/275/shell-extension-2-0-3-released https://www.yiiframework.com/news/275/shell-extension-2-0-3-released samdark samdark

We are very pleased to announce the release of Shell extension version 2.0.3. This version adds compatibility with symfony/var-dumper 5.x.

]]>
0
[news] Sphinx extension 2.0.13 released Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/news/274/sphinx-extension-2-0-13-released https://www.yiiframework.com/news/274/sphinx-extension-2-0-13-released samdark samdark

We are very pleased to announce the release of Sphinx extension version 2.0.13. This release fixes a bug in search() method.

See the CHANGELOG for details.

]]>
0
[wiki] Yii2 - Upgrading to Bootstrap 4 Fri, 20 Mar 2020 12:18:55 +0000 https://www.yiiframework.com/wiki/2556/yii2-upgrading-to-bootstrap-4 https://www.yiiframework.com/wiki/2556/yii2-upgrading-to-bootstrap-4 RichardPillay RichardPillay

Yii2 - Converting from Bootstrap3 to Bootstrap4

This article has been written because while conversion is a largely pain-free process, there are some minor issues. These are not difficult to solve, but it is not immediately obvious where the problem lies.

1 - Install Bootstrap4 My preference is to simply use composer. Change composer.json in the root of your project:

  • find the line that includes Bootstrap3.
  • Copy the line, and change the new line:

    • change bootstrap to bootstrap4
    • Now head over to https://github.com/yiisoft/ - the Yii2 repository on Github
    • Change the version string to that version number, and also change the ~ to ^
    • After this, you should have something like this below, maybe with higher version numbers:

      "yiisoft/yii2-bootstrap" : "~2.0.6", "yiisoft/yii2-bootstrap4" : "^2.0.8",

  • Save the file, then run 'composer update'
  • Use your IDE, text editor or whatever other means you have at your disposal to find all occurrences where bootstrap is used and change it bootstrap4. My suggestion is to search for the string yii\bootstrap\ and change it to yii\bootstrap4\. However, be careful - your IDE may require the backslash to be escaped. For example, Eclipse will find all files with the string easily, but the search string must have double-backslashes, while the replacement string must be left as single ones.
  • Now run all your tests and fix the problems that have arisen. Most of the failures will come from the fact that the Navbar no longer works, most likely. It's still there, just being rendered differently, and in my case it was invisible. This is because Bootstrap4 has changed some elements of the navbar. In my case, 14 tests failed - many of which failed due to the use of navbar content in some manner, such as looking for the Login link to infer whether the user is logged in or not.
    • You're not going to fix these issues without understanding them, so take a look at https://getbootstrap.com/docs/4.0/migration/. In particular, look at what has changed regarding the Navbars. The most meaningful is that Bootstrap4 no longer specifies a background, where Bootstrap3 did.
    • Open up frontend/viewslayouts/main.php in your editor, then look at your site in the browser. In my case no navbar, except for the Brand, and that is because I changed this from what was delivered as part of the Yii2 template, and it included a background. Since the rest of it was standard, there was nothing else - no ribbon strip, and no menu entries, although mousing into the area would show the links were there and could be clicked.
      • Find the line that starts with NavBar::begin and look at it's class options. In my case they were the original: 'class' => 'navbar-inverse navbar-fixed-top'
        • As I said before, no background is included, so add one: bg-dark - and check again. Now there's the ribbon back again, but no menu items.
        • Right-click on the ribbon and select "Inspect Element", or do whatever you have to do in your browser to inspect the page source. Looking at that starts to give you clues over what the navbar is doing. Looking at that, and referring back to the Bootstrap4 migration guide, I had the impression that neither navbar-inverse nor navbar-fixed-top were doing anything. So I removed those, and when refreshing the page, confirmed there were no changes.
        • More reading on the Bootstrap website gave me the bg-dark I mentioned earlier, and for the text, navbar-light or navbar-dark produced results.
        • now I had no menu items, but I did have a button that expanded the menu. Inspecting it's properties told me it was 'navbar-toggler', and the Bootstrap website told me it new to Bootstrap4, while it and was collapsed by default, 'navbar-expand' would expand it by default. That's cool - I reckon I'm going to add a setting for logged-in users that let them choose which they prefer. In the end, I opted for navbar-expand-md, which keeps it expanded unless the screen width is tight.

At the end of all this, I had the class line changed to something which gave me a navbar very similar to the original:

        //Bootstrap3: 'class' => 'navbar-inverse navbar-fixed-top',
        //Changed for Bootstrap4: 
        'class' => 'navbar navbar-expand-md navbar-light bg-dark',


Breadcrumbs

Note - March 2020: This entire section on Breadcrumbs may no longer be an issue. While I've left the section in as a tutorial, before making any changes read what Davide has to say in the user comments.

So, that fixed my navbar. Next, I noticed that the breadcrumbs were not quite right - the slash separating the path elements was no longer there. Preparing for a lot of debugging, I went to the Bootstrap site to look for a little inspiration. I didn't need to look any further - Bootstrap 4 requires each Breadcrumb element to have a class of "breadcrumb-item". After I spent a little time looking at vendors/yiisoft/yii2/widgets/Breadcrumbs.php to get some understanding of the issue, I discovered all that's needed is to change the itemTemplate and activeItemTemplate. Of course, since these are part of the Yii2 framework, you don't want to change that file, otherwise, it will probably get updated at some stage, and all your changes would be lost. Since both of these attributes are public, you can change them from outside the class, and the easiest place to do this is in 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>```


Data Grid ActionColumn One of my pages was a data grid generated for me by gii. On each row it has a set of buttons that you can click to view, edit or delete the row. Under Bootstrap 4, the ActionColumn disappeared. Viewing the page source showed me it was there, but I couldn't see it or click on it. Going to the migration guide, it turns out that Bootstrap 3 includes icons, but Bootstrap 4 doesn't. I got a lot of help from a question asked in the Yii2forum.. In the end, my solution was to get a local copy of FontAwesome 5 by including the line "fortawesome/font-awesome": "^5.12.1" in the require section of composer.json, and then choosing the icons that I wanted. I spent a lot of time figuring out how to do this, but when I was done, it seemed almost anti-climactic in it's simplicity. This is what I did in my data form:

            ['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')
                        ]);
                    }
                 ]
            ],


Functional Tests

Not seeing anything more visually, at least nothing that was obvious, I now ran the suite of tests. These were all previously passing, but now several of them failed. One of them was the Contact Form, so I ran that one separately and the tests informed me they were failing because they couldn't see the error message:

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.


I, on the other hand, could see the error messages on the form, so I used the browser's page source and discovered that the css class was no longer "help-block", it had changed to "invalid-feedback". Easy enough - in frontend/tests/_support/FunctionalTester.php, I changed the expected css class:

public function seeValidationError($message)
{
    $this->see($message, '.invalid-feedback');
}

Of course, this little excerpt is just an example. I found the same thing had to be done in several locations, but all were easily found and resolved.


After this, running my tests pointed me to no other problems, but I don't expect that to mean there aren't any other problems. While everything seems to be working so far, I expect there are more issues hiding in the woodwork. Somehow, those problems don't seem quite so insurmountable anymore.

]]>
0
[news] Auth Client extension 2.2.7 released Mon, 06 Jul 2020 08:39:20 +0000 https://www.yiiframework.com/news/272/auth-client-extension-2-2-7-released https://www.yiiframework.com/news/272/auth-client-extension-2-2-7-released samdark samdark

We are very pleased to announce the release of Auth Client extension version 2.2.7.

This release updates GitHub token transfer method since GitHub deprecated old way to transfer a token.

Note: Make sure to update your project before July 1st, 2020. That is when GitHub will stop supporting old token trafer method.

]]>
0
[wiki] UUID instead of an auto-increment integer for ID with Active Record Wed, 22 Apr 2020 13:09:03 +0000 https://www.yiiframework.com/wiki/2555/uuid-instead-of-an-auto-increment-integer-for-id-with-active-record https://www.yiiframework.com/wiki/2555/uuid-instead-of-an-auto-increment-integer-for-id-with-active-record grigori grigori

I have a dream ... I am happy to join with you today in what will go down in history as the greatest demonstration of bad design of Active Record.

I have an API. It's built with a RESTful extension over Active Record, and some endpoints provide PUT methods to upload files. By a REST design we create an entity with POST /video first, and then upload a video file with PUT /video/{id}/data.

How do we get the {id}? The essential solutuion is UUID generated by a client. It allows API application to be stateless and scale it, use master-master replication for databases and feel yourself a modern guy. If you have Postgres — lucky you, feel free to use the built-in UUID data type and close this article. With MySQL the essential solution is insert into users values(unhex(replace(uuid(),'-',''))... MySQL team recommends updating our INSERT queries. With Active Record it is not really possible. For fetching UUIDs it recommends adding a virtual column — this can be used.

If you design the application from ground up, you can use defferent fields for a binary and text representation of UUID, and reference them in different parts of an application, but I am bound to the legacy code.

Adding getId()/setId() won't help - data comes from a client in JSON and fills the model object with a setAttributes() call avoiding generic magic methods.

Here's the hack:

Step 1. Add a private $idText property

use yii\db\ActiveRecord;
class Video extends ActiveRecord
{
    private $idText;

Step 2. Add two validators and a filter

//check if value is a valid UUID
['id','match', 'pattern'=>'/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i'],
// convert UUID from text value to binary and store the text value in a private variable,
// this is a workaround for lack of mapping in active record
['id','filter','skipOnError' => true, 'filter' => function($uuid) {
    $this->idText = $uuid;
    return pack("H*", str_replace('-', '', $uuid));
}],
//now let's check if ID is taken
['id','unique','filter' => function(\yii\db\Query $q) {
    $q->where(['id' => $this->getAttribute('id')]);
}],

First rule is a validator for an input. Second rule is a filter preparing UUID to be written in a binary format and keeping the text form for output. Third one is a validator running a query over the binary value generated by a filter.

Note: I wrote $this->getAttribute('id'), $this->id returns a text form.

We can write a query to validate data, not to save it.

Step 3. Add getters

public function __get($name)
{
    return ($name === 'id') ? $this->getId() : parent::__get($name);
}

/**
 * Return UUID in a textual representation
 */
public function getId(): string
{
    if ($this->idText === NULL && $this->getIsNewRecord()){
        //the filter did not convert ID to binary yet, return the data from input
        return strtoupper($this->getAttribute('id'));
    }
    //ID is converted
    return strtoupper($this->idText ?? $this->getAttribute('id_text'));
}

When we call the $model->id property we need the getId() executed. But Active Record base class overrides Yii compoent default behavior and does not call a getter method of an object if a property is a field in a table. So I override the magic getter. From the other hand, a regexp valiator I wrote calls $model->id, triggering the getter before the UUID is saved to the private property. I check if the object is newly created to serve the text value for validator.

Note the strtoupper() call: client may send UUID in both upper and low cases, but after unpacking from binary we will have a value in upper case. I received different string values before storing data to DB and after fetching it. Convert the textual UUID value to an upper or lower case everywhere to avoid problems.

It looks weird to mutate data in a validator, but I found this is the best way. I belive I shouldn't use beforeSave() callback to set the binary value for generating SQL, and return the text value back in afterSave() - supporting this code would be a classic hell like #define true false;.

Step 4. Define the mapping for output

public function fields()
{
    $fields = parent::fields();
    $fields['id'] =function(){return $this->getId();};
    return $fields;
}

This method is used by RESTful serializers to format data when you access your API with GET /video requests.

So, now you can go the generic MySQL way

Step 5. add a virtual column

ALTER TABLE t1 ADD id_text varchar(36) generated always as
 (insert(
    insert(
      insert(
        insert(hex(id_bin),9,0,'-'),
        14,0,'-'),
      19,0,'-'),
    24,0,'-')
 ) virtual;

Step 5. Use Object Relation Mapping in Yii 3 when it's available and write mapping instead of these hacks.

P.S. A couple of helper functions.

declare(strict_types=1);

namespace common\helpers;


class UUIDHelper
{
    const UUID_REGEXP = '/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i';

    public static function string2bin(string $uuid): string
    {
        return pack("H*", str_replace('-', '', $uuid));
    }

    public static function bin2string(string $binary): string
    {
        return strtolower(join("-", unpack("H8time_low/H4time_mid/H4time_hi/H4clock_seq_hi/H12clock_seq_low", $binary)));
    }

    public static function isUUID(string $uuid): bool
    {
        return (bool)preg_match(self::UUID_REGEXP,$uuid);
    }
}
]]>
0
[wiki] Yii v2 snippet guide Fri, 03 Jul 2020 12:47:50 +0000 https://www.yiiframework.com/wiki/2552/yii-v2-snippet-guide https://www.yiiframework.com/wiki/2552/yii-v2-snippet-guide rackycz rackycz
  1. Intro
  2. Prerequisities
  3. Yii demo app + GitLab
  4. Automatical copying from GitLab to FTP
  5. User management + DB creation + login via DB
  6. i18n translations
  7. Switching languages + session + lang-dropdown in the top menu
  8. Simple access rights
  9. Nice URLs
  10. How to redirect web to subfolder /web
  11. Auto redirection from login to desired URL
  12. What to change when exporting to the Internet
  13. Saving contact inqueries into DB
  14. Tests - unit + opa
  15. Adding a google-like calendar
  16. Scenarios - UNKNOWN SCENARIO EXCEPTION
  17. Richtext / wysiwyg HTML editor - Summernote
  18. SEO optimization
  19. Other useful links
  20. jQuery + draggable/droppable on mobile devices (Android)
  21. Enhancing Gii
  22. Webproject outsite docroot (htdocs) folder (Windows)
  23. Modal window + ajax
  24. Simple Bootstrap themes
  25. Yii2 + Composer
  26. Favicon
  27. GridView + DatePicker in filter + filter reset
  28. Drop down list for foreign-key column
  29. GridViev - Variable page size

Intro

Hi all!

Please note, that this article will be updated regularly as I have more and more snippets so come back in a few weeks

This snippet guide works with the basic Yii demo application and enhances it. It continues in my series of simple Yii tutorials. Previous two contain basic info about MVC concept, exporting to Excel and other topics so read them as well, but they are meant for Yii v1. I started with them cca in year 2011:

... and today I am beginning with Yii 2 so I will also gather my snippets and publish them here so we all can quickly setup the yii-basic-demo just by copying and pasting. This is my goal - to show how-to without long descriptions.

I was suprised that the Yii 2 demo application does not contain some basic functionalities (like login via DB, translations etc) which must be implemented in the most of web projects so I will focus on them. Plus I will talk about GitLab.

If you find any problems in my snippets, let me know, please.

.

.

Prerequisities

Skip this paragraph if you know how to run your Yii demo project...

I work with Win10 + XAMPP Server so I will expect this configuration. Do not forget to start the server and enable Apache + MySQL in the dialog. Then test that following 2 URLs work for you

You should also download the Yii basic demo application and place it into the htdocs folder. In my case it is here:

  • C:\xampp\htdocs

And your index.php should be here:

  • C:\xampp\htdocs\basic\web\index.php

If you set things correctly up, following URL will open your demo application. Now it will probably throw an exception:

The Exception is removed by entering any text into attribute 'cookieValidationKey' in file:

  • C:\xampp\htdocs\basic\config\web.php

Dont forget to connect Yii to the DB. It is done in file:

  • C:\xampp\htdocs\basic\config\db.php

... but it should work out-of-the-box if you use DB name "yii2basic" which is also used in examples below ...

.

.

Yii demo app + GitLab

Once you download and run the basic app, I recommend to push it into GitLab. You will probably need a SSH certificate which can be generated like this using PuTTYgen. When I work with Git I use TortoiseGIT which integrates all git functionalities into the context menu in Windows File Explorer.

First go to GitLab web and create a new project. Then you might need to fight a bit, because the process of connecting your PC to GIT seems to be quite complicated. At least for me.

Once things work, just create an empty folder, right click it and select Git Clone. Enter your git path, best is this format:

Note: What works for me the best is using the following command to clone my project and system asks me for the password. Other means of connection usually refuse me. Then I can start using TortoiseGIT.

git clone https://{username}@gitlab.com/{username}/{myProjectName}.git

When cloned, copy the content of the "basic" folder into the new empty git-folder and push everything except for folder "vendor". (It contains 75MB and 7000 files so you dont want to have it in GIT)

Then you can start to modify you project, for example based on this "tutorial".

Thanks to .gitignore files only 115 files are uploaded. Te vendor-folder can be recreated using command composer install which only needs file composer.json to exist.

Automatical copying from GitLab to FTP

I found these two pages where things are explained: link link and I just copied something.

You only need to create 1 file in your GitLab repository. It is named .gitlab-ci.yml and it should contain following code:

variables:
  HOST: "ftp url"
  USERNAME: "user"
  PASSWORD: "password"
  TARGETFOLDER: "relative path if needed, or just ./"

deploy:
  script:
    - apt-get update -qq && apt-get install -y -qq lftp
    - lftp -c "set ftp:ssl-allow no; open -u $USERNAME,$PASSWORD $HOST; mirror -Rnev ./ $TARGETFOLDER --ignore-time --parallel=10 --exclude-glob .git* --exclude .git/ --exclude vendor --exclude web/assets --exclude web/index.php --exclude web/index-test.php" 
  only:
    - master

I just added some exclusions (listed below) and will probably add --delete in the future. Read linked webs.

  • exclude vendor = huge folder with 3rd party SW which is not in GIT
  • exclude web/assets = also some cache
  • exclude web/index.php = in GIT is your devel index with DEBUG mode enabled. You dont wanna have this file in productive environment
  • exclude web/index-test.php = tests are only on your computer and in GIT

.

.

User management + DB creation + login via DB

To create DB with users, use following command. I recommend charset utf8_unicode_ci (or utf8mb4_unicode_ci) as it allows you to use more international characters.

CREATE DATABASE IF NOT EXISTS `yii2basic` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;

USE `yii2basic`;

CREATE TABLE IF NOT EXISTS `user` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(45) NOT NULL,
  `password` VARCHAR(60) NOT NULL,
  `email`    VARCHAR(60) NOT NULL,
  `authKey`  VARCHAR(60),
  PRIMARY KEY (`id`))
ENGINE = InnoDB;

INSERT INTO `user` (`id`, `username`, `password`, `email`, `authKey`) VALUES (NULL, 'user01', '0497fe4d674fe37194a6fcb08913e596ef6a307f', 'user01@gmail.com', NULL);

If you must use MyISAM instead of InnoDB, just change the word InnoDB into MYISAM.

Then replace existing model User with following snippet

  • The model was generated by Gii and originally had 3 methods: tableName(), rules(), attributeLabels()
  • In order to use the DB for login, we needed to implement IdentityInterface which requires 5 new methods.
  • Plus we add 2 methods because of the default LoginForm and 1 validator.
<?php

namespace app\models;

use Yii;

class User extends \yii\db\ActiveRecord implements \yii\web\IdentityInterface {

    // When user detail is being edited we will only modify attribute password_new
    // Why? We dont want to load password-hash from DB and display it to the user
    // We only want him to see empty field and if it is filled in, password is changed on background
    public $password_new;
    public $password_new_repeat;

    // Use this scenario in UserController->actionCreate() right after: $model = new User() like this:
    // $model->scenario = User::SCENARIO_CREATE;
    // This will force the user to enter the password when new user is created
    // When user is edited, new password is not needed
    const SCENARIO_CREATE = "user-create";

    // ----- Default 3 model-methods by GII:

    public static function tableName() {
        return 'user';
    }

    public function rules() {
        return [
            [['username', 'email'], 'required'],
            [['password_new_repeat', 'password_new'], 'required', "on" => self::SCENARIO_CREATE],
            [['username', 'email'], 'string', 'max' => 45],
            ['email', 'email'],
            [['password', 'authKey'], 'string', 'max' => 60],
            [['password', 'password_new_repeat', 'password_new'], 'safe'],
            ['password_new_repeat', 'compare', 'operator' => '==', 'compareAttribute' => 'password_new'],
            ['password_new', 'compare', 'operator' => '==', 'compareAttribute' => 'password_new_repeat'],
            
            ['password_new_repeat', 'setPasswordWhenChanged'],
        ];
    }

    public function attributeLabels() {
        return [
            'id' => Yii::t('app', 'ID'),
            'username' => Yii::t('app', 'Username'),
            'password' => Yii::t('app', 'Password'),
            'password_new' => Yii::t('app', 'New password'),
            'password_new_repeat' => Yii::t('app', 'Repeat new password'),
            'authKey' => Yii::t('app', 'Auth Key'),
            'email' => Yii::t('app', 'Email'),
        ];
    }

    // ----- Password validator

    public function setPasswordWhenChanged($attribute_name, $params) {

        if (trim($this->password_new_repeat) === "") {
            return true;
        }

        if ($this->password_new_repeat === $this->password_new) {
            $this->password = sha1($this->password_new_repeat);
        }

        return true;
    }

    // ----- IdentityInterface methods:

    public static function findIdentity($id) {
        return static::findOne($id);
    }

    public static function findIdentityByAccessToken($token, $type = null) {
        return static::findOne(['access_token' => $token]);
    }

    public function getId() {
        return $this->id;
    }

    public function getAuthKey() {
        return $this->authKey;
    }

    public function validateAuthKey($authKey) {
        return $this->authKey === $authKey;
    }

    // ----- Because of default LoginForm:

    public static function findByUsername($username) {
        return static::findOne(['username' => $username]);
    }

    public function validatePassword($password) {
        return $this->password === sha1($password);
    }

}

Validators vs JavaScript:

  • There are 2 types of validators. All of them are used in method rules, but as you can see, the validator setPasswordWhenChanged is my custom validator and needs a special method. (I just abused a validator to set the password value, no real validation happens inside)
  • If a validator does not need this special method, it is automatically converted into JavaScript and is used on the web page when you are typing.
  • If a validator needs the method, it cannot be converted into JavaScript so the rule is checked only in the moment when user sends the form to the server - after successful JavaScript validation.

Now you can also create CRUD for the User model using GII:

CRUD = Create Read Update Delete = views and controller. On the GII page enter following values:

  • Model Class = app\models\User
  • Search Model Class = app\models\UserSearch
  • Controller Class = app\controllers\UserController
  • View Path can be empty or you can set: views\user
  • Again enable i18n

And then you can edit users on this URL: http://localhost/basic/web/index.php?r=user ... but it is not all. You have to modify the view-files so that correct input fields are displayed!

Open folder views\user and do following:

  • _form.php - rename input password to password_new then duplicate it and rename to password_new_repeat. Remove authKey.
  • _search.php - remove password and authKey.
  • index.php - remove password and authKey.
  • view.php - remove password and authKey.

Plus do not forget to use the new scenario in UserController->actionCreate() like this:

public function actionCreate()
{
  $model = new User();
  $model->scenario = User::SCENARIO_CREATE; // the new scenario!
  // ...

.

.

i18n translations

Translations are fairly simple, but I probably didnt read manuals carefully so it took me some time. Note that now I am only describing translations which are saved in files. I do not use DB translations yet. Maybe later.

1 - Translating short texts and captions

First create following folders and file.

  • "C:\xampp\htdocs\basic\messages\cs-CZ\app.php"

(Note that cs-CZ is for Czech Lanuage. For German you should use de-DE etc. Use any other language if you want.)

The idea behind is that in the code there are used only English texts and if you want to change from English to some other language this file will be used.

Now go to file config/web.php, find section "components" and paste the i18n section:

    'components' => [
        'i18n' => [
          'translations' => [
            '*' => [
              'class' => 'yii\i18n\PhpMessageSource',
              'basePath' => '@app/messages',
              'sourceLanguage' => 'en-US',
              'fileMap' => [
                'app' => 'app.php'
              ],
            ],
          ],
        ], // end of 'i18n'

        // ... other configurations

    ], // end of 'components'
    

Explanation of the asterisk * can be found in article https://www.yiiframework.com/doc/guide/2.0/en/tutorial-i18n

You surely saw that in views and models there are translated-texts saved like this:

Yii::t('app', 'New password'),

It means that this text belongs to category "app" and its English version (and also its ID) is "New password". So this ID will be searched in the file you just created. In my case it was the Czech file:

  • "C:\xampp\htdocs\basic\messages\cs-CZ\app.php"

Therefore open the file and paste there following code:

<?php
return [
    'New password' => 'Nové heslo',
];
?>

Now you can open the page for adding a new user and you will see than so far nothing changed :-)

We must change the language ... For now let's do it in a primitive and permanent way again in file config/web.php

$config = [
    // use your language
    // also accessible via Yii::$app->language
    'language' => 'cs-CZ',
    
    // This attribute is not necessary.
    // en-US is default value
    'sourceLanguage' => 'en-US',
    
    // ... other configs

2 - Translating long texts and whole views

If you have a view with long texts and you want to translate it into a 2nd language, it is not good idea to use the previous approach, because it uses the English text as the ID.

It is better to translate the whole view. How? ... Just create a sub-folder next to the view and give it name which will be identical to the target-lang-ID. In my case the 2nd language is Czech so I created following folder and copied my view in it. So now I have 2 identical views with identical names:

  • "C:\xampp\htdocs\basic\views\site\about.php" ... English
  • "C:\xampp\htdocs\basic\views\site\cs-CZ\about.php" ... Czech

Yii will automatically use the Czech version if needed.

.

.

Switching languages + session + lang-dropdown in the top menu

First lets add to file config/params.php attributes with list of supported languages:

<?php
return [
    // ...
    'allowedLanguages' => [
        'en-US' => "English",
        'cs-CZ' => "Česky",
    ],
    'langSwitchUrl' => '/site/set-lang',
];

This list can be displayed in the main menu. Edit file:

  • C:\xampp\htdocs\basic\views\layouts\main.php

And above the Nav::widget add few rows:

    $listOfLanguages = [];
    $langSwitchUrl = Yii::$app->params["langSwitchUrl"];
    foreach (Yii::$app->params["allowedLanguages"] as $langId => $langName) {
        $listOfLanguages[] = ['label' => Yii::t('app', $langName), 'url' => [$langSwitchUrl, 'langID' => $langId]];
    }

and then add one item into Nav::widge

    echo Nav::widget([
        // ...
        'items' => [
            // ...
            ['label' => Yii::t('app', 'Language'),'items' => $listOfLanguages],
            // ...

Now in the top-right corner you can see a new drop-down-list with list of 2 languages. If one is selected, action "site/setLang" is called so we have to create it in SiteController.

Note that this approach will always redirect user to the new action and his work will be lost. Nevertheless this approach is very simple so I am using it in small projects. More complex projects may require an ajax call when language is changed and then updating texts using javascript so reload is not needed and user's work is preserved. But I expect that when someone opens the web, he/she sets the language immediately and then there is no need for further changes.

The setLang action looks like this:

    public function actionSetLang($langID = "") {
        $allowedLanguages = Yii::$app->params["allowedLanguages"];
        $langID = trim($langID);
        if ($langID !== "" && array_key_exists($langID, $allowedLanguages)) {
            Yii::$app->session->set('langID', $langID);
        }
        return $this->redirect(['site/index']);
    }

As you can see when the language is changed, redirection to site/index happens. Also mind that we are not modifying the attribute from config/web.php using Yii::$app->language, but we are saving the value into the session. The reason is that PHP deletes memory after every click, only session is kept.

We then can use the langID-value in other controllers using new method beforeAction:

    public function beforeAction($action) {

        if (!parent::beforeAction($action)) {
            return false;
        }

        Yii::$app->language = Yii::$app->session->get('langID');

        return true;
    }

.. or you can create one parent-controller named for example BaseController. All other controllers will extend it.

<?php

namespace app\controllers;

use Yii;
use yii\web\Controller;

class BaseController extends Controller {

    public function beforeAction($action) {

        if (!parent::beforeAction($action)) {
            return false;
        }

        Yii::$app->language = Yii::$app->session->get('langID');

        return true;
    }

}

As you can see in the snippet above, other controllers must contain row "use app\controllers\BaseController" + "extends BaseController"

.

.

Simple access rights

Every controller can allow different users/guests to use different actions. Method behaviors() can be used to do this. If you generate the controller using GII the method will be present and you will just add the "access-part" like this:


// don't forget to add this import:
use yii\filters\AccessControl;

public function behaviors() {
  return [
    // ...
    'access' => [
      'class' => AccessControl::className(),
      'rules' => [
        [
          'allow' => true,
          'roles' => ['@'], // logged in users
          // 'roles' => ['?'], // guests
          // 'matchCallback' => function ($rule, $action) {
            // all logged in users are redirected to some other page
            // just for demonstration of matchCallback
            // return $this->redirect('index.php?r=user/create');
          // }
        ],
      ],
      // All guests are redirected to site/index in current controller:
      'denyCallback' => function($rule, $action) {
        Yii::$app->response->redirect(['site/index']);
      },
    ],
  ];
}

.. This is all I needed so far. I will add more complex snippet as soon as I need it ...

Details can be found here https://www.yiiframework.com/doc/guide/2.0/en/security-authorization.

.

.

Nice URLs

Just uncomment section "urlManager" in config/web.php .. htaccess file is already included in the basic demo. In case of problems see this link.

My problem was that images were not displayed when I enabled nice URLs. Smilar discussion here.

// Originally I used these img-paths:
<img src="..\web\imgs\myimg01.jpg"/>

/// Then I had to chage them to this:
Html::img(Yii::$app->request->baseUrl . '/imgs/myimg01.jpg')

// The important change is using the "baseUrl"

Note that Yii::$app->request->baseUrl returns "/myProject/web". No trailing slash.

.

.

How to redirect web to subfolder /web

Note: If you are using the advanced demo app, this link can be interesting for you.

Yii 2 has the speciality that index.php is hidden in the web folder. I didnt find in the official documentation the important info - how to hide the folder, because user is not interested in it ...

Our demo application is placed in folder:

  • C:\xampp\htdocs\basic\web\index.php

Now you will need 2 files named .htaccess

  • C:\xampp\htdocs\basic\web\.htaccess
  • C:\xampp\htdocs\basic\.htaccess

The first one is mentioned in chapter Nice URLs and looks like this:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]

The second is simpler:

RewriteEngine on
RewriteRule ^(.*)$ web/$1 [L]

... it only adds the word "web" into all URLs. But first we have to remove the word from URLs. Open file config/web.php and find section request. Add attribute baseUrl:

'request' => [
  // 'cookieValidationKey' => ...
  'baseUrl' => '/basic', // add this line
],

Now things will work for you. But it might be needed to use different value for devel and productive environment. Productive web is usually in the root-folder so baseUrl should be en empty string. I did it like this:

$baseUrlWithoutWebFolder = "";
if (YII_ENV_DEV) {
  $baseUrlWithoutWebFolder = '/basic';
}

// ...

'request' => [
  // 'cookieValidationKey' => ...
  'baseUrl' => $baseUrlWithoutWebFolder,
],

I will test this and if I find problems and solutions I will add them.

.

.

Auto redirection from login to desired URL

... to be added ...

.

.

What to change when exporting to the Internet

  • Delete file web/index-test.php
  • In file web/index.php comment you 2 first lines containing YII_DEBUG + YII_ENV
  • Delete the text from view site/login which says "You may login with admin/admin or demo/demo."

.

.

Saving contact inqueries into DB

DROP TABLE IF EXISTS `contact` ;

CREATE TABLE IF NOT EXISTS `contact` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL,
  `email` VARCHAR(45) NOT NULL,
  `subject` VARCHAR(100) NOT NULL,
  `body` TEXT NOT NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;
  • Create the DB table
  • Generate Model + CRUD using GII
  • In Site controller replace ContactForm with Contact (in section "use" and in actionContact) and in the action change the IF condition:
    use app\models\Contact;
    // ... 
    public function actionContact() {
      $model = new Contact();
      if ($model->load(Yii::$app->request->post()) && $model->save()) {
      // ...
    
  • Open the new contact model and add one attribute and 2 rules:
public $verifyCode;
// ...
  ['verifyCode', 'captcha'],
  ['email', 'email'],

// and translation for Captcha
'verifyCode' => Yii::t('app', 'Verification'),
  • You can also delete one paragraph from view/site/contact
    <p>
    Note that if you turn on the Yii debugger ...
    

Then some security - filtering users in the new ContactController:

public function beforeAction($action) {

  if (!parent::beforeAction($action)) {
    return false;
  }

  $guestAllowedActions = [];

  if (Yii::$app->user->isGuest) {
    if (!in_array($action->actionMethod, $guestAllowedActions)) {
      return $this->redirect(['site/index']);
    }
  }
  
  return true;
}

.

.

Tests - unit + opa

... text ...

.

.

Adding a google-like calendar

I needed to show user a list of his events in a large calendar so I used library fullcalendar.

Great demo which you can just copy and paste:

/*I added this style to hide vertical scroll-bars*/
.fc-scroller.fc-day-grid-container{
  overflow: hidden !important;
}
  • Don't forget to use these files for example in your view like this:
$this->registerCssFile('@web/css/fullcalendar/fullcalendar.css');
$this->registerCssFile('@web/css/fullcalendar/fullcalendar.print.css', ['media' => 'print']); 

$this->registerJsFile('@web/js/fullcalendar/moment.min.js', ['depends' => ['yii\web\JqueryAsset']]);
$this->registerJsFile('@web/js/fullcalendar/fullcalendar.min.js', ['depends' => ['yii\web\JqueryAsset']]);

// details here:
// https://www.yiiframework.com/doc/api/2.0/yii-web-view

... if you want to go pro, use NPM. The NPM way is described here.

API is here: https://fullcalendar.io/docs ... you can then enhace the calendar config from the example above

In order to make things work I had to force jQuery to be loaded before calendar scripts using file config/web.php like this

   'components' => [
        
		// ...
		
       'assetManager' => [
            'bundles' => [
                'yii\web\JqueryAsset' => [
                    'jsOptions' => [ 'position' => \yii\web\View::POS_HEAD ],
                ],
            ],
        ],

You can customize the calendar in many ways. For example different event-color is shown here. Check the source code.

.

.

Scenarios - UNKNOWN SCENARIO EXCEPTION

I have been using scenarios a lot but today I spent 1 hour on a problem - I had 2 scenarios and one of them was just assigned to the model ...

$model->scenario = "abc";

... but had no rule defined yet. I wanted to implement the rule later, but I didnt know that when you set a scenario to your model it must be used in method rules() or defined in method scenarios(). So take this into consideration. I expected that when the scenario has no rules it will just be skipped or deleted.

.

.

Richtext / wysiwyg HTML editor - Summernote

If you want to allow user to enter html-formatted text, you need to use some HTML wysiwyg editor, because ordinary TextArea can only work with plain text. It seems to me that Summernote is the simplest addon available:

// Add following code to file layouts/main.php .. 
// But make sure jquery is already loaded !! 
// - Read about this topic in chapter "Adding a google-like calendar"

<!-- include summernote css/js -->
<link href="http://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.12/summernote.css" rel="stylesheet">
<script src="http://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.12/summernote.js"></script>

// And then in any view you can use this code:

<script>
$(document).ready(function() {
  $('#summernote1').summernote();
  $('#summernote2').summernote();
});
</script>
<div id="summernote1">Hello Summernote</div>

<form method="post">
  <textarea id="summernote2" name="editordata"></textarea>
</form>

On this page I showed how to save Contacts inqueries into database. If you want to use the richtext editor in this section, open view contact/_form.php and just add this JS code:

<script>
$(document).ready(function() {
  $('#contact-body').summernote();
});
</script>

It will be saved to DB as HTML code. But this might be also a source of problems, because user can inject some dangerous HTML code. So keep this in mind.

Now you will also have to modify view contact/view.php like this in order to see nice formatted text:

DetailView::widget([
  'model' => $model,
  'attributes' => [
    // ...
    'body:html',
  ],
])

... to discover all possible formatters, check all asXXX() functions on this page:

.

.

SEO optimization

This is not really a YII topic but as my article is some kind of a code-library I will paste it here as well. To test your SEO score you can use special webs. For example seotesteronline, but only once per day. It will show some statistics and recommend enhancements so that your web is nicely shown on FB and Twitter or found by Google.

Important are for example OG meta tags or TWITTER meta tags. They are basicly the same. Read more here. You can test them at iframely.com.

Basic tags are following and you should place them to head:

  • Note that Twitter is using attribute "name" instead of "property" which is defined in OG
  • btw OG was introduced by Facebook. Twitter can process it as well, but SEO optimizers will report an error when Twitter's tags are missing.

<!DOCTYPE html>
<html lang="<?= Yii::$app->language ?>">
<head>

  <meta property="og:site_name" content="European Travel, Inc.">
  <meta property="og:title" content="European Travel Destinations">
  <meta property="og:description" content="Offering tour packages for individuals or groups.">
  <meta property="og:image" content="http://euro-travel-example.com/thumbnail.jpg">
  <meta property="og:url" content="http://euro-travel-example.com/index.htm">
  <meta name="twitter:card" content="summary_large_image">

  <!--  Non-Essential, But Recommended -->
  <meta property="og:site_name" content="European Travel, Inc.">
  <meta name="twitter:image:alt" content="Alt text for image">

  <!--  Non-Essential, But Required for Analytics -->
  <meta property="fb:app_id" content="your_app_id" />
  <meta name="twitter:site" content="@website-username">
  
  <!-- seotesteronline.com will also want you to add these: -->
  <meta name="description" content="blah blah">
  <meta property="og:type" content="website">
  <meta name="twitter:title" content="blah blah">
  <meta name="twitter:description" content="blah blah">
  <meta name="twitter:image" content="http://something.jpg">

Do not forget about file robots.txt and sitemap.xml:

// robots.txt can contain this:
User-agent: *
Allow: /

Sitemap: http://www.example.com/sitemap.xml
// And file sitemap.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
  <url>
    <loc>http://example.com/someFile.html</loc>
    <image:image>
      <image:loc>http://example.com/someImg.jpg</image:loc>
    </image:image>
  </url> 
</urlset> 

You can also minify here or here all your files. Adding "microdata" can help as well, but I have never used it. On the other hand what I do is that I compress images using these two sites tinyjpg.com and tinypng.com.

.

.

Other useful links

.

.

jQuery + draggable/droppable on mobile devices (Android)

JQuery and its UI extension provide drag&drop functionalities, but these do not work on Android or generally on mobile devices. You can use one more dependency called touch-punch to fix the problem. It should be loaded after jQuery and UI.

<!-- jQuery + UI -->
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>

<!-- http://touchpunch.furf.com/ -->
<!-- Use this file locally -->
<script src="./jquery.ui.touch-punch.min.js"></script>

And then standard code should work:

<!doctype html>

<html lang="en">
  <head>
    <meta charset="utf-8">

    <title>Title</title>

    <!-- jQuery + UI -->
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>

    <!-- http://touchpunch.furf.com/ -->
    <script src="./jquery.ui.touch-punch.min.js"></script>

    <style>
      .draggable {
        width: 100px;
        height: 100px;
        border: 1px solid red;
      }

      .droppable {
        width: 300px;
        height: 300px;
        border: 1px solid blue;
      }

      .over {
        background-color: gold;
      }
    </style>
  </head>

  <body>
    <div class="draggable my1">draggable my1</div>
    <div class="draggable my2">draggable my2</div>
    <div class="droppable myA">droppable myA</div>
    <div class="droppable myB">droppable myB</div>
  </body>


  <script>
    $( function() {

      // All draggables will return to their original position if not dropped to correct droppable
      // ... and will always stay in the area of BODY
      $(".draggable").draggable({ revert: "invalid", containment: "body" });

      // Demonstration of how particular droppables can accept only particular draggables
      $( ".droppable.myA" ).droppable({
        accept: ".draggable.my1",
        drop: function( event, ui ) {

          // positioning the dropped box into the target area
          var dropped = ui.draggable;
          var droppedOn = $(this);
          $(dropped).detach().css({top: 0,left: 0}).appendTo(droppedOn);    
          $(this).removeClass("over");
        },
        over: function(event, elem) {
          $(this).addClass("over");
          console.log("over");
        },
        out: function(event, elem) {
          $(this).removeClass("over");
        }
      });

      // Demonstration of how particular droppables can accept only particular draggables
      $( ".droppable.myB" ).droppable({
        accept: ".draggable.my2",
        drop: function( event, ui ) {

          // positioning the dropped box into the target area
          var dropped = ui.draggable;
          var droppedOn = $(this);
          $(dropped).detach().css({top: 0,left: 0}).appendTo(droppedOn);    
          $(this).removeClass("over");
        },
        over: function(event, elem) {
          $(this).addClass("over");
          console.log("over");
        },
        out: function(event, elem) {
          $(this).removeClass("over");
        }
      });

    });
  </script>

</html>

.

.

Enhancing Gii

If you do not like entering long model-paths and controller-paths in CRUD-generator, you can modify text boxes in "\vendor\yiisoft\yii2-gii\src\generators\crud\form.php" and enter default paths and then only manually add the name of the model.

if (!$generator->modelClass) {
	echo $form->field($generator, 'modelClass')->textInput(['value' => 'app\\models\\']);
	echo $form->field($generator, 'searchModelClass')->textInput(['value' => 'app\\models\\*Search']);
	echo $form->field($generator, 'controllerClass')->textInput(['value' => 'app\\controllers\\*Controller']);	
} else {
	echo $form->field($generator, 'modelClass');
	echo $form->field($generator, 'searchModelClass');
	echo $form->field($generator, 'controllerClass');
}

.

.

Webproject outsite docroot (htdocs) folder (Windows)

If you need to store you project for example in folder D:\GIT\EmployerNr1\ProjectNr2, you can. Just modify 2 files and restart Apache (I am using XAMPP under Win):

  • C:\Windows\System32\drivers\etc\hosts
127.0.0.1 myFictiveUrl.local
  • C:\xampp\apache\conf\extra\httpd-vhosts.conf
<VirtualHost *:80>
  DocumentRoot "D:\GIT\EmployerNr1\ProjectNr2"
  ServerName myFictiveUrl.local
  ServerAlias myFictiveUrl.local
  <Directory "D:\GIT\EmployerNr1\ProjectNr2">
    Options Indexes FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
    # New directive needed in Apache 2.4.3:
    Require all granted
  </Directory>
</VirtualHost>

You can then use http://myFictiveUrl.local in your browser

.

.

Modal window + ajax

Let's have a GridView (list of users) with edit-button which will open the edit-form in a modal window. Once user-detail is changed, ajax validation will be executed. If something is wrong, the field will be highlighted. If everything is OK and saved, modal window will be closed and the GridView will be updated.

Let's add the button to the GridView in the view index.php and let's wrap the GridView into the Pjax. Also ID is added to the GridView so it can be refreshed later via JS:

<?php yii\widgets\Pjax::begin();?>
<?= GridView::widget([
  'dataProvider' => $dataProvider,
  'filterModel' => $searchModel,
  'id' => 'user-list-GridView',
  'columns' => [
    ['class' => 'yii\grid\SerialColumn'],
      'id',
      'username',
      'email:email',
      ['class' => 'yii\grid\ActionColumn',
        'buttons' => [
          'user_ajax_update_btn' => function ($url, $model, $key) {
            return Html::a ( '<span class="glyphicon glyphicon-share" aria-hidden="true"></span> ', 
			  ['user/update', 'id' =>  $model->id], 
			  ['class' => 'openInModal', 'data-modal-titl' => 'Some text'] 
		    );
          },
        ],
        'template' => '{update} {view} {delete} {user_ajax_update_btn}'
      ],
  ],
]); ?>
<?php yii\widgets\Pjax::end();?>

Plus add (to the end of this view) following JS code:

<?php
// This section can be moved to "\views\layouts\main.php"
yii\bootstrap\Modal::begin([
  'header' => '<span id="modalTitle">Title</span>',
  'id' => 'modalDialog1',
  'size' => 'modal-lg',
]);
echo "<div id='modalContent'></div>";
yii\bootstrap\Modal::end();

$this->registerJs(
  "$('a.openInModal').click(function(e){  
  e.preventDefault();
  $('#modalDialog1 #modalTitle').text('aaa');
  $('#modalDialog1').modal('show')
    .find('#modalContent')
    .load($(this).attr('href'));
  return false;
  });",
  yii\web\View::POS_READY,
  'modalHandler'
);
?>

Now we need to modify the updateAction:

public function actionUpdate($id)
{
  $model = $this->findModel($id);

  if ($model->load(Yii::$app->request->post()) && $model->save()) {
    if (Yii::$app->request->isAjax) {
      return "<script>"
        . "$.pjax.reload({container:'#user-list-GridView'});"
        . "$('#modalDialog1').modal('hide');"
        . "</script>";
    }

    return $this->redirect(['view', 'id' => $model->id]);
  }

  if (Yii::$app->request->isAjax) {
    return $this->renderAjax('update', [
      'model' => $model,
    ]);
  }
    
  return $this->render('update', [
        'model' => $model,
  ]);
}

And file _form.php:

<?php yii\widgets\Pjax::begin([
  'id' => 'user-detail-Pjax', 
  'enablePushState' => false, 
  'enableReplaceState' => false
]);  ?>

<?php $form = ActiveForm::begin([
  'id'=>'user-detail-ActiveForm',
  'options' => ['data-pjax' => 1 ]
  ]); ?>

<?= $form->field($model, 'username')->textInput(['maxlength' => true]) ?>

<?= $form->field($model, 'password')->passwordInput(['maxlength' => true]) ?>

<?= $form->field($model, 'email')->textInput(['maxlength' => true]) ?>

<?= $form->field($model, 'authKey')->textInput(['maxlength' => true]) ?>

<div class="form-group">
    <?= Html::submitButton(Yii::t('app', 'Save'), ['class' => 'btn btn-success']) ?>
</div>

<?php ActiveForm::end(); ?>

<?php yii\widgets\Pjax::end() ?>

Simple Bootstrap themes

There is this page bootswatch.com which provides simple bootstrap themes. It is enough to replace one CSS file - you can do it in file "views/layouts/main.php" just by adding following row before < /head > tag:

<link href="https://bootswatch.com/3/united/bootstrap.min.css" rel="stylesheet">

</head>

Note that currently Yii2 is using Bootstrap3 so when searching for themes, dont forget to switch to section Bootstrap 3.

Important: Yii2 is using navbar with classes "navbar-inverse navbar-fixed-top". If you are using themes from Bootswatch, change the navbar class to "navbar navbar-default navbar-fixed-top" otherwise the top menu-bar will have weird color. This is also done in file "views/layouts/main.php" like this:

    NavBar::begin([
        // ...
        'options' => [
            'class' => 'navbar navbar-default navbar-fixed-top',
        ],
    ]);

Note: If you want to download the theme, you should link it like this:

<link href="<?=Yii::$app->getUrlManager()->getBaseUrl()?>/css/bootstrap-bootswatch-united.min.css" rel="stylesheet">

Now you technically do not need the original bootstrap.css file so you can remove it in "basic/config/web.php" by adding the assetManager section to "components":

'components' => [
  // https://stackoverflow.com/questions/26734385/yii2-disable-bootstrap-js-jquery-and-css
  'assetManager' => [
    'bundles' => [
	'yii\bootstrap\BootstrapAsset' => [
	  'css' => [],
	 ],
     ],
   ],

Yii2 + Composer

Once composer is installed, you might want to use it to download Yii, but following command might not work:

php composer.phar create-project yiisoft/yii2-app-basic basic

Change it to:

composer create-project yiisoft/yii2-app-basic basic

.. and run it. If you are in the desired folder right now, you can use . (dot) instead of the last "word":

composer create-project yiisoft/yii2-app-basic .

Using DatePicker

Run this command:

composer require --prefer-dist yiisoft/yii2-jui

and then use this code in your view:

<?= $form->field($model, 'date_deadline')->widget(\yii\jui\DatePicker::classname(), [
    //'language' => 'en',
    'dateFormat' => 'yyyy-MM-dd',
    'options' => ['class' => 'form-control']
]) ?>

Read more at the official documentation and on GIT

Favicon

Favicon is already included, but it nos used in the basic project. Just type this into views/layouts/main.php:

<link rel="icon" type="image/png" sizes="16x16" href="favicon.ico">

Or you can use the official yii-favicon:

<link rel="apple-touch-icon" sizes="180x180" href="https://www.yiiframework.com/favico/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://www.yiiframework.com/favico/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="https://www.yiiframework.com/favico/favicon-16x16.png">

GridView + DatePicker in filter + filter reset

If you are using DatePicker as described above, you can use it also in GridView as a filter, but it will not work properly. Current filter-value will not be visible and resetting the filter wont be possible. Use following in views/xxx/index.php to solve the issue:

function getDatepickerFilter($searchModel, $attribute) {
  $name = basename(get_class($searchModel)) . "[$attribute]";
  $result = \yii\jui\DatePicker::widget(['language' => 'en', 'dateFormat' => 'php:Y-m-d', 'name'=>$name, 'value'=>$searchModel->$attribute, 'options' => ['class' => 'form-control'] ]);
  if (trim($searchModel->$attribute)!=='') {
    $result = '<div style="display:flex;flex-direction:column">' . $result
    . '<div class="btn btn-danger btn-xs glyphicon glyphicon-remove" onclick="$(this).prev(\'input\').val(\'\').trigger(\'change\')"></div></div>';
  }	
  return $result;
}

// ...

<?= GridView::widget([
  'dataProvider' => $dataProvider,
  'filterModel' => $searchModel,
  'columns' => [
  // ...
  [
    'attribute' => 'myDateCol',
    'value' => 'myDateCol',
    'label'=>'My date label',
    'filter' => getDatepickerFilter($searchModel,'myDateCol'),
    'format' => 'html'
  ],
        
  // ...
        

Drop down list for foreign-key column

Do you need to specify for example currency using a predefined list, but your view contains only a simple text-input where you must manually enter currency_id from table Currency?

Read how to enhance it.

use yii\helpers\ArrayHelper;
use app\models\Currency; // My example uses Currency model

$currencies = Currency::find()->asArray()->all();

// 'id' = the primary key column
// 'name' = the column with text to be dispalyed to user
// https://www.yiiframework.com/doc/api/2.0/yii-helpers-basearrayhelper#map()-detail
$currencies = ArrayHelper::map($currencies, 'id', 'name'); 

<?= $form->field($model, 'id_currency')->dropDownList($currencies) ?>

Note: In other views you will need models with predefined relations to reach the correct value. Relations can be created using GII (when they are defined in DB) or manually.

GridViev - Variable page size

GridView cannot display DropDownList which could be used by the user to change the number of rows per page. You have to add it manually like this:

When you are creating a new model using Gii, you can select if you want to create the SearchModel as well. Do it, it is usefull for example in this situation. Then add following rows to the model:

// file models/InvoiceSearch.php

use yii\helpers\Html; // add this row

class InvoiceSearch extends Invoice
{
  public $pageSize = null // add this row
  // ...
  
  // This method already exists:
  public function rules()
  {
    return [ // ...
      ['pageSize', 'safe'], // add this row
      // ...
  
  // Add this function:
  public function getPageSizeDropDown($htmlOptions = [], $prefixHtml = '', $suffixHtml = '', $labelPrefix = '') {
    return $prefixHtml . Html::activeDropDownList($this, 'pageSize',
      [
        10 => $labelPrefix.'10', 
        20 => $labelPrefix.'20', 
        50 => $labelPrefix.'50', 
        100 => $labelPrefix.'100', 
        150 => $labelPrefix.'150', 
        200 => $labelPrefix.'200', 
        300 => $labelPrefix.'300', 
        500 => $labelPrefix.'500', 
        1000 => $labelPrefix.'1000'
      ],$htmlOptions ) . $suffixHtml;
    }

    // Add this function:
    public function getPageSizeDropDownID($prefix = '#') {
      return $prefix . Html::getInputId($this, 'pageSize');
    }
    
    // This method already exists:
    public function search($params)
    {
        // Remember to call load() first and then you can work with pageSize
        $this->load($params);
        
        // Add following rows:
        if (!isset($this->pageSize)) {
          // Here we make sure that the dropDownLst will have correct value preselected
          $this->pageSize = $dataProvider->pagination->defaultPageSize;
        } 
        $dataProvider->pagination->pageSize = (int)$this->pageSize; 
        

And then in your views/xxx/index.php use following:

$pageSizeDropDown = $searchModel->getPageSizeDropDown(['class' => 'form-control', 'style'=>'width: 20rem'],'','','Rows per page: ');

echo GridView::widget([
  'dataProvider' => $dataProvider,
  'filterModel' => $searchModel,
  'layout'=>'{summary}<br>{items}<br><div style="display:flex; background-color: #f9f9f9; padding: 0px 3rem;"><div style="flex-grow: 2;">{pager}</div><div style="align-self:center;">'.$pageSizeDropDown.'</div></div>',
  'pager' => [ 'maxButtonCount' => 20 ],
  
  'filterSelector' => $searchModel->getPageSizeDropDownID(),
  // filterSelector is the core solution of this problem. It refreshes the grid.
]]>
0
[wiki] Change default date format in Oracle Fri, 20 Sep 2019 06:15:43 +0000 https://www.yiiframework.com/wiki/2551/change-default-date-format-in-oracle https://www.yiiframework.com/wiki/2551/change-default-date-format-in-oracle lenovo7 lenovo7

Default date format in Oracle is DD-MON-RR (25-JAN-18). With that output, we can't using date formatting.

Too solve this issue, we must change date format oracle like date commonly using

ALTER SESSION SET NLS_DATE_FORMAT = ...

Add this script inside your database connection file

<?php

return [
    'class' => 'yii\db\Connection',
    'dsn' => 'oci:host=127.0.0.1:1521/XE',
    'username' => 'your_username',
    'password' => 'your_password',
    'charset' => 'utf8',

    // Schema cache options (for production environment)
    //'enableSchemaCache' => true,
    //'schemaCacheDuration' => 60,
    //'schemaCache' => 'cache',

    'on afterOpen' => function($event) {
        // $event->sender refers to the DB connection
        $event->sender->createCommand("ALTER SESSION SET NLS_DATE_FORMAT='DD-MM-YYYY hh24:mi:ss'")->execute();
    }    
];
]]>
0
[wiki] Move sources to src Tue, 27 Aug 2019 21:43:50 +0000 https://www.yiiframework.com/wiki/2550/move-sources-to-src https://www.yiiframework.com/wiki/2550/move-sources-to-src samdark samdark

Yii 3 and many Yii 2 package sources are contained within src directory which is convenient since you have less directories to check.

/config
/runtime
/src
  /assets
  /commands
  /controllers
  /mail
  /models
  /views
  /widgets
/tests
/vendor
/web
yii

Let's start with the basic applicaiton template.

  1. Create src directory.
  2. Move source directories there.
  3. Adjust config/web.php:
$config = [
    // ...
    'basePath' => dirname(__DIR__) . '/src',
    'runtimePath' => dirname(__DIR__) . '/runtime',
    'vendorPath' => dirname(__DIR__) . '/vendor',    
    // ...
];

And config/console.php:

$config = [
    // ...
    'basePath' => dirname(__DIR__) . '/src',
    'runtimePath' => dirname(__DIR__) . '/runtime',
    'vendorPath' => dirname(__DIR__) . '/vendor',
    // ...
];

That's it now you have both console and web application source code in src.

]]>
0
[wiki] Nested Set with Yii2 Mon, 01 Apr 2019 07:50:53 +0000 https://www.yiiframework.com/wiki/2549/nested-set-with-yii2 https://www.yiiframework.com/wiki/2549/nested-set-with-yii2 sangprabo sangprabo

The nested set behaviour is an approach to store hierarchical data in relational databases. For example, if we have many categories for our product or items. One category can be a "parent" for other categories, means that one category consists of more than one category. The model can be drawn using a "tree" model. There are other approaches available but what we will learn in this article is specifically the NestedSetsBehavior made by Alexander Kochetov, which utilizing the Modified Preorder Tree Traversal algorithm.

Requirements :

  • Yii2 framework advanced template
  • Yii2 nested sets package

Install the package using composer

It is always recommended to use Composer to install any kind of package or extension for our Yii2-powered project.

$ composer require creocoder/yii2-nested-sets

Create the table

In this article, we will use Category for our model/table name. So we would like to generate the table using our beloved migration tool.

$ ./yii migrate/create create_category_table

We need to modify the table so it contains our desired fields. We also generate three additional fields named position, created_at, and updated_at.

<?php

use yii\db\Migration;

/**
 * Handles the creation for table `category`.
 */
class m160611_114633_create_category extends Migration
{
    /**
     * @inheritdoc
     */
    public function up()
    {
        $this->createTable('category', [
            'id'         => $this->primaryKey(),
            'name'       => $this->string()->notNull(),
            'tree'       => $this->integer()->notNull(),
            'lft'        => $this->integer()->notNull(),
            'rgt'        => $this->integer()->notNull(),
            'depth'      => $this->integer()->notNull(),
            'position'   => $this->integer()->notNull()->defaultValue(0),
            'created_at' => $this->integer()->notNull(),
            'updated_at' => $this->integer()->notNull(),
        ]);
    }

    /**
     * @inheritdoc
     */
    public function down()
    {
        $this->dropTable('category');
    }
}

Then, generate the table using the migration tool.

$ ./yii migrate

If everything is okay, then you could see that a new table named category already exists.

Generate the default CRUD using Gii

To initiate a model, we need to use Gii tool from Yii2. Call the tool from your localhost:8080/gii/model, and fill in the Table Name field with our existing table: category. Fill other fields with appropriate values, and don't forget to give a check to "Generate ActiveQuery" checklist item. This will generate another file that needs to be modified later.

Continue to generate the CRUD for our model with CRUD Generator Tool. Fill in each field with our existing model. After all files are generated, you can see that we already have models, controllers, and views but our work is far from done because we need to modify each file.

Modify models, controllers, and views

The first file we should modify is the model file: Category.

<?php

namespace common\models;

use Yii;
use creocoder\nestedsets\NestedSetsBehavior;

/**
 * This is the model class for table "category".
 *
 * @property integer $id
 * @property string $name
 * @property integer $tree
 * @property integer $lft
 * @property integer $rgt
 * @property integer $depth
 * @property integer $position
 * @property integer $created_at
 * @property integer $updated_at
 */
class Category extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'category';
    }

    public function behaviors() {
        return [
            \yii\behaviors\TimeStampBehavior::className(),
            'tree' => [
                'class' => NestedSetsBehavior::className(),
                'treeAttribute' => 'tree',
                // 'leftAttribute' => 'lft',
                // 'rightAttribute' => 'rgt',
                // 'depthAttribute' => 'depth',
            ],
        ];
    }

    public function transactions()
    {
        return [
            self::SCENARIO_DEFAULT => self::OP_ALL,
        ];
    }

    public static function find()
    {
        return new CategoryQuery(get_called_class());
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['name'], 'required'],
            [['position'], 'default', 'value' => 0],
            [['tree', 'lft', 'rgt', 'depth', 'position', 'created_at', 'updated_at'], 'integer'],
            [['name'], 'string', 'max' => 255],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id'         => Yii::t('app', 'ID'),
            'name'       => Yii::t('app', 'Name'),
            'tree'       => Yii::t('app', 'Tree'),
            'lft'        => Yii::t('app', 'Lft'),
            'rgt'        => Yii::t('app', 'Rgt'),
            'depth'      => Yii::t('app', 'Depth'),
            'position'   => Yii::t('app', 'Position'),
            'created_at' => Yii::t('app', 'Created At'),
            'updated_at' => Yii::t('app', 'Updated At'),
        ];
    }

    /**
     * Get parent's ID
     * @return \yii\db\ActiveQuery 
     */
    public function getParentId()
    {
        $parent = $this->parent;
        return $parent ? $parent->id : null;
    }

    /**
     * Get parent's node
     * @return \yii\db\ActiveQuery 
     */
    public function getParent()
    {
        return $this->parents(1)->one();
    }

    /**
     * Get a full tree as a list, except the node and its children
     * @param  integer $node_id node's ID
     * @return array array of node
     */
    public static function getTree($node_id = 0)
    {
        // don't include children and the node
        $children = [];

        if ( ! empty($node_id))
            $children = array_merge(
                self::findOne($node_id)->children()->column(),
                [$node_id]
                );

        $rows = self::find()->
            select('id, name, depth')->
            where(['NOT IN', 'id', $children])->
            orderBy('tree, lft, position')->
            all();

        $return = [];
        foreach ($rows as $row)
            $return[$row->id] = str_repeat('-', $row->depth) . ' ' . $row->name;

        return $return;
    }
}

As you can see, we import the extension using the keyword use:

use creocoder\nestedsets\NestedSetsBehavior;

and I also add the TimeStampBehavior for our additional fields, created_at and updated_at

\yii\behaviors\TimeStampBehavior::className(),

Next : Our modification to CategoryController file is at update, create, and delete function.

<?php

namespace backend\controllers;

use Yii;
use common\models\Category;
use common\models\CategorySearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;

/**
 * CategoryController implements the CRUD actions for Category model.
 */
class CategoryController extends Controller
{
    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'delete' => ['POST'],
                ],
            ],
        ];
    }

    /**
     * Lists all Category models.
     * @return mixed
     */
    public function actionIndex()
    {
        $searchModel = new CategorySearch();
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);

        return $this->render('index', [
            'searchModel' => $searchModel,
            'dataProvider' => $dataProvider,
        ]);
    }

    /**
     * Displays a single Category model.
     * @param integer $id
     * @return mixed
     */
    public function actionView($id)
    {
        return $this->render('view', [
            'model' => $this->findModel($id),
        ]);
    }

    /**
     * Creates a new Category model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     * @return mixed
     */
    public function actionCreate()
    {
        $model = new Category();

        if ( ! empty(Yii::$app->request->post('Category'))) 
        {
            $post            = Yii::$app->request->post('Category');
            $model->name     = $post['name'];
            $model->position = $post['position'];
            $parent_id       = $post['parentId'];

            if (empty($parent_id))
                $model->makeRoot();
            else
            {
                $parent = Category::findOne($parent_id);
                $model->appendTo($parent);
            }

            return $this->redirect(['view', 'id' => $model->id]);
        }

        return $this->render('create', [
                'model' => $model,
            ]);
    }

    /**
     * Updates an existing Category model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param integer $id
     * @return mixed
     */
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);

        if ( ! empty(Yii::$app->request->post('Category'))) 
        {
            $post            = Yii::$app->request->post('Category');

            $model->name     = $post['name'];
            $model->position = $post['position'];
            $parent_id       = $post['parentId'];

            if ($model->save())            
            {
                if (empty($parent_id))
                {
                    if ( ! $model->isRoot())
                        $model->makeRoot();
                }
                else // move node to other root 
                {
                    if ($model->id != $parent_id)
                    {
                        $parent = Category::findOne($parent_id);
                        $model->appendTo($parent);
                    }
                }

                return $this->redirect(['view', 'id' => $model->id]);
            }
        }

        return $this->render('update', [
            'model' => $model,
        ]);
    }

    /**
     * Deletes an existing Category model.
     * If deletion is successful, the browser will be redirected to the 'index' page.
     * @param integer $id
     * @return mixed
     */
    public function actionDelete($id)
    {
        $model = $this->findModel($id);

        if ($model->isRoot())
            $model->deleteWithChildren();
        else 
            $model->delete();

        return $this->redirect(['index']);
    }

    /**
     * Finds the Category model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     * @param integer $id
     * @return Category the loaded model
     * @throws NotFoundHttpException if the model cannot be found
     */
    protected function findModel($id)
    {
        if (($model = Category::findOne($id)) !== null) {
            return $model;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }
}

As for our views files, we need to remove unnecessary fields from our form, such as the lft, rgt, etc, and add the sophisticated parent field to be a dropdown list. This requires a lot of effort, as you can see on the getTree function on our model.

<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;

use common\models\Category;

/* @var $this yii\web\View */
/* @var $model common\models\Category */
/* @var $form yii\widgets\ActiveForm */
?>

<div class="category-form">

    <?php $form = ActiveForm::begin(); ?>

    <?= $form->field($model, 'name')->textInput(['maxlength' => true]) ?>

    <div class='form-group field-attribute-parentId'>
    <?= Html::label('Parent', 'parent', ['class' => 'control-label']);?>
    <?= Html::dropdownList(
        'Category[parentId]',
        $model->parentId,
        Category::getTree($model->id),
        ['prompt' => 'No Parent (saved as root)', 'class' => 'form-control']
    );?>

    </div>

    <?= $form->field($model, 'position')->textInput(['type' => 'number']) ?>

    <div class="form-group">
        <?= Html::submitButton($model->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
    </div>

    <?php ActiveForm::end(); ?>

</div>

Complete files can be found at my GitHub page: https://github.com/prabowomurti/learn-nested-set

The screencast (subtitled English) here : https://www.youtube.com/watch?v=MjJEjF1arHs

]]>
0
[wiki] Events registration examples Mon, 11 Mar 2019 14:04:14 +0000 https://www.yiiframework.com/wiki/2548/events-registration-examples https://www.yiiframework.com/wiki/2548/events-registration-examples minitia82 minitia82

Register an event handler at Object-Level

e.g inside the init method of a Model

// this should be inside your model class. For example User.php
public function init(){
  $this->on(self::EVENT_NEW_USER, [$this, 'sendMail']);
  $this->on(self::EVENT_NEW_USER, [$this, 'notification']);
  // first parameter is the name of the event and second is the handler. 
  // For handlers I use methods sendMail and notification
  // from $this class.
  parent::init(); // DON'T Forget to call the parent method.
}

from https://stackoverflow.com/questions/28575636/how-to-use-events-in-yii2

Register an event handler at Class-Level

To register event handlers at Class-Level a good place can be inside the bootstrap process.

So, you can put the registration code as

Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
    Yii::debug(get_class($event->sender) . ' is inserted');
});

in a php file like the bootstrap.php used in Advanced-Template or through configuration, with your custom component for example

see docs

(https://www.yiiframework.com/doc/guide/2.0/en/structure-applications#bootstrap or https://www.yiiframework.com/doc/guide/2.0/en/structure-extensions#bootstrapping-classes)

]]>
0
[wiki] (draft) Understanding Yii 3 Sun, 16 Feb 2020 13:25:06 +0000 https://www.yiiframework.com/wiki/2547/draft-understanding-yii-3 https://www.yiiframework.com/wiki/2547/draft-understanding-yii-3 machour machour

Understanding Yii 3

  1. Introduction
  2. Changes overview
  3. Yii 3 composer packages
  4. Running your first Yii 3 powered application

Note: The information here is outdated. You can check yii-demo for current Yii 3 features.

Since this Wiki page is getting bigger and bigger, I decided to document things in a Yii 3 project instead.

Project source can be found at https://github.com/machour/yii3-kitchen-sink Live web site: https://yii3.idk.tn/

Follow the repo/website to get fresher informations, or better yet, pull the project and run it by yourself to get acquainted with Yii3

Introduction

This document is intended for an audience already familiar with Yii2. It's meant to bring together all information related to Yii 3 in one place to make it easier to get on track.

Yii 3 is the second major rewrite of the Yii framework.

Originally started in the 2.1 branch, it was later decided to switch to the 3.X series because of all the backward compatibility breakage. Starting with 3.0, Yii will follow the Sementic Versionning.

This rewrite addresses a lot of issues Yii 2 suffered from, like the framework being too coupled with jQuery, bower, bootstrap. [TODO: add more grieffs about Yii2]

Changes overview

Here are the main changes in Yii 3. You can check the complete CHANGELOG for an exhaustive list.

Source code splitting

The framework source code have been split into several packages, and at its core level, Yii no longer makes assumptions about your development stack, or the features you will be using.

This enable you to cherry pick the packages you need to compose your application.

This re-organisation is also a great news for maintainance, as these packages will be released separately, thus allowing more frequent updates.

Autoloading

The custom PHP class autoloader have been removed in favor of Composer's PSR-4 implementation. This means that in order for Yii to see your classes, you will have to explicitly register your namespace in composer.json. We will see an example later.

PSR compatibility

Yii 3 takes some positive steps following the PHP-FIG recommendations, by implementing the following PSRs:

  • Logging is now compliant with PSR-3
  • Caching is now compliant with PSR-16
  • Dependency Injection is now compliant with PSR-11
Application configuration

If you've ever installed an extension using Yii 2, you may/certainly have found yourself on the extension README file, looking for the chunk of configuration to copy/paste in your own config/main.php file.

This can often lead to:

  • a huge configuration file (which you may have decided to split into smaller files)
  • non-trivials configurations update when a new version of the extension is realeased with new/changed configurations options.

Yii 3 takes another approach. Every package bundle its own configuration, and will probably work out of the box. And you may override them, if you need to, from your configuration file.

This is all done by leveraging the hiqdev/composer-config-plugin composer plugin, which takes care of scanning & merging all the configurations when you run composer dump-autoload (also know as composer du).

You can read Yii2 projects alternative organization for an in-depth explanation of the motivation behind hiqdev/composer-config-plugin.

Packages authors will have the responsibility to avoid introducing BC breaks, by adopting a strict sementical versionning.

Dependencies injection

[TODO]

Yii 3 composer packages

Here are the new packages introduced in Yii 3, which can be found in this official list.

Let's introduce them briefly:

The Framework

This is the new kernel of Yii. It defines the base framework and its core features like behaviors, i18n, mail, validation..

You will rarely want to directly install yiisoft/yii-core. Instead, you will install one or more of the following:

This three packages, considered as Extensions, are responsible for implementing the basic functionnalities of each "channel" they refer to:

  • yii-console implements all that you need to build a console application (the base Controller for commands, the Command helper, ..)
  • yii-web implements all that you need to build a web application (Assets management, Sessions, Request handling ..)
  • yii-rest implements all that you need to build a REST interface (ActiveController, ..)
Librairies

In Yii 3, libraries do not depend on Yii and are meant to be usable outside the framework. Their package name is yiisoft/something without yii-prefix.

Drivers for yiisoft/db

The various drivers for DB have also been separated into packages:

Extensions

Extensions depends (at least) on yii-core. Aside from the 3 extensions already encountered above (yii-console, yii-web, yii-api), these packages are available

Development
View rendering engines
Data rendering
JS & CSS Frameworks integration
Widgets
Misc
Yii project template and application bases

This is a very basic Yii project template, that you can use to start your development.

You will probably want to pick one or more of these three starters to install in your project next:

Let's try running the web base template in the next section.

Running your first Yii 3 powered application

Let's try running a web application using Yii 3, and the provided project template.

Installing the project template
composer create-project --prefer-dist --stability=dev yiisoft/yii-project-template myapp
cd myapp

Here's the created structure:

.
├── LICENSE
├── README.md
├── composer.json
├── composer.lock
├── config
│   ├── common.php
│   └── params.php
├── docker-compose.yml
├── hidev.yml
├── public
│   ├── assets
│   ├── favicon.ico
│   ├── index.php
│   └── robots.txt
├── runtime
└── vendor

You won't be able to start the web server right away using ./vendor/bin/yii serve, as it will complain about not knowing the "app" class.

In fact, this project template only introduce the bare minimum in your application: Caching, Dependencies injection, and Logging. The template doesn't make an assumption about the kind of application you're building (web, cli, api).

You could start from scratch using this bare template, select the extensions & packages you want to use and start developing, or you can pick one of the three starters provided.

Installing the web starter

Since we're doing a web application, we will need an asset manager. We can pick either one of those:

  • Asset-packagist & composer-merge-plugin (requires only PHP)
  • Foxy (requires npm or yarn)

Let's go with foxy (personal taste since composer is so slow from Tunisia):

composer require "foxy/foxy:^1.0.0"

We can now install the yii-base-web starter and run our application:

composer require yiisoft/yii-base-web
vendor/bin/yii serve

By visiting http://localhost:8080/, you should now see something like this:

50153967-44a6af00-02c8-11e9-9914-ceb463065cdf.png

Checking back our project structure, nothing really changed, aside from the creation of these three entries:

  • node_modules/
  • package-lock.json
  • package.json

So where do what we see in the browser comes from ?

Exploring yiisoft/yii-base-web structure:

If you explore the folder in vendor/yiisoft/yii-base-web, you will see that the template is in fact a project itself, with this structure:

.
├── LICENSE.md
├── README.md
├── composer.json
├── config
│   ├── common.php
│   ├── console.php
│   ├── env.php
│   ├── messages.php
│   ├── params.php
│   └── web.php
├── phpunit.xml.dist
├── public
│   └── css
│       └── site.css
├── requirements.php
├── runtime
└── src
    ├── assets
    │   └── AppAsset.php
    ├── commands
    │   └── HelloController.php
    ├── controllers
    │   └── SiteController.php
    ├── forms
    │   ├── ContactForm.php
    │   └── LoginForm.php
    ├── mail
    │   └── layouts
    ├── messages
    │   ├── et
    │   ├── pt-BR
    │   ├── ru
    │   └── uk
    ├── models
    │   └── User.php
    ├── views
    │   ├── layouts
    │   └── site
    └── widgets
        └── Alert.php

The folders and files should make sense to you if you already developed applications using Yii2 and the basic template.

]]>
0
[wiki] Batch Gridview data ajax send splitted in chunks displaying bootstrap Progress bar Sat, 24 Nov 2018 10:48:47 +0000 https://www.yiiframework.com/wiki/2546/batch-gridview-data-ajax-send-splitted-in-chunks-displaying-bootstrap-progress-bar https://www.yiiframework.com/wiki/2546/batch-gridview-data-ajax-send-splitted-in-chunks-displaying-bootstrap-progress-bar toaster toaster

The scenario in which this wiki can be useful is when you have to send an (huge) array of model ids and perform a time consuming computation with it like linking every model to other models. The idea is to split the array into smaller arrays and perform sequential ajax requests, showing the calculation progress using a Bootstrap Progress bar.

I have created a Country model and generated the CRUD operations using Gii. The model class look like this:

namespace app\models;
use Yii;
use yii\helpers\ArrayHelper;

/**
 * This is the model class for table "country".
 *
 * @property string $code
 * @property string $name
 * @property int $population
 *
 */
class Country extends \yii\db\ActiveRecord {

    /**
     * {@inheritdoc}
     */
    public static function tableName() {
        return 'country';
    }

    /**
     * {@inheritdoc}
     */
    public function rules() {
        return [
            [['code', 'name'], 'required'],
            [['population'], 'integer'],
            [['code'], 'string', 'max' => 3],
            [['name'], 'string', 'max' => 52],
            [['code'], 'unique'],
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels() {
        return [
            'code' => 'Code',
            'name' => 'Name',
            'population' => 'Population',
        ];
    }
    /**
     * 
     * @return array
     */
    public static function getCodes(){
        return array_keys(ArrayHelper::map(Country::find()->select('code')->asArray()->all(), 'code', 'code'));
    }
}

I have also created another model that will take care to validate the ids and perform the time consuming calculation through the process() function. (in this example I just use a sleep(1) that will wait for one second for each element of the $codes array). I also want to keep track of the processed models, incrementing the $processed class attribute.

<?php

namespace app\models;

use app\models\Country;

/**
 * Description of BatchProcessForm
 *
 * @author toaster
 */
class BatchProcessForm extends \yii\base\Model {

    /**
     * The codes to process
     * @var string
     */
    public $codes;

    /**
     * The number of codes processed
     * @var integer
     */
    public $processed = 0;

    /**
     * @return array the validation rules.
     */
    public function rules() {
        return [
            ['codes', 'required'],
            ['codes', 'validateCodes'],
        ];
    }

    /**
     * Check whether codes exists in the database
     * @param type $attribute
     * @param type $params
     * @param type $validator
     */
    public function validateCodes($attribute, $params, $validator) {
        $input_codes = json_decode($this->$attribute);
        if (null == $input_codes || !is_array($input_codes)) {
            $this->addError($attribute, 'Wrong data format');
            return;
        }
        $codes = Country::getCodes();
        if (!array_intersect($input_codes, $codes) == $input_codes) {
            $this->addError($attribute, 'Some codes selected are not recognized');
        }
    }

    /**
     * Process the codes
     * @return boolean true if everything goes well
     */
    public function process() {
        if ($this->validate()) {
            $input_codes = json_decode($this->codes);
            foreach ($input_codes as $code) {
                sleep(1);
                $this->processed++;
            }
            return true;
        }
        return false;
    }

}

The code for the controller action is the following:

/**
 * Process an array of codes
 * @return mixed
 * @throws BadRequestHttpException if the request is not ajax
 */
public function actionProcess(){
    if(Yii::$app->request->isAjax){
        Yii::$app->response->format = Response::FORMAT_JSON;
        $model = new BatchProcessForm();
        if($model->load(Yii::$app->request->post()) && $model->process()){
            return ['result' => true, 'processed' => $model->processed];
        }
        return ['result' => false, 'error' => $model->getFirstError('codes')];
    }
    throw new \yii\web\BadRequestHttpException;
}

In my index.php view I have added a CheckboxColumn as first column of the Gridview that allows, out of the box, to collect the ids of the models selected via Javascript (in this case the values of the code attribute). I have also added a button-like hyperlink tag to submit the collected data (with id batch_process) and a Bootstrap Progress bar inside a Modal Window. The code of my view is the following:

<?php

use yii\helpers\Html;
use yii\grid\GridView;
use yii\bootstrap\Modal;

/* @var $this yii\web\View */
/* @var $searchModel app\models\CountrySearch */
/* @var $dataProvider yii\data\ActiveDataProvider */

$this->title = 'Countries';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="country-index">

    <h1><?= Html::encode($this->title) ?></h1>
    <?php // echo $this->render('_search', ['model' => $searchModel]); ?>

    <p>
        <?= Html::a('Create Country', ['create'], ['class' => 'btn btn-success']) ?>
        <?= Html::a('Batch Process', ['process'], ['class' => 'btn btn-info', 'id' => 'batch_process']) ?>
    </p>

    <?=
    GridView::widget([
        'dataProvider' => $dataProvider,
        'filterModel' => $searchModel,
        'options' => [
            'id' => 'country-index'
        ],
        'columns' => [
            ['class' => 'yii\grid\CheckboxColumn'],
            'code',
            'name',
            'population',
            ['class' => 'yii\grid\ActionColumn'],
        ],
    ]);
    ?>
</div>

<?php Modal::begin(['header' => '<h5 id="progress">0% Complete</h5>', 'id' => 'progress-modal', 'closeButton' => false]); ?>

<?=
yii\bootstrap\Progress::widget([
    'percent' => 0,
     'options' => ['class' => 'progress-success active progress-striped']
]);
?>

<?php Modal::end(); ?>

<?php $this->registerJsFile('@web/js/main.js', ['depends' => [\app\assets\AppAsset::class]]); ?>

The Javascript file that split in chunks the array and send each chunk sequentially is main.js. Although I have registered the Javascript file using registerJsFile() method I highly recommend to better handle and organize js files using Asset Bundles. Here is the code:


function updateProgressBar(percentage) {
    var perc_string = percentage + '% Complete';
    $('.progress-bar').attr('aria-valuenow', percentage);
    $('.progress-bar').css('width', percentage + '%');
    $('#pb-small').text(perc_string);
    $('#progress').text(perc_string);
}

function disposeProgressBar(message) {
    alert(message);
    $('#progress-modal').modal('hide');
}

function showProgressBar() {
    var perc = 0;
    var perc_string = perc + '% Complete';
    $('.progress-bar').attr('aria-valuenow', perc);
    $('.progress-bar').css('width', perc + '%');
    $('#pb-small').text(perc_string);
    $('#progress').text(perc_string);
    $('#progress-modal').modal('show');
}

function batchSend(set, iter, take, processed) {
    var group = iter + take < set.length ? iter + take : set.length;
    var progress = Math.round((group / set.length) * 100);
    var dataObj = [];
    for (var i = iter; i < group; i++) {
        dataObj.push(set[i]);
    }
    iter += take;
    $.ajax({
        url: '/country/process', ///?r=country/process
        type: 'post',
        data: {'BatchProcessForm[codes]': JSON.stringify(dataObj)},
        success: function (data) {
            if (data.result) {
                updateProgressBar(progress);
                processed += data.processed;
                if (progress < 100) {
                    batchSend(set, iter, take, processed);
                } else {
                    var plural = processed == 1 ? 'country' : 'countries';
                    disposeProgressBar(processed + ' ' + plural + ' correctly processed');
                }
                return true;
            }
            disposeProgressBar(data.error);
            return false;
        },
        error: function () {
            disposeProgressBar('Server error, please try again later');
            return false;
        }
    });
}

$('#batch_process').on('click', function (e) {
    e.preventDefault();
    var keys = $('#country-index').yiiGridView('getSelectedRows');
    if (keys.length <= 0) {
        alert('Select at least one country');
        return false;
    }
    var plural = keys.length == 1 ? 'country' : 'countries';
    if (confirm(keys.length + ' ' + plural + ' selected.. continue?')) {
        showProgressBar();
        batchSend(keys, 0, 5, 0);
    }
});

The first three functions take care to show, update and hide the progress bar inside the modal window, while the batchSend function perform the array split and, using recursion, send the data through post ajax request, encoding the array as JSON string and calling itself until there is no more chunks to send. The last lines of code bind the click event of the #batch_process button checking if at least one row of Gridview has been selected. The number of elements on each array chunk can be set as the third argument of the batchSend function (in my case 5), you can adjust it accordingly. The first parameter is the whole array obtained by the yiiGridView('getSelectedRows') function. The final result is something like this:

batch_send1.gif

...looks cool, isn't it?

]]>
0
[wiki] Using multiple models in an identity Tue, 29 Jan 2019 23:11:01 +0000 https://www.yiiframework.com/wiki/2545/using-multiple-models-in-an-identity https://www.yiiframework.com/wiki/2545/using-multiple-models-in-an-identity samdark samdark

Let's assume we have two models: Customer and Supplier and we want both to log in. Yii is quite flexible when it comes to authentication and authorization so it's possible.

First of all, Yii assumes that there's a single type of user component used at once. Still, we're able to create a universal Identity that works with both customers and suppliers.

So we create models/Identity.php:

final class Identity implements IdentityInterface
{
    const TYPE_CUSTOMER = 'customer';
    const TYPE_SUPPLIER = 'supplier';

    const ALLOWED_TYPES = [self::TYPE_CUSTOMER, self::TYPE_SUPPLIER];

    private $_id;
    private $_authkey;
    private $_passwordHash;

    public static function findIdentity($id)
    {
        $parts = explode('-', $id);
        if (\count($parts) !== 2) {
            throw new InvalidCallException('id should be in form of Type-number');
        }
        [$type, $number] = $parts;

        if (!\in_array($type, self::ALLOWED_TYPES, true)) {
            throw new InvalidCallException('Unsupported identity type');
        }

        $model = null;
        switch ($type) {
            case self::TYPE_CUSTOMER:
                $model = Customer::find()->where(['id' => $number])->one();
                break;
            case self::TYPE_SUPPLIER:
                $model = Supplier::find()->where(['id' => $number])->one();
                break;
        }

        if ($model === null) {
            return false;
        }


        $identity = new Identity();
        $identity->_id = $id;
        $identity->_authkey = $model->authkey;
        $identity->_passwordHash = $model->password_hash;
        return $identity;
    }

    public static function findIdentityByAccessToken($token, $type = null)
    {
        $model = Customer::find()->where(['token' => $token])->one();
        if (!$model) {
            $model = Supplier::find()->where(['token' => $token])->one();
        }

        if (!$model) {
            return false;
        }

        if ($model instanceof Customer) {
            $type = self::TYPE_CUSTOMER;
        } else {
            $type = self::TYPE_SUPPLIER;
        }

        $identity = new Identity();
        $identity->_id = $type . '-' . $model->id;
        $identity->_authkey = $model->authkey;
        $identity->_passwordHash = $model->password_hash;
        return $identity;
    }

    public function validatePassword($password)
    {
        return password_verify($password, $this->_passwordHash);
    }

    public static function findIdentityByEmail($email)
    {
        $model = Customer::find()->where(['email' => $email])->one();
        if (!$model) {
            $model = Supplier::find()->where(['email' => $email])->one();
        }

        if (!$model) {
            return false;
        }

        if ($model instanceof Customer) {
            $type = self::TYPE_CUSTOMER;
        } else {
            $type = self::TYPE_SUPPLIER;
        }

        $identity = new Identity();
        $identity->_id = $type . '-' . $model->id;
        $identity->_authkey = $model->authkey;
        $identity->_passwordHash = $model->password_hash;
        return $identity;
    }

    public function getId()
    {
        return $this->_id;
    }

    public function getAuthKey()
    {
        return $this->_authkey;
    }

    public function validateAuthKey($authKey)
    {
        return $this->getAuthKey() === $authKey;
    }
}

In the above we assume that our ids are like customer-23 or supplier-34. When we need to get identity instance we split the id by - and getting both type and integer id for the model corresponding to that type.

Having identity we can tell Yii to use it via config/main.php:

[
    // ...
    'components' => [
        // ...
        'user' => [
            'identityClass' => 'app\models\Identity',
            'enableAutoLogin' => true,
        ],
    ],
],

The only thing left is to adjust models\LoginForm.php:

class LoginForm extends Model
{
    // ...
    
    public function getUser()
    {
        if ($this->_user === false) {
            $this->_user = Identity::findIdentityByEmail($this->username);
        }

        return $this->_user;
    }
}

That's it. Now you can log in using both models.

]]>
0
[wiki] Update and Delete buttons on Breadcrumb Mon, 06 Jul 2020 08:39:21 +0000 https://www.yiiframework.com/wiki/2544/update-and-delete-buttons-on-breadcrumb https://www.yiiframework.com/wiki/2544/update-and-delete-buttons-on-breadcrumb adinugro adinugro

The definition of breadcrumbs according to its documentation is as follow: Breadcrumbs displays a list of links indicating the position of the current page in the whole site hierarchy.

We can define the breadcrumbs easily by adding these lines.

$this->title = $model->formNo;
$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Supplier receiving'), 'url' => ['index', 'type' => $model->type]];
$this->params['breadcrumbs'][] = ['label' => $this->title, 'url' => ['view', 'id' => $model->id]];

Reading the documentation, I encountered the template keyword. I was excited about the possibility to add buttons into breadcrumbs. Add these lines and you would have buttons on the right side of the breadcrumb.

$this->title = $model->formNo;
$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Supplier receiving'), 'url' => ['index', 'type' => $model->type]];
$this->params['breadcrumbs'][] = ['label' => $this->title, 'url' => ['view', 'id' => $model->id]];
$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Delete'), 'template' =>
    Html::tag('span', Html::a(Html::icon('glyphicon glyphicon-trash white') . ' ' . Yii::t('app', 'Delete'), Url::to(['delete', 'id' => $model->id]), [
                'class' => 'btn btn-xs btn-danger',
                'title' => Yii::t('app', 'Delete'),
                'data-pjax' => '0',
                'data-method' => 'POST',
                'data-confirm' => Yii::t('app', 'Are you sure you want to delete this supplier receiving?'),
                    ]
            ), ['class' => 'pull-right'])];
$this->params['breadcrumbs'][] = ['label' => Yii::t('app', 'Update'), 'template' => Html::tag('span', Html::a(
                    Html::icon('glyphicon glyphicon-pencil') . ' ' . Yii::t('app', 'Update'), Url::to(['update', 'id' => $model->id, 'inview' => 1]), [
                'class' => 'btn btn-xs btn-warning',
                'title' => Yii::t('app', 'Update'),
                'role' => 'modal-remote',
                'data-toggle' => 'tooltip',
                    ]
            ), ['class' => 'pull-right', 'style' => 'margin-right: 5px;'])];

Actually you can use this line to define the template, but $links means the full link. A suggestion to the developer is to make the $url and $label variables to be available like $links, so that we can make the template more flexible and meaningful than above codes.

FYI,

  1. I use ajaxcrud extension, so that the update link has 'role' => 'modal-remote' to allow the update process taken place on the modal window. I added a inview parameter to distinguish the update from index or view. Both actions will be called using ajax request and will show the form on the modal, but the index update will redirect to index and refresh gridview while in the view, it should redirect to the view.
  2. On the template, I used span over li to make ' / ' character not appear on the links buttons.

A simple tip to make the view layout efficient.

]]>
0
[wiki] When to use Active Record Sat, 23 May 2020 07:32:04 +0000 https://www.yiiframework.com/wiki/2541/when-to-use-active-record https://www.yiiframework.com/wiki/2541/when-to-use-active-record samdark samdark

When to use Active Record is a common question among developers, Yii and overall.

I have about 500K records in my DB tables and each query there are 2 or 3 joins. I was getting data via AR, about a hundred records a time and noticed that using createCommand consumes less memory and CPU. I think with DAO instead of AR it will be faster.

It is true that Active Record consumes memory for storing objects and CPU cycles for instantiate these objects.

Is AR bad? Is it for small projects only? We have lots of inserts, about 5000 records an hour and we're using AR for that. Should we re-write?

AR is a pleasure to use when you're managing not that many records at the same time. CRUD is very easy with it. Yii 2 dirty attribute support (only what was modified is saved) off-loads database and mitigates many parallel editing conflicts. If you don't have complex logic in your app and you don't need entity abstractions, AR is an ideal choice.

AR is OK for queries not too complicated when they return no more than a hundred objects. Yes, it is faster and less memory consuming to use query builder or asArray() but working with arrays is significantly less convenient.

For complex queries, such as aggregates or reports, AR isn't recommended. These are easier to express with query builder or SQL. Same goes for import and export routines.

]]>
0
[wiki] Getting information from the current locale Thu, 09 Aug 2018 18:56:33 +0000 https://www.yiiframework.com/wiki/2540/getting-information-from-the-current-locale https://www.yiiframework.com/wiki/2540/getting-information-from-the-current-locale CeBe CeBe

Yii 2.0 comes with a formatter component to format dates, numbers, and other values for international users according to their locale. This is very useful for displaying data. When working with incoming data or when using enhanced input methods like the MaskedInput widget you sometimes need to access the symbols used by the formatter to generate a pattern or parse the input.

The Yii formatter does not have methods to access this data, it relies on the PHP intl extension for formatting. If you want to access the formatting symbols you need to work with intl directly.

Getting decimal and thousand separator

$locale = 'de_DE';

$formatter = new \NumberFormatter($locale,\NumberFormatter::DECIMAL);

$decimalSeparator = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
$thousandSeparator = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);

There are more symbols than shown above, for the full list of symbols see the list of NumberFormatter constants in the php manual.

Getting a currency symbol

Getting the currency symbol is a bit more complicated if you do not want the symbol of the default currency of a locale, but another currency:

$locale = 'de_DE'; // get the currency symbol of Germanys default currency EUR = "€"
$locale = 'de_DE@currency=USD'; // get the currency symbol of USD in German locale = "$"

$formatter = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);

$currencySymbol = $formatter->getSymbol(\NumberFormatter::CURRENCY_SYMBOL);

Note, that the NumberFormatter is instantiated with the CURRENCY constant instead of DECIMAL here.

Since Yii 2.0.14 Yii has this method built-in in the Locale class.

]]>
0
[wiki] What SQL-Statement creates yii? Fri, 27 Jul 2018 11:28:13 +0000 https://www.yiiframework.com/wiki/2539/what-sql-statement-creates-yii https://www.yiiframework.com/wiki/2539/what-sql-statement-creates-yii Necip Necip

The usual way to find out what Yii has created for an SQL query is to mutilate the SQL in the sourcecode and call the program again so that the SQL statement with errors is displayed. Or you can use the SQL logger, which must be switched on and off each time and logs all SQL statements, which leads to an enormous slowdown in program execution and decelerates your workflow.

These all is not necessary if you use XDebug and place a breakpoint after the function "getCommandBuilder" in the CActiveRecord.php and take a closer look at the return value in $command.

This database access is created in yii:

yii-sql11.png?w=474

This code is called by the yii framework

yii-sql21.png?w=474

getCommandBuilder returns this data structure with the generated SQL statement:

yii-sql32.png?w=474

Path to CActiveRecord.php

C:\xampp\htdocs\your_project\yii\framework\db\ar\CActiveRecord.php

]]>
0
[wiki] How to organize Design "things" in Yii 2 (themes and layout) Thu, 23 Aug 2018 17:32:30 +0000 https://www.yiiframework.com/wiki/2538/how-to-organize-design-things-in-yii-2-themes-and-layout https://www.yiiframework.com/wiki/2538/how-to-organize-design-things-in-yii-2-themes-and-layout olissongs olissongs
  1. Via "theming"
  2. Using layouts
  3. Both
  4. UseCase one
  5. UseCase two

Sometimes the App needs a nicer look & feel, so its necessary to organize the assets for this and Yii can help a lot to make it easy. In this article I provide tips for handling multiple "Designs". I use these three features:

  • AssetBundle
  • Layouts
  • Themes

What do you need for desingning your website:

  • CSS files
  • JS files
  • some Images or other media.

Lets call it "DesignAssets"

What Yii needs:

  • one or more Layout files
  • the view files
  • AssetBundle set in the layout-file

Lets call it "DesignTemplates".

So how to bundle these and where is the best place to put them in your application directory?

Via "theming"

  • myYiiApp
    • ...
    • designs
      • myDesign1
        • put here all your "DesignTemplates" (AssetBundle,layout, and view folder/files, when using themes)

Using layouts

  • myYiiApp
    • ...
    • views
      • layouts
        • myDesign1.php (layout file)
      • ...

Both

  • myYiiApp

    • ...
    • web

      • designs
        • myDesign1
          • put here all your "DesignAssets" (css, js, img, etc.)
    • ...

So you can work with the default security file-rights for the correct purpose.

UseCase one

Write an App and distribute it to some Customer. Here can it be necessary that every customer wants a personal design.

Solution: Using themes

you config in your web.php or main.php (advanced template) whats documented here

if you need special "DesignAssets"

put your "DesignAssets" und the web directory myYiiApp/web/designs/myDesign1/css and js and img folders

customize your myYiiApp/designs/myDesign1/assets/MyDesignAsset.php

and your layout file myYiiApp/designs/myDesign1/layout/main.php

thats it.

UseCase two

you need several designs for e.g. controllers. so my remmmendation is using layouts, because easy switching in controller action.

Solution with layouts

there is no need to configure anything in the config files

if you need special "DesignAssets"

put your "DesignAssets" und the web directory myYiiApp/web/designs/myDesign1/css and js and img folders

create and customize your myYiiApp/assets/MyDesignAsset.php

and your layout file myYiiApp/views/layout/mydesign1.php

in your controller action set your layout.

public function actionIndex() {
    $this->layout = 'mydesign1';
}

may it helps your start with designing Yii.

]]>
0
[wiki] An alternative way to ElasticSearch Sun, 12 Aug 2018 07:49:50 +0000 https://www.yiiframework.com/wiki/2255/an-alternative-way-to-elasticsearch https://www.yiiframework.com/wiki/2255/an-alternative-way-to-elasticsearch Necip Necip

This article is for those who have dealt with the complexity of Elasticsearch or any other indexing machines and are looking for an easier way to index the existing database without additional effort.

Why this post?

The amount of data increases every day. As a result, the search in the databases becomes longer and longer. Conventional data structures must be realigned in order to be able to access information more quickly. There are already database systems like Elasticsearch that can do this. However, such systems also have disadvantages.

The most noticeable major drawbacks are:

  • Learning a new query language. SQL-Plugin won’t get you far or is not flexible enough.
  • The existing programs must be rewritten in order to process the new result sets appropriately.
  • The safety regulations must be defined again.
  • A second database must be set up, which in principle contains the same data.

Who will benefit from this post?

This information is useful for any programmer who wants to integrate an index database into his existing system in a simple way without additional effort.

The basic idea behind Indexing-machines

We will use this simple Table to demonstrate what an Index-machine does

Tablename: object

UID	TITLE	DESCRIPTION
4711	Rudyard Kipling	If you can keep your head when all about you …
4712	John Magee	I have slipped the surly bonds of Earth and danced the skies on laugher-silvered wings …
4713	Wiliam Wordsworth	Ten thousand saw I at a glance, Tossing their heads in sprightly dance…

With this request we can find very specific texts in this single table:

SELECT ID, Title, Description
FROM object
WHERE Description like '%head%'

But what if we want to find ‚%head%‘ in all tables of our database? We have to write a code to do this job for us. This is inefficent and will work very slowy. The idea behind Elasticsearch and other indexing tables is – so far I understood – to break the strings in single tokens. That means in a very easy way that we have to transform the horicontal order of the table into a vertical order.

Tablename: ncp_index

UID	TABLENAME	FIELDNAME	ID	TOKEN
1001	object	Description	4711	if
1002	object	Description	4711	you
1003	object	Description	4711	can
…				
1010	object	Description	4712	I
1011	object	Description	4712	have
1012	object	Description	4712	slipped
…				

We can tokenize any field of any table of our database into the table ncp_index. Now we can find with a single query very fast any (tokenized) word in our hole database.

SELECT Tablenname, Fieldname, Token
FROM ncp_index
WHERE Token like '%head%'

That is the secret of any Index-Searchengine. Yes, the ncp_index table has a lot of redundant data that we can normalize as follows:

Every field is stored in a system table and has a unique id. let us call it field_id Every content of a field has a lot of same words. These words should be stored only once in a separat words-table.

Our ncp_index table looks now so:

UID	FIELD_ID	ID	TOKEN_ID
1001	123	4711	1
1002	123	4711	2
1003	123	4711	31010	123	4712	4
1011	123	4712	5
1012	123	4712	6
…	
		
Systemtable: fields

UID	TABLENAME	NAME
122	object	Name
123	object	Description
…		

Tablename: word

UID	TOKEN
1	if
2	you
3	can
…	

Some basic examples

/**
 * @author ncp <necips@live.de>
 */
class NCPSearch
{
    /**
     * @param $model
     * @param $tablename
     * @param $fieldnames
     */
    public static function delete_ncp_search_item($model, $tablename) {
        $criteria = new CDbCriteria;
        $criteria->condition = "tablename = :tablename " .
                    "AND id = :id ";
        $criteria->params[":tablename"] = $tablename;
        $criteria->params[":id"] = $model->uid;
        NCPIndexModel::model()->deleteAll($criteria);
    }
    /**
     * @param $model
     * @param $tablename
     * @param $fieldnames
     */
    public static function update_ncp_search_item($model, $tablename, $fieldnames) {
        NCPSearch::delete_ncp_search_item($model, $tablename);
        NCPSearch::insert_ncp_search_item($model, $tablename, $fieldnames);
    }
    /**
     * @param $model
     * @param $tablename
     * @param $fieldnames
     */
    public static function insert_ncp_search_item($model, $tablename, $fieldnames) {
        foreach ($fieldnames as $fieldname) {
            $NCP_index_model = new NCPIndexModel("create");
            $NCP_index_model->tablename = $tablename;
            $NCP_index_model->fieldname = $fieldname;
            $NCP_index_model->id = $model->uid;
            $NCP_index_model->save();
            // a very simple way to tokenize the strings!
            $raw = strip_tags($model->{$fieldname});
            $tokens = explode( ' ', $raw);
            foreach ($tokens as $token) {
                $NCP_token_model = new NCPTokenModel("create");
                $NCP_token_model->NCP_index_uid = $NCP_index_model->uid;
                $NCP_token_model->token = $token;
                $NCP_token_model->save();
            }
        }
    }
    /**
     * @param $models
     * @param $tablename
     * @param $fieldnames
     */
    public static function insert_ncp_search_items($models, $tablename, $fieldnames) {
        foreach ($models as $model) {
            NCPSearch::insert_ncp_search_item($model, $tablename, $fieldnames);
        }
    }
}


// main.php:

// initialize ncp_search table once with all tables which has to be indexed in the main function
NCPSearch::insert_ncp_search_items(UserModel::model()->findAll(), "user", ["login", "mail", "name_last", "name_first"]);
NCPSearch::insert_ncp_search_items(DepartmentModel::model()->findAll(), "department", ["title", "description"]);
NCPSearch::insert_ncp_search_items(ObjectModel::model()->findAll(), "object", ["title", "description"]);
  

// model.php:

class Object : Model
{
  function afterSave()  {
    
    
     // insert this code to synchronize the informations on ncp_index
     if ($this->status === ObjectStatus::DELETED)
            NCPSearch::delete_ncp_search_item($this, "object");
        else
            NCPSearch::update_ncp_search_item($this, "object", ["title", "description"]);       
            
    ...
  }
  
  ...
  
}

Conclusion

These are my basic observations on this subject. These are the first steps to a search-engine that can index existing tables so that informations can be found quickly.

Thanks to

Github

https://github.com/Necip8/ncp_search

Homepage https://ncpup.wordpress.com

I hope this information helps you to build your own super search engine!

]]>
0
[wiki] Pjax GridView: refresh page after delete Tue, 24 Jul 2018 14:11:51 +0000 https://www.yiiframework.com/wiki/867/pjax-gridview-refresh-page-after-delete https://www.yiiframework.com/wiki/867/pjax-gridview-refresh-page-after-delete hehbhehb hehbhehb

Normally, after clicking the delete button in gridview, the record will be deleted and the page will refresh, but the page number in query string is lost. This is not always the case we expect.

How to refresh current page with pjax after deleting the record? It seems there is no very simple solution.

  1. Controller file

     public function actionDelete($id)
     {
         $this->findModel($id)->delete();
         if (Yii::$app->request->isAjax) {
             Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
             return ['success' => true];
         }
         return $this->redirect(['index']);
     }
    
  2. index.php (view file)

    <?php
     $this->registerJs("
         $(document).on('ready pjax:success', function() {
             $('.pjax-delete-link').on('click', function(e) {
                 e.preventDefault();
                 var deleteUrl = $(this).attr('delete-url');
                 var pjaxContainer = $(this).attr('pjax-container');
                 var result = confirm('Delete this item, are you sure?');                                
                 if(result) {
                     $.ajax({
                         url: deleteUrl,
                         type: 'post',
                         error: function(xhr, status, error) {
                             alert('There was an error with your request.' + xhr.responseText);
                         }
                     }).done(function(data) {
                         $.pjax.reload('#' + $.trim(pjaxContainer), {timeout: 3000});
                     });
                 }
             });
    
         });
     ");
    ?>
    
    <?php Pjax::begin(['id' => 'my_pjax']); ?>
     <div class="shop-index">
         <?= GridView::widget([
             'dataProvider' => $dataProvider,
             'filterModel' => $searchModel,
             'columns' => [
                 'id',
                 [
                     'class' => 'yii\grid\ActionColumn',
                     'buttons' => [
                         'update' => function ($url, $model) {
                             return Html::a('<span class="glyphicon glyphicon-pencil"></span>', $url, [
                                 'class' => 'pjax-update-link',
                                 'title' => Yii::t('yii', 'Update'),
                             ]);
                         },
                         'delete' => function ($url, $model) {
                             return Html::a('<span class="glyphicon glyphicon-trash"></span>', false, [
                                 'class' => 'pjax-delete-link',
                                 'delete-url' => $url,
                                 'pjax-container' => 'my_pjax',
                                 'title' => Yii::t('yii', 'Delete')
                             ]);
                         }
                     ],
                 ],
             ],
         ]); ?>
     </div>
    <?php Pjax::end(); ?>
    
]]>
0
[wiki] Configuring PhpStorm IDE for Yii 2 Fri, 27 Apr 2018 13:08:08 +0000 https://www.yiiframework.com/wiki/865/configuring-phpstorm-ide-for-yii-2 https://www.yiiframework.com/wiki/865/configuring-phpstorm-ide-for-yii-2 CeBe CeBe

There are a few settings and plugins that can enhance the development experience with Yii in PHPStorm or IntelliJ IDEA. This article explains how to get the most out of your IDE.

This arcticle is valid for Yii 2 and above, there is an older article for Yii 1.1.

Plugins

  • Yii2 Support adds many useful tools that improve working with Yii. It makes working with class configuration, DI and views easier.

  • Yii2 Inspections is a very useful plugin that adds Yii specific code inspections to PHPStorm. It helps for example to manage @property annotations for getters and setters as well as translation messages.

  • Php Inspections (EA Extended) is not directly Yii related but has a lot of additional code inspections that help you write better code. There is also a paid version that has even more features, especially security related inspections.

Project Setup

  • Exclude runtime directories from code search. Debug toolbar stores a lot of stuff that clutters search results.

    runtime - right click - Mark Directory As - Excluded

  • Enable composer integration to tell PHPStorm to separate vendor files from project files.

    composer.json - right click - Composer - Init Composer ...

PHPUnit and Codeception

PHPStorm has integrations for PHPUnit as well as Codeception, so you can run your tests directly from the IDE.

Settings for that can be found at Run - Edit Configurations....

To add your Codeception tests, click the + button, select Codeception. Then enter the following details:

  • Name: "Codeception tests" - or whatever you want to name it
  • Test Scope: Defined in the Configuration file
  • Use alternative configuration file: "codeception.yml"
  • In case PHPStorm asks you to do it, configure a PHP Interpreter in PHPStorm settings
  • Configure Codeception binary as vendor/bin/codecept

You can now run your tests directly from PHPStorm.

For PHPUnit the steps are similar but Yii application templates do not provide PHPUnit tests by default so the options depend on your setup.

]]>
0
[wiki] How to login from different tables in Yii2 Thu, 07 May 2020 12:13:11 +0000 https://www.yiiframework.com/wiki/864/how-to-login-from-different-tables-in-yii2 https://www.yiiframework.com/wiki/864/how-to-login-from-different-tables-in-yii2 androidelp androidelp

The Problem: Yii2 utilizes by default UserIdentity configured in config/web.php for connection, this object apply one table to authentication ('identityClass' => 'app\panel\models\User'). How to authentication from diferent tables? Solution: Create instances in web.php to uses UserIdentify. eg:

$user = \Yii::$app->user;  
$school = \Yii::$app->school; 
$teacher = \Yii::$app->teacher;

My config/web.php

'user' => [
	'class'=>'yii\web\User',
	'identityClass' => 'app\models\User',
	'enableAutoLogin' => false,
	'authTimeout' => 60*30,
	'loginUrl' => ['dashboard/login'],
	'identityCookie' => [
		'name' => '_panelUser',
	]
],
'school'=>[
	'class'=>'yii\web\User',
	'identityClass' => 'app\models\SchoolUser',
	'enableAutoLogin' => false,
	'authTimeout' => 60*30,
	'loginUrl' => ['dashboard-school/login'],
	'identityCookie' => [
		'name' => '_panelSchool',
	]	
],
'teacher'=> [
	'class'=>'yii\web\User',
	'identityClass' => 'app\models\TeacherUser',
	'enableAutoLogin' => false,
	'authTimeout' => 60*30,
	'loginUrl' => ['dashboard-teacher/login'],
    'identityCookie' => [
		'name' => '_panelTeacher',
	]
],

Note that for each there is a identifyClass and one view login. Now, we need to create the models:

namespace app\models;
use Yii;
// My user
class User extends \yii\db\ActiveRecord implements \yii\web\IdentityInterface
{
    public static function tableName()
    {
        return '{{%user}}';
    }
    // to continues....

Model school:


namespace app\models;
use Yii;
// My School
class SchoolUser' extends \yii\db\ActiveRecord implements \yii\web\IdentityInterface
{
    public static function tableName()
    {
        return '{{%schoolUser}}';
    }
    // to continues
    

Model Teacher:


namespace app\models;
use Yii;
// My School
class TeacherUser'' extends \yii\db\ActiveRecord implements \yii\web\IdentityInterface
{
    public static function tableName()
    {
        return '{{%teacher}}';
    }
    // to continues....

Now In my example I want to have controllers for each type of access, without generating conflicts between them:

In Behavior of the controller i have defined for dashboard-school, teacher and user, the user object representing the authentication status or the ID of the user application component.


	//behaviors of the school
public function behaviors()
{
	return [
        'access' => [
            'class' => AccessControl::className(),
            'user'=>'school', // this user object defined in web.php
            'rules' => [
                [
                    'allow' => true,
                    'roles' => ['@'],
                ],
                [
                    'allow' => true,
                    'actions' => ['login'],                    
 'roles' => ['?'],

                ],
            ],
        ]
    ];
}

To use login:

//school
\Yii::$app->scholl->login($model, $this->rememberMe ? 3600*24*30 : 0);

//teacher
\Yii::$app->teacher->login($model, $this->rememberMe ? 3600*24*30 : 0);

For restrict access in views:

<?php if (!\Yii::$app->scholl->isGuest):?>
<h1>My school Name: <?=\Yii::$app->scholl->identity->name?>
<?php endif;?>

<?php if (!\Yii::$app->teacher->isGuest):?>
<h1>Teacher Name: <?=\Yii::$app->teacher->identity->name?>
<?php endif;?>

Always use the specific instance of the user you want to work with.

Criticism, suggestions and improvements, use the comments

Agência Next4 Comunicação Digital Ltda.

]]>
0
[wiki] Use non Gmail/Gsuite on Gcloud projects Sun, 15 Jul 2018 14:00:50 +0000 https://www.yiiframework.com/wiki/863/use-non-gmailgsuite-on-gcloud-projects https://www.yiiframework.com/wiki/863/use-non-gmailgsuite-on-gcloud-projects gogl92 gogl92

Small companies and startups use cheap email services or even Cpanel's mail services which are less secure and compete directly with bigger email providers like Microsoft with Outlook and Google with Gmail. This creates a problem when you try to use their services to send/receive emails from this cheap services. google-cloud-platform.png

Google says this: Google Compute Engine does not allow outbound connections on ports 25, 465, and 587. By default, these outbound SMTP ports are blocked because of the large amount of abuse these ports are susceptible to. In addition, having a trusted third-party provider such as SendGrid, Mailgun, or Mailjet relieves Compute Engine and you from maintaining IP reputation with your receivers.

Some time ago I had a project that requieres this, send emails using a cpanel mail services but we have Google Cloud services so when the client send me the credentials to put it in production the app crashes in a critical time, client will not pay to use Gsuite and I have all the stuff ready for production and configured in Google Cloud, so I found a solution.

  • Make sure you have swiftmail extension installed (it also comes with the basic and advance template)
  1. First get a Gmail or Gsuite account to use it as a bridge between emails. The name doesn't matter it will never be seen by final users. let's say mail_service54@gmail.com or use a Gsuite custom email. It is also important that you have full priviledges to this email as you will need it in the next step.
  2. Enable less secure apps to your email. [https://support.google.com/accounts/answer/6010255?hl=en](Official Docs here)
  3. Now configure the swiftmailer extension to send emails with your account. I use this config for gmail/gsuite accounts:
    'mailer' => [
             'class' => 'yii\swiftmailer\Mailer',
             'transport' => [
                 'class' => 'Swift_SmtpTransport',
                 'host' => 'smtp.gmail.com',
                 'username' => 'yourmail@gmail.com',
                 'password' => 'your password',
                 'port' => '587',
                 'encryption' => 'tls',
             ],
         ],
    
  4. Test that you can send an email so we can get sure the past configuration works well.
  5. After you can send an email, login into your account on the web and go to settings > Accounts and Import > Send mail as
  6. Do the import process of the other service/cpanel email, usually they will send you a link/code to the other email and you will have to confirm it.
  7. Now you can from your gmail account send mails as some other mail.
  8. Do this in your Yii code:
    Yii::$app->mailer->compose()
     ->setFrom('from@cpanel-mail.com')
     ->setTo('to@domain.com')
     ->setSubject('Message subject')
     ->setTextBody('Plain text content')
     ->setHtmlBody('<b>HTML content</b>')
     ->send();
    

    And that's it!

This is the first post of many about using Yii/Open Source in business and enterprise level becuase Open source != not cost.

]]>
0