Quantcast
Channel: Live News for Yii Framework
Viewing all 2926 articles
Browse latest View live

[news] New member joining Yii team as primary Yii 3.0 developer

$
0
0

Andrii Vasyliev, @hiqsol joined Yii team. Andrii uses Yii daily for his team projects and is very interested in framework moving forward.

He's from Kiev, Ukraine and is part of HiQDev team same as another long term Yii team member, Dmytro Naumenko (@SilverFire).

His primary focus will be refreshing Yii architecture splitting it into more packages to achieve more frequent independent releases and making Yii core more robust.


[extension] yii2-lacaixa

$
0
0

Yii2 lacaixa Module

  1. Installation
  2. Usage

Yii2 lacaixa module to integrate the payment gateway (TPV Virtual) Redsys to be integrated into virtual web shops that have been developed under Yii2.

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require --prefer-dist kholmatov/yii2-lacaixa "*"

or add

"kholmatov/yii2-lacaixa": "*"

to the require section of your composer.json file.

Usage

Setting configuration file `

kholmatov/yii2-lacaixa/config.php

Once the extension is installed, simply use it in your code by:
```php
echo \kholmatov\lacaixa\RedsysWDG::getFormData($DS_MERCHANT_ORDER,$DS_MERCHANT_AMOUNT,$languageCode,$ProductDescription);

or get result on Json format: `php echo \kholmatov\lacaixa\RedsysWDG::getFormDataJson($DS_MERCHANT_ORDER,$DS_MERCHANT_AMOUNT,$languageCode,$ProductDescription); ` Put this example code in any controller script for testing success url in action (URLOK):

    
    ...
    
    public function actionOk(){
        $get = Yii::$app->request->get();
        if(isset($get) && isset($get['Ds_SignatureVersion']) && isset($get['Ds_MerchantParameters']) && isset($get['Ds_Signature'])):
            
            $rs = \kholmatov\lacaixa\RedsysWDG::checkData($get['Ds_SignatureVersion'],$get['Ds_MerchantParameters'],$get['Ds_Signature']);
            if($rs){
                $rsParam = \kholmatov\lacaixa\RedsysWDG::decodeData($get['Ds_MerchantParameters']);
                $myParam = json_decode($rsParam,true);
                print_r($myParam);
                  ....
               }
        endif;

        //return $this->redirect(array('/'));
    }
    
    ...

Put this example code in any controller script for testing cancel or error url in action (URLKO):

    
    ...
    
    public function actionKo(){
       $get = Yii::$app->request->get();
          if(isset($get) && isset($get['Ds_SignatureVersion']) && isset($get['Ds_MerchantParameters']) && isset($get['Ds_Signature'])):
            $rs = \kholmatov\lacaixa\RedsysWDG::checkData($get['Ds_SignatureVersion'],$get['Ds_MerchantParameters'],$get['Ds_Signature']);
            if($rs){
                       $rsParam = RedsysWDG::decodeData($get['Ds_MerchantParameters']);
                       $myParam = json_decode($rsParam,true);
                       print_r($myParam);
                      ... 
            }
          endif;
       
          //return $this->redirect(array('/'));
    }
    
    ...
    

[news] Replacing the forum software, moving to Discourse

$
0
0

A few month ago we have replaced the old Yii Framework website with a rewritten version in Yii 2. While developing the new site, we also discussed the replacement of the old IPB forum software with a more modern solution, but replacing the forum together with the site would have been too much work to do at once.

From a long discussion, which started already a few years ago, we have now evaluated different forum software and decided to go with Discourse, which is an open source forum software made by the people who created also StackOverflow. We are going to replace the old forum with a Discourse instance starting tomorrow (September 4, 2018).

Here is a list of things that are going to change:

  • User accounts will be managed by the website, there is no duplicate login as we have it now, and all users are signed in to the forum via SSO.
  • Forum categories, topics and posts are migrated from the old forum, so no content will be lost (we might re-arrange the categories though).
  • Links from the old forum should all be redirected to the new location. If you hit broken links, please report those to us!
  • Watched topics and watched forums are not going to be migrated, so if you want to get notified about new posts, make sure to visit the new forum and configure your notification settings as well as update watched topics.
  • User badges on the website do not include the forum posts anymore, instead we are using the Badge system by Discourse, which has a lot more badges than we had before.
  • Discourse allows you to configure it to behave like a mailing list, so if you prefer to take part in Yii discussions from your email client, you can do that now.

In case you have problems logging in to the new forum, please use the Contact form or Chat to get help.

There is a forum topic for discussion on this announcement.

[extension] ellera/yii2-backup

$
0
0

Yii2-Backup

  1. Getting started
  2. Installation
  3. Basic Usage
  4. Advanced Usage

Console backup for yii2 applications.

Current limitations:

  • Currently only MySQL on localhost is supported
  • Requires a linux system

Getting started

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require ellera/yii2-backup

or add

"ellera/yii2-backup": "*"

to the require section of your composer.json file.

Basic Usage

Add the following to your config file. For yii2 advanced template, this should be placed in console/config/main.php, for basic it should be in config/console.php. You can use another name then backup but you'll need to adjust the commands accordingly.

'modules' => [
    ...
    'backup' => [
        'class' => 'ellera\backup\Module'
    ]
    ...
]

Then migrate database migrations. This will create a table named backup in your database.

php yii migrate/up --migrationPath=@vendor/ellera/yii2-backup/src/migrations

When the migration is done, you're ready to backup your site. Use php yii *command* or ./yii *command* with the commands from the following table:

Command Description
backup List all the available commands
backup/create "Optional comment" Show file differences that haven't been staged
backup/list #OptionalPage Lists the current backups
backup/delete #ID Deletes a backup
backup/restore #ID Restores a backup
Manual: Create backup

php yii backup/create "Your Comment"

Creating a backup

Manual: List backups

php yii backup/list (# optional page number)

Listing backups

Manual: Restore backup

php yii backup/restore #

Restore backup

This will create an additional backup of the state before the restore

List after restore

Manual: Delete backup

php yii backup/delete #

Listing backups

Advanced Usage

Backing up files and folders

You can backup as many locations as you'd like by adding them to the folders array. To create backups require read access to the folder and files, to restore requires write/delete access. The folders will be zipped and saved in the backup folder.

'modules' => [
    ...
    'backup' => [
        'class' => 'ellera\backup\Module',
        'folders' => [
            'images' => '@app/web/images'
        ],
    ]
    ...
]
Changing or adding database handlers

You can backup as many databases as you'd like, but they have to be defined in your config as yii\db\Connections. Add the connection name to the array (defautls to only db).

'modules' => [
    ...
    'backup' => [
        'class' => 'ellera\backup\Module',
        'databases' => [
            'db', 
            'db2'
        ],
    ]
    ...
]
Database Conflict

If you already have a table named backup, create a table with your own migration and add 'table' => 'new_table_name' to the configuration. The content of the table can be found in the migration.

'modules' => [
    ...
    'backup' => [
        'class' => 'ellera\backup\Module',
        'table' => 'your_new_table_name'
    ]
    ...
]
Change the default backup location

If you want to store the backups in another directory, add 'path' => 'new/path' to the config.

This variable is parsed trough Yii::getAlias() and defaults to @app/_backup.

'modules' => [
    ...
    'backup' => [
        'class' => 'ellera\backup\Module',
        'path' => '@app/_backup'
    ]
    ...
]
Upload to remote server

If you want redundant backup over several servers, this module supports scp over SSH. For this function to work, you need to have SSH keys in place and the user must have write access to the remote folder specified in the config. It's highly recommended to create a user on the remote server for this purpose - do not use root.

'modules' => [
    ...
    'backup' => [
        'class' => 'ellera\backup\Module',
        'servers' => [
            // Unique name
            'server_name' => [
                // Server IP or domain
                'host' => '192.186.0.1',
                // Server username
                'user' => 'remote_user',
                // Remote backup path
                'path' => '/var/backups/myserver'
            ]
        ]
    ]
    ...
]

[news] Auth Client extension 2.1.6 released

$
0
0

We are very pleased to announce the release of Auth Client extension version 2.1.6. It upgrades VKontakte API used to version 5.0 and fixes a couple of bugs related to OpenID and canceling login in auth form.

See the CHANGELOG for details.

[extension] sam-it/yii2-mariadb

$
0
0

Latest Stable Version Total Downloads Scrutinizer Code Quality Code Coverage Build Status

yii2-mariadb

  1. Override the schema class used for MySQL
  2. Add schema class to schemaMap
  3. Use the provided behavior

While Yii2 supports MariaDB through its MySQL driver, the differences between MariaDB and MySQL are increasing. At this time the driver included in Yii2 will not properly detect JSON columns in MariaDB and will not properly store data in them.

The goal of this library is to implement the MariaDB specific changes required to get all features working in MariaDB that are supported in the Yii2 core library for other DBMSes.

Tests

  1. Override the schema class used for MySQL
  2. Add schema class to schemaMap
  3. Use the provided behavior

The tests coverage is really high due to 2 reasons:

  • All code extends their MySQL counter parts in the framework, only very little is added.
  • We run the core tests for Yii2 (with some minor changes) to guarantee interoperability with the framework.

Usage

  1. Override the schema class used for MySQL
  2. Add schema class to schemaMap
  3. Use the provided behavior

To use the MariaDB Schema implementation there are several approaches.

Override the schema class used for MySQL

Update the schemaMap property in your Connection config (the drivername is still mysql since we use the MySQL PDO driver) (RECOMMENDED)

'db' => [
    'class' => Connection::class,
    'schemaMap' => [
        'mysql' => SamIT\Yii2\MariaDb\Schema::class
    ]
]

Add schema class to schemaMap

Append the new Schema class to the schemaMap property and set the driverName property manually.

'db' => [
    'class' => Connection::class,
    'driverName' => 'mariadb',
    'schemaMap' => [
        'mariadb' => SamIT\Yii2\MariaDb\Schema::class
    ]
]

Use the provided behavior

Add the MariaDbBehavior to your Connection. `php 'db' => [

'class' => Connection::class',
'as mariadb' => \SamIT\Yii2\MariaDb\MariaDbBehavior::class

] `

The behavior will register a handler for the EVENT_AFTER_OPEN on the connection. When a connection opens it will check the PDO attribute(s) to see if it's a MariaDB connection. If that's the case then it will update the $schemaMap property on the connection.

While the behavior method in theory allows you to use the connection without knowing the database type in advance, it has some disadvantages. Specifically the Connection class might instantiate a the Schema before opening the connection. This happens when a query builder is requested before the database connection is opened. If you run into issues related to SQL syntax please try the first approach to see if that resolves the issue.

JSON Column detection

  1. Override the schema class used for MySQL
  2. Add schema class to schemaMap
  3. Use the provided behavior

Since MariaDB has no built-in JSON data type we need to do some extra work to detect JSON columns. We do this by parsing the SQL obtained when using SHOW CREATE TABLE. Since MariaDB supports CHECK constraints these are used to ensure a column can only contain valid JSON. Any constraint that of the form: `json_valid(column1) will identify the column as JSON. Note that this could lead to problems if you have weird constraints, consider this: `sql column1 longtext CHECK(not json_valid(column1)); ` Will mark column1` as a JSON column.

Column creation

  1. Override the schema class used for MySQL
  2. Add schema class to schemaMap
  3. Use the provided behavior

When creating JSON columns the ColumnSchemaBuilder requires the name of the column to add the table constraint. Since this is not the case for all other column types Yii does not pass the name of the column to the builder. Consider this code, for example in a migration:

$this->alterColumn('{{test}}', 'field1', $this->json());

Here there is no way for the ColumnSchemaBuilder to know what the name of the column is going to be. Since the schema builder is ultimately passed to QueryBuilder::alterColumn(), we can intercept it there and replace the column name in the constraint.

If you coerce the ColumnSchemaBuilder to string early, or use it without the QueryBuilder you will end up with SQL like this: `php ALTER COLUMN field JSON CHECK(json_valid({name})); That will clearly not work. For those cases we have added a `toString(string $columnName)` method to the builder.php // Will result in broken SQL. $this->alterColumn('{{test}}', 'field1', $this->json() . ' --APPEND SOMETHING'); // Will result in working SQL. $this->alterColumn('{{test}}', 'field1', $this->json()->toString('field1') . ' --APPEND SOMETHING'); `

[extension] sam-it/abac

[extension] sam-it/yii2-urlsigner

$
0
0

Scrutinizer Code Quality Code Coverage Build Status

yii2-urlsigner

Secure URL signing and validation.


[extension] sam-it/yii2-phpfpm

[extension] sam-it/yii2-static-assets

$
0
0

yii2-static-assets

Prevent publishing of assets at production time without changing your application.

Motivation

Nowadays docker is getting more and more attention and PHP applications are seeing different deployment scenarios. Where a few years ago if you split your nodes up at all you'd only split up the database server and the webserver that runs PHP as module or more recently via PHP-FPM, nowadays you want to split everything up.

Challenges

Having your webserver, for example nginx, running on a different server than PHP-FPM comes with several challenges, pros and cons:

  1. PHP can't write files and create publicly accessible URLs for them
  2. Cannot use local session storage
  3. File uploads that need to be publicly accessible need to be published to the webserver.
  4. File uploads that need to be protected have to accessible by all PHP nodes (so some kind of central storage is needed).

Solution

This extension will provide a solution for number 1 when it comes to Yii assets. The asset management system is nice when pushing changed assets directly to a server, it doesn't really work well in distributed environments though, so we need another approach.

This extension provides:

  • A console command that will scan your source code and vendor directory and will extract all your assets.
  • An AssetManager for use in production that will generate URLs for assets without checking for touching storage.

The workflow then becomes a bit different. During development the asset manager will act like a normal asset manager, publishing assets to the asset directory. When using docker-compose for the test environment you would mount the same host directory to the asset folder in the PHP container and in the webserver container.

During deployment the assetmanager simply returns URLs for assets on the assumption that they exist. Before deploying a new version of your app, you will rebuild your webserver container. This extension provides a console command that will publish all your assets to a directory of your choosing, this can then be used as part of the docker build context.

To publish your assets run the following command: yii staticAssets/assets/publish build12345

This will create the directory build12345 inside your runtime directory and publish all assets there.

Asset discovery / publishing

Assets are discovered by recursively iterating over all folders and files. Each file that ends with .php is then processed:

  • The namespace is extracted via regular expression matching.
  • The class name is extracted via regular expression matching.
  • We use reflection to check if the class is an instance of yii\web\AssetBundle and if so it is published.

Container building

To build an nignx container for server your application use this: yii staticAssets/assets/build-container You can configure the module to set some default values.

Configuration

For simple configuration use the ReadOnlyAssetManager in your application during production and development. This asset manager will use a simpler "hash" function that keeps directory structure readable. It supports $assetDevelopmentMode which allows for local asset development in a dockerized environment.

Asset development

The assumption is that you use docker-compose for local development, in which case you need to define a volume where assets are stored so that they are available in both the webserver as well as the phpfpm container: `` volumes: assets: nginx:

image: [name of your nginx container, built by this module]
environment:
  PHPFPM: "phpfpm:9000"
  RESOLVER: "127.0.0.11"
ports:
  - "12346:80" # Port where the application will be available
depends_on:
  - phpfpm
volumes:
# Defines the named volume as read-only for the webserver.
# Note the dev-assets, which allows to easily identify development
# mode while using browsers' developer tools.
  - type: volume
    source: assets
    target: /www/dev-assets
    read_only: true
    volume:
      nocopy: true

phpfpm:

dns: 8.8.4.4
image: [ name of your PHPFPM docker image ]
environment:
  DB_USER: root
  DB_NAME: test
  DB_PASS: secret
  DB_HOST: mysql
depends_on:
  - mysql
volumes:
  # The source code is loaded into PHPFPM for local development,
  # for production it should be baked into the image.
  - .:/project:ro
  # The asset volume, note the location which is where the ReadOnlyAssetManager
  # will publish assets when in development mode.
  - type: volume
    source: assets
    target: /tmp/assets



[extension] sam-it/yii2-csv-formatter

$
0
0

yii2-csv-formatter

Yii2 CSV Response Formatter

[extension] sam-it/yii2-jsonbehavior

$
0
0

Scrutinizer Code Quality Code Coverage Build Status Total Downloads Latest Stable Version

Yii2 JsonBehavior

Work with JSON fields in Yii2

This behavior adds advanced support for working with JSON data in Yii AR models.

Use JSON fields like normal fields

Consider a model having a data attribute that is stored as JSON. `` public function behaviors() {

return [
    ['class' => JsonBehavior::class, 'jsonAttributes' => ['data']]
];

}

// Examples: $model = new Model(); $model->a = "test"; // If attribute 'a' does not exist this is stored inside the data.

$model->a['b'] = 'c']; // Nested arrays are supported.

$model->data = ['x' => 'y']; // Assigning directly is supported. ``

[extension] sam-it/yii2-magic

$
0
0

Latest Stable Version Total Downloads Latest Unstable Version License Monthly Downloads Daily Downloads

yii2-magic

Improvements for Yii2 that make it more "magic".

ActionInjectionTrait

Use this trait in your controller to get dependency injection in controller actions. use \SamIT\Yii2\Traits\ActionInjectionTrait;

HighlightUnsafeAttributesTrait

Use this trait in your form to highlight unsafe attributes. use \SamIT\Yii2\Traits\HighlightUnsafeAttributesTrait;

SingleTableInheritanceTrait

Use this trait in your active record model to implement single table inheritance. `` use \SamIT\Yii2\Traits\SingleTableInheritanceTrait;

protected static function inheritanceConfig() {

return [
    'map' => [
        PartnerProject::class => 'partner'
    ],
    'column' => 'type'
];

} ` This trait uses a different query object. If you use your own ActiveQuery implementation, use SingleTableInheritanceQueryTrait`.

[extension] marketflow/yii2-regex-validator

[extension] mludvik/yii2-gopay

$
0
0

yii2-gopay

  1. Installation
  2. Usage
  3. Example

Yii2 integration for official GoPay's PHP SDK for Payments REST API.

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require mludvik/yii2-gopay:"~1.0"

or add

"mludvik/yii2-gopay": "~1.0"

to the require section of your composer.json.

Usage

Before you start using this extension, it is recommended to read GoPay REST API documentation.

GoPayComponent

GoPayComponent is thin wrapper over GoPay PHP SDK.

Enable it by adding respective component configuration.

'components' => [

  ...

  'goPay' => [
    'class' => 'mludvik\gopay\GoPayComponent',
    'config' => [
      'goid' => '...',
      'clientId' => '...',
      'clientSecret' => '...',
      'isProductionMode' => YII_ENV_PROD,
      'scope' => TokenScope::ALL,
      'language' => Language::ENGLISH,
      'timeout' => 30,
    ],
  ],

  ...
]

Then you can call any method you would call on Payments object.

use Yii;

Yii::$app->goPay->createPayment([...]);
Yii::$app->goPay->getStatus(...);
...
GoPayAsset

GoPayAsset imports front-end GoPay dependencies to your pages. It is sensitive to YII_EVN constant — loads production version when YII_ENV === 'prod', test version otherwise. There are multiple ways how to use it.

  1. To load it on all pages, register it in AppAsset.
class AppAsset extends AssetBundle {

  ...

  public $depends = [
    ...
    'mludvik\gopay\GoPayAsset',
  ];
}
  1. To load it on specific page, register it in respective view.
use mludvik\gopay\GoPayAsset;

GoPayAsset::register($this);
  1. When using GoPayForm described below, you do not need to register GoPayAsset at all. It will be registered automatically on pages where GoPayForm is rendered.
GoPayForm

GoPayForm is widget created to help you build checkout page. Use it if you like or develop your own widget with the help of GoPayComponent and GoPayAsset.

GoPayForm is basically ActiveForm with 2 differences.

  • It uses asynchronous POST request to submit data to controller and then redirects user to payment gateway.
  • It has static method response, which can be used in controller to build expected response to form submission.

Example

First of all, you will need form to collect user payment preferences (eg. preferred payment method). It is also a good place to put some business logic in.

namespace app\models;

use Yii;
use yii\base\Model;

class CheckoutForm extends Model {

  public $paymentMethod;

  ...

  public function createPayment() {
    // Build request based on form's data.
    $request = [...];

    // Establish payment on GoPay side.
    return Yii::$app->goPay->createPayment($request);
  }
}

Then create checkout action.

namespace app\controllers;

use Yii;
use yii\web\Controller;
use mludvik\gopay\GoPayForm;
use app\models\CheckoutForm;

class PaymentController extends Controller {

  ...

  public function actionCheckout() {
    $model = CheckoutForm;

    if(Yii::$app->request->isAjax) {
      if($model->load(Yii::$app->request->post()) && $model->validate()) {
        $response = $model->createPayment();
        return GoPayForm::response($response);
      } else {
        return GoPayForm::response(null);
      }
    }

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

Finally, create the view.

<?php use mludvik\gopay\GoPayForm; ?>

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

  ...

  <?= Html::submitButton('Pay') ?>
<?php GoPayForm::end(); ?>

[extension] lubosdz/yii2-ui-range-plus-minus

[extension] Mirocow/yii2-elasticsearch

$
0
0

Elasticsearch module based on official Elasticsearch PHP library

Join the chat at https://gitter.im/Mirocow/yii2-elasticsearch

Docs are available in english and russian.

Install

$ composer require --prefer-dist mirocow/yii2-elasticsearch

Configure

  • Create a class that implements the common\modules\elasticsearch\contracts\Index interface.
  • Add it to the module configuration in common/config/main.php
  • Start indexing
return [
    'modules' => [

        // elasticsearch
        common\modules\elasticsearch\Module::MODULE_NAME => [
          'class' => common\modules\elasticsearch\Module::class,
          'indexes' => [
            common\repositories\indexes\ProductsSearchIndex::class
          ]
        ],

    ],
    'bootstrap' => [
        common\modules\elasticsearch\Bootstrap::class
    ]
];

Create index

Create empty index `bash $ php yii elasticsearch/index/create index_name `

Fill index with all documents `bash $ php yii elasticsearch/index/populate index_name `

Destroy an index and all its data `bash $ php yii elasticsearch/index/destroy index_name `

Remove all existing indexes, re-create all indexes and re-index all documents for all indexes `bash $ php yii elasticsearch/index/rebuild `

Debug

$ export PHP_IDE_CONFIG="serverName=www.site.loc" && export XDEBUG_CONFIG="remote_host=192.168.1.6 idekey=xdebug" && php7.0 ./yii elasticsearch/index/create products_search

Query

For creating queries, you may use https://github.com/crowdskout/es-search-builder

[extension] urosg/yii2-outdated-browser-rework

$
0
0

yii2-outdatedbrowser-rework

  1. About
  2. Installation
  3. Usage
  4. License

YII2 Widget implementing outdated-browser-rework package for browser better detection

About

This widget was inspired by the mdscomp/yii2-outdated-browser-widget which uses the Outdated Browser script by Bürocratik.

This widget on the other hand uses the Outdated Browser Rework script made by the Mike MacCana.

Installation

The preferred way to install this extension is through composer. Check the composer.json for this extension's requirements and dependencies. Read this web tip /wiki on setting the minimum-stability settings for your application's composer.json.

To install, either run $ php composer.phar require urosg/yii2-outdated-browser-rework "~1.0" or add "urosg/yii2-outdated-browser-rework": "~1.0" to the require section of your composer.json file.

Refer to the CHANGELOG for details of release changes.

Usage

use urosg\widget\OutdatedBrowserRework;

// To use default configuration
echo OutdatedBrowserRework::widget();

// To specify custom configuration - listed are default settings
echo OutdatedBrowserReword::widget([
    'requiredCssProperty' => '',
    'language' => 'en',
    'isUnknownBrowserOK' => false,
    'requireChromeOnAndroid' => false,
    'messages' => [
        'outOfDate' => 'Your browser is out of date!',
        'updateWeb' => 'Update your browser to view this website correctly.',
        'updateGooglePlay' => 'Please install Chrome from Google Play',
        'updateAppStore' => 'Please update iOS from the Settings',
        'webUpdateUrl' => 'http://outdatedbrowser.com',
        'callToAction' => 'Update my browser now',
        'close' => 'Close'
    ],
    'browserSupport' => [
        'Chrome' => 57,
        'Edge' => 39,
        'Safari' => 10,
        'MobileSafari' => 10,
        'Firefox' => 50,
        'Opera' => 50,
        'Vivaldi' => 1,
        'IE' => false
    ]
]);

License

yii2-outdated-browser-rework is released under the BSD-3 Clause license. See the bundled LICENSE file for details.

[extension] rohitiuc/yii2-dynamicform

$
0
0

yii2-dynamicform

  1. Installation
  2. Demos
  3. Usage

It is widget to yii2 framework to clone form elements in a nested manner, maintaining accessibility. yii2-dynamicform

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require --prefer-dist rohitiuc/yii2-dynamicform "@dev"

or add

"rohitiuc/yii2-dynamicform": "@dev"

to the require section of your composer.json file.

Demos

Usage

Hypothetical Scenario

Database

The View
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use rohitiuc\dynamicform\DynamicFormWidget;
?>

<div class="customer-form">

    <?php $form = ActiveForm::begin(['id' => 'dynamic-form']); ?>
    <div class="row">
        <div class="col-sm-6">
            <?= $form->field($modelCustomer, 'first_name')->textInput(['maxlength' => true]) ?>
        </div>
        <div class="col-sm-6">
            <?= $form->field($modelCustomer, 'last_name')->textInput(['maxlength' => true]) ?>
        </div>
    </div>

    <div class="panel panel-default">
        <div class="panel-heading"><h4><i class="glyphicon glyphicon-envelope"></i> Addresses</h4></div>
        <div class="panel-body">
             <?php DynamicFormWidget::begin([
                'widgetContainer' => 'dynamicform_wrapper', // required: only alphanumeric characters plus "_" [A-Za-z0-9_]
                'widgetBody' => '.container-items', // required: css class selector
                'widgetItem' => '.item', // required: css class
                'limit' => 4, // the maximum times, an element can be cloned (default 999)
                'min' => 1, // 0 or 1 (default 1)
                'insertButton' => '.add-item', // css class
                'deleteButton' => '.remove-item', // css class
                'model' => $modelsAddress[0],
                'formId' => 'dynamic-form',
                'formFields' => [
                    'full_name',
                    'address_line1',
                    'address_line2',
                    'city',
                    'state',
                    'postal_code',
                ],
            ]); ?>

            <div class="container-items"><!-- widgetContainer -->
            <?php foreach ($modelsAddress as $i => $modelAddress): ?>
                <div class="item panel panel-default"><!-- widgetBody -->
                    <div class="panel-heading">
                        <h3 class="panel-title pull-left">Address</h3>
                        <div class="pull-right">
                            <button type="button" class="add-item btn btn-success btn-xs"><i class="glyphicon glyphicon-plus"></i></button>
                            <button type="button" class="remove-item btn btn-danger btn-xs"><i class="glyphicon glyphicon-minus"></i></button>
                        </div>
                        <div class="clearfix"></div>
                    </div>
                    <div class="panel-body">
                        <?php
                            // necessary for update action.
                            if (! $modelAddress->isNewRecord) {
                                echo Html::activeHiddenInput($modelAddress, "[{$i}]id");
                            }
                        ?>
                        <?= $form->field($modelAddress, "[{$i}]full_name")->textInput(['maxlength' => true]) ?>
                        <div class="row">
                            <div class="col-sm-6">
                                <?= $form->field($modelAddress, "[{$i}]address_line1")->textInput(['maxlength' => true]) ?>
                            </div>
                            <div class="col-sm-6">
                                <?= $form->field($modelAddress, "[{$i}]address_line2")->textInput(['maxlength' => true]) ?>
                            </div>
                        </div><!-- .row -->
                        <div class="row">
                            <div class="col-sm-4">
                                <?= $form->field($modelAddress, "[{$i}]city")->textInput(['maxlength' => true]) ?>
                            </div>
                            <div class="col-sm-4">
                                <?= $form->field($modelAddress, "[{$i}]state")->textInput(['maxlength' => true]) ?>
                            </div>
                            <div class="col-sm-4">
                                <?= $form->field($modelAddress, "[{$i}]postal_code")->textInput(['maxlength' => true]) ?>
                            </div>
                        </div><!-- .row -->
                    </div>
                </div>
            <?php endforeach; ?>
            </div>
            <?php DynamicFormWidget::end(); ?>
        </div>
    </div>

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

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

</div>
Javascript Events

$(".dynamicform_wrapper").on("beforeInsert", function(e, item) {
    console.log("beforeInsert");
});

$(".dynamicform_wrapper").on("afterInsert", function(e, item) {
    console.log("afterInsert");
});

$(".dynamicform_wrapper").on("beforeDelete", function(e, item) {
    if (! confirm("Are you sure you want to delete this item?")) {
        return false;
    }
    return true;
});

$(".dynamicform_wrapper").on("afterDelete", function(e) {
    console.log("Deleted item!");
});

$(".dynamicform_wrapper").on("limitReached", function(e, item) {
    alert("Limit reached");
});

The Controller (sample code)
<?php

namespace app\controllers;

use Yii;
use app\models\Customer;
use app\models\CustomerSearch;
use app\models\Address;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use app\base\Model;
use yii\web\Response;
use yii\widgets\ActiveForm;
use yii\helpers\ArrayHelper;

/**
 * CustomerController implements the CRUD actions for Customer model.
 */
class CustomerController extends Controller
{
    ...

    /**
     * Creates a new Customer model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     * @return mixed
     */
    public function actionCreate()
    {
        $modelCustomer = new Customer;
        $modelsAddress = [new Address];
        if ($modelCustomer->load(Yii::$app->request->post())) {

            $modelsAddress = Model::createMultiple(Address::classname());
            Model::loadMultiple($modelsAddress, Yii::$app->request->post());

            // ajax validation
            if (Yii::$app->request->isAjax) {
                Yii::$app->response->format = Response::FORMAT_JSON;
                return ArrayHelper::merge(
                    ActiveForm::validateMultiple($modelsAddress),
                    ActiveForm::validate($modelCustomer)
                );
            }

            // validate all models
            $valid = $modelCustomer->validate();
            $valid = Model::validateMultiple($modelsAddress) && $valid;
            
            if ($valid) {
                $transaction = \Yii::$app->db->beginTransaction();
                try {
                    if ($flag = $modelCustomer->save(false)) {
                        foreach ($modelsAddress as $modelAddress) {
                            $modelAddress->customer_id = $modelCustomer->id;
                            if (! ($flag = $modelAddress->save(false))) {
                                $transaction->rollBack();
                                break;
                            }
                        }
                    }
                    if ($flag) {
                        $transaction->commit();
                        return $this->redirect(['view', 'id' => $modelCustomer->id]);
                    }
                } catch (Exception $e) {
                    $transaction->rollBack();
                }
            }
        }

        return $this->render('create', [
            'modelCustomer' => $modelCustomer,
            'modelsAddress' => (empty($modelsAddress)) ? [new Address] : $modelsAddress
        ]);
    }

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

        if ($modelCustomer->load(Yii::$app->request->post())) {

            $oldIDs = ArrayHelper::map($modelsAddress, 'id', 'id');
            $modelsAddress = Model::createMultiple(Address::classname(), $modelsAddress);
            Model::loadMultiple($modelsAddress, Yii::$app->request->post());
            $deletedIDs = array_diff($oldIDs, array_filter(ArrayHelper::map($modelsAddress, 'id', 'id')));

            // ajax validation
            if (Yii::$app->request->isAjax) {
                Yii::$app->response->format = Response::FORMAT_JSON;
                return ArrayHelper::merge(
                    ActiveForm::validateMultiple($modelsAddress),
                    ActiveForm::validate($modelCustomer)
                );
            }

            // validate all models
            $valid = $modelCustomer->validate();
            $valid = Model::validateMultiple($modelsAddress) && $valid;

            if ($valid) {
                $transaction = \Yii::$app->db->beginTransaction();
                try {
                    if ($flag = $modelCustomer->save(false)) {
                        if (! empty($deletedIDs)) {
                            Address::deleteAll(['id' => $deletedIDs]);
                        }
                        foreach ($modelsAddress as $modelAddress) {
                            $modelAddress->customer_id = $modelCustomer->id;
                            if (! ($flag = $modelAddress->save(false))) {
                                $transaction->rollBack();
                                break;
                            }
                        }
                    }
                    if ($flag) {
                        $transaction->commit();
                        return $this->redirect(['view', 'id' => $modelCustomer->id]);
                    }
                } catch (Exception $e) {
                    $transaction->rollBack();
                }
            }
        }

        return $this->render('update', [
            'modelCustomer' => $modelCustomer,
            'modelsAddress' => (empty($modelsAddress)) ? [new Address] : $modelsAddress
        ]);
    }

    ...
}
Model Class
<?php

namespace app\base;

use Yii;
use yii\helpers\ArrayHelper;

class Model extends \yii\base\Model
{
    /**
     * Creates and populates a set of models.
     *
     * @param string $modelClass
     * @param array $multipleModels
     * @return array
     */
    public static function createMultiple($modelClass, $multipleModels = [])
    {
        $model    = new $modelClass;
        $formName = $model->formName();
        $post     = Yii::$app->request->post($formName);
        $models   = [];

        if (! empty($multipleModels)) {
            $keys = array_keys(ArrayHelper::map($multipleModels, 'id', 'id'));
            $multipleModels = array_combine($keys, $multipleModels);
        }

        if ($post && is_array($post)) {
            foreach ($post as $i => $item) {
                if (isset($item['id']) && !empty($item['id']) && isset($multipleModels[$item['id']])) {
                    $models[] = $multipleModels[$item['id']];
                } else {
                    $models[] = new $modelClass;
                }
            }
        }

        unset($model, $formName, $post);

        return $models;
    }
}


To zero or more elements (use the following code in your view file)

<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use rohitiuc\dynamicform\DynamicFormWidget;
?>

<div class="customer-form">

    <?php $form = ActiveForm::begin(['id' => 'dynamic-form']); ?>
    <div class="row">
        <div class="col-sm-6">
            <?= $form->field($modelCustomer, 'first_name')->textInput(['maxlength' => true]) ?>
        </div>
        <div class="col-sm-6">
            <?= $form->field($modelCustomer, 'last_name')->textInput(['maxlength' => true]) ?>
        </div>
    </div>

    <?php DynamicFormWidget::begin([
        'widgetContainer' => 'dynamicform_wrapper', // required: only alphanumeric characters plus "_" [A-Za-z0-9_]
        'widgetBody' => '.container-items', // required: css class selector
        'widgetItem' => '.item', // required: css class
        'limit' => 4, // the maximum times, an element can be added (default 999)
        'min' => 0, // 0 or 1 (default 1)
        'insertButton' => '.add-item', // css class
        'deleteButton' => '.remove-item', // css class
        'model' => $modelsAddress[0],
        'formId' => 'dynamic-form',
        'formFields' => [
            'full_name',
            'address_line1',
            'address_line2',
            'city',
            'state',
            'postal_code',
        ],
    ]); ?>

    <div class="panel panel-default">
        <div class="panel-heading">
            <h4>
                <i class="glyphicon glyphicon-envelope"></i> Addresses
                <button type="button" class="add-item btn btn-success btn-sm pull-right"><i class="glyphicon glyphicon-plus"></i> Add</button>
            </h4>
        </div>
        <div class="panel-body">
            <div class="container-items"><!-- widgetBody -->
            <?php foreach ($modelsAddress as $i => $modelAddress): ?>
                <div class="item panel panel-default"><!-- widgetItem -->
                    <div class="panel-heading">
                        <h3 class="panel-title pull-left">Address</h3>
                        <div class="pull-right">
                            <button type="button" class="remove-item btn btn-danger btn-xs"><i class="glyphicon glyphicon-minus"></i></button>
                        </div>
                        <div class="clearfix"></div>
                    </div>
                    <div class="panel-body">
                        <?php
                            // necessary for update action.
                            if (! $modelAddress->isNewRecord) {
                                echo Html::activeHiddenInput($modelAddress, "[{$i}]id");
                            }
                        ?>
                        <?= $form->field($modelAddress, "[{$i}]full_name")->textInput(['maxlength' => true]) ?>
                        <div class="row">
                            <div class="col-sm-6">
                                <?= $form->field($modelAddress, "[{$i}]address_line1")->textInput(['maxlength' => true]) ?>
                            </div>
                            <div class="col-sm-6">
                                <?= $form->field($modelAddress, "[{$i}]address_line2")->textInput(['maxlength' => true]) ?>
                            </div>
                        </div><!-- .row -->
                        <div class="row">
                            <div class="col-sm-4">
                                <?= $form->field($modelAddress, "[{$i}]city")->textInput(['maxlength' => true]) ?>
                            </div>
                            <div class="col-sm-4">
                                <?= $form->field($modelAddress, "[{$i}]state")->textInput(['maxlength' => true]) ?>
                            </div>
                            <div class="col-sm-4">
                                <?= $form->field($modelAddress, "[{$i}]postal_code")->textInput(['maxlength' => true]) ?>
                            </div>
                        </div><!-- .row -->
                    </div>
                </div>
            <?php endforeach; ?>
            </div>
        </div>
    </div><!-- .panel -->
    <?php DynamicFormWidget::end(); ?>

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

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

</div>

[wiki] Update and Delete buttons on Breadcrumb

$
0
0

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.

Viewing all 2926 articles
Browse latest View live