alexs/yii2-tabularload

A simple extension for complex forms have a tabular input for Yii2 Framework.

Installs: 14

Dependents: 0

Suggesters: 0

Security: 0

Type:yii2-extension

1.1.0 2021-10-09 22:05 UTC

This package is auto-updated.

Last update: 2024-04-10 05:49:11 UTC


README

A simple trait for complex forms which have a tabular input for Yii2 Framework.

It makes sense when we have a create/update form with relations one-to-many inside to do fast input, for example the main form adds Countries and it has many related Cities inside.

title

SQL

CREATE TABLE `country` (
  `id` int(4) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `population` int(4) DEFAULT NULL
) ENGINE=InnoDB;

CREATE TABLE `city` (
  `id` int(4) NOT NULL,
  `country_id` int(4) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `is_capital` tinyint(1) DEFAULT '0',
  `foundation` int(4) DEFAULT NULL,
  `image` varchar(255) DEFAULT NULL
) ENGINE=InnoDB;

ALTER TABLE `country`
  ADD PRIMARY KEY (`id`);

ALTER TABLE `city`
  ADD PRIMARY KEY (`id`),
  ADD KEY `fk-city-country_id` (`country_id`);

ALTER TABLE `country`
  MODIFY `id` int(4) NOT NULL AUTO_INCREMENT;

ALTER TABLE `city`
  MODIFY `id` int(4) NOT NULL AUTO_INCREMENT;

ALTER TABLE `city`
  ADD CONSTRAINT `fk-city-country_id` FOREIGN KEY (`country_id`) REFERENCES `country` (`id`) ON DELETE SET NULL;

Models

Country

<?php
namespace alexs\yii2tabularload\tests\models;
use yii\db\ActiveRecord;

/**
 * @property int $id
 * @property string $name
 * @property int $population
 */

class Country extends ActiveRecord
{
    public function rules() {
        return [
            [['name', 'population'], 'filter', 'filter'=>'trim'],
            [['name', 'population'], 'required'],
            ['population', 'integer'],
        ];
    }
}

City

<?php
namespace alexs\yii2tabularload\tests\models;
use alexs\yii2tabularload\TraitTabularload;
use yii\db\ActiveRecord;

/**
 * @property int $id
 * @property int $country_id
 * @property string $name
 * @property int $is_capital
 * @property int $foundation
 * @property string $image
 * @property Country $course
 */

class City extends ActiveRecord
{
    use TraitTabularload;

    public function rules() {
        return [
            [['name', 'foundation'], 'filter', 'filter'=>'trim'],
            [['name', 'foundation'], 'required'],
            [['is_capital', 'foundation'], 'integer'],
            //['image', 'image', 'extensions'=>['jpg', 'jpeg', 'png', 'gif']],
            ['image', 'string'], // just for tests
        ];
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getCountry() {
        return $this->hasOne(Country::class, ['id' => 'country_id']);
    }
}

The form

<div class="form">
    <!-- existing cities -->
    <?php if (!empty($cities)) { ?>
        <?php foreach ($cities as $i=>$city) { ?>
            <div class="row">
                <div class="col col-lg-6">
                    <?=$activeForm->field($city, "[$i]name", ['enableClientValidation'=>false])?>
                    <?=$activeForm->field($city, "[$i]foundation", ['enableClientValidation'=>false])->textarea();?>
                    <?=$activeForm->field($city, "[$i]is_capital", ['enableClientValidation'=>false])->checkbox();?>
                </div>
                <?=$activeForm->field($city, "[$i]id", ['enableClientValidation'=>false])->hiddenInput()->label(false);?>
            </div>
        <?php } ?>
    <?php } ?>
    <!-- 3 new empty blocks, they can be added via JS -->
    <?php $city = new City;?>
    <?php for ($i = 0; $i <= 2; $i ++) { ?>
        <div class="row">
            <div class="col col-lg-6">
                <?=$activeForm->field($city, "[$i]name", ['enableClientValidation'=>false])?>
                <?=$activeForm->field($city, "[$i]foundation", ['enableClientValidation'=>false])->textarea();?>
                <?=$activeForm->field($city, "[$i]is_capital", ['enableClientValidation'=>false])->checkbox();?>
            </div>
        </div>
    <?php } ?>
</div>

Actions

Create

<?php
public function actionCreate() {
    $country = new Country();
    $cities = [];
    if (\Yii::$app->request->isPost) {
        $country->load(\Yii::$app->request->post());
        $cities = City::tabularloadCreate('\app\models\City');
        $valid = $country->validate() && City::validateMultiple($cities);
        if ($valid) {
            $country->save(false);
            City::tabularloadSave($cities, 'country', $country);
            return $this->redirect('/countries');
        }
    }
    return $this->render([
        'country'=>$country,
        'cities'=>$cities,
    ]);
}

Update

<?php
public function actionUpdate($id) {
    if (!$country = Country::findOne($id)) {
        throw new \yii\web\NotFoundHttpException;  
    }
    $cities = [];
    if (\Yii::$app->request->isPost) {
        $country->load(\Yii::$app->request->post());
        $cities = City::tabularloadUpdate('\app\models\City', ['country_id'=>$country->id]);
        $valid = $country->validate() && City::validateMultiple($cities);
        if ($valid) {
            $country->save(false);
            City::tabularloadSave($cities, 'country', $country);
            return $this->redirect('/countries');
        }
    }
    return $this->render([
        'country'=>$country,
        'cities'=>$cities,
    ]);
}