Provide validation for minimum number inserted in Yii2 - php

i am having trouble with this validation,
how can i validate minimum number to be inserted in
$form->field($modeldetails, "[{$i}]qty")->textInput()
based on sum of table quantity field with parameter to filter from $form->field($model, 'tgl') because i cannot simply add range validation in models rule.
I need a function to get paramater from two models ($model,$modeldetails), and process summary of quantity, if user inserted more than minimum sum then show an error in quantity field.
View activeform field for header ( using dynamic form )
<?php $form = ActiveForm::begin(['id' => 'dynamic-form')]); ?>
<?= $form->field($model, 'tgl')->widget(DatePicker::classname(), [
'options' => ['placeholder' => 'Enter Transaction Date ...'],
'type' => DatePicker::TYPE_COMPONENT_APPEND,
'pluginOptions' => [
'autoclose'=>true,
'format' => 'yyyy-mm-dd',
'todayHighlight' => true
],
]); ?>
View activeform field for detail
<?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' => 15, // 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' => $modeldetail[0],
'formId' => 'dynamic-form',
'formFields' => [
'id_brg',
'qty',
],
]); ?>
<div class="panel panel-default">
<div class="panel-heading">
<h4>
Detail
<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 ($modeldetail as $i => $modeldetails): ?>
<div class="item panel panel-default"><!-- widgetItem -->
<div class="panel-heading">
<h3 class="panel-title pull-left"></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 (! $modeldetails->isNewRecord) {
echo Html::activeHiddenInput($modeldetails, "[{$i}]id");
}
?>
<div class="row">
<div class="col-sm-6">
<?= $form->field($modeldetails, "[{$i}]id_brg")->label('Nama')->widget(Select2::classname(), [
'data' => $brg,
'language' => 'en',
'options' => ['placeholder' => 'Select an item ...'],
'pluginOptions' => [
'allowClear' => true
],
]);?>
</div>
<div class="col-sm-6">
<?= $form->field($modeldetails, "[{$i}]qty")->textInput() ?>
</div>
</div><!-- .row -->
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div><!-- .panel -->
<?php DynamicFormWidget::end(); ?>
my models for details
class Detail extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return 'detail';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['id_brg', 'qty'], 'required'],
[['kode', 'id_brg', 'qty'], 'integer'],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'kode' => 'Kode',
'id_brg' => 'ID Barang',
'qty' => 'Qty',
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getLinkheader()
{
return $this->hasOne(Transaksi::className(), ['kode' => 'kode']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getItems()
{
return $this->hasOne(Barang::className(), ['id' => 'id_brg']);
}
}

Core Validators integer
[['qty'], 'integer', 'min' => 123],

Use custom validator. Simple example is inline validator:
public function rules()
{
return [
[['id_brg', 'qty'], 'required'],
[['kode', 'id_brg', 'qty'], 'integer'],
['qty', function ($attribute, $params) {
/* calculate min value */
$min = 123;
if ($this->$attribute > $min) {
$this->addError($attribute, "Qty must be less than {$min}.");
}
}],
];
}

Related

Why is the section of my view disappears?

I have a view made with livewire and it contains two dynamically add sections for spoken languages and the other one for courses and certificates. The language section was working fine until I added the add certificates section. now when the user clicks on the add section for the certificate section to add a new one, one of the added language sections disappears and then will appear again once you add another one. I guess it will appear again once the view re-renders. I have been going back and force moving functions around from render method to update/updated/hydrate/dehydrate but got no luck. at first, I thought it had something to do with the $loop->index that I use in my view but after changing that I realized it was not it. I'm hitting a dead-end here and can't figure out what's going on here.
I also made a screen record of what is happening so it might help:
https://drive.google.com/file/d/1Hq2wTKcPvhs05SKFMICaRoOAzpSyj9Iz/view?usp=sharing
View section:
<!-- language section -->
<div class="card card-profile shadow-sm mt-4">
<div class="px-4 mt-4 mb-4">
<div class="h5 font-weight-bold mb-4">Spoken languages</div>
<div class="heading text-muted mb-4">you only can add 3 languages to your profile.</div>
#foreach ($languages as $lindex => $language)
<div class="card card-body mb-4" wire:key="{{ $lindex }}">
<div class="text-left"><span class="fa fa-trash text-gray language-delete" wire:click="removeLanguage({{ $lindex }}, {{ !empty($language['id']) ? $language['id'] : 0 }})"></span></div>
<div class="row">
<div class="form-group col-md-6">
<label class="" for="languageName">language</label>
<select class="form-control form-control-alternative" name="language-name" {{-- id="languageName" --}} wire:model="languages.{{ $lindex }}.language_name">
<option value="" class="form-control" selected disabled>select language</option>
#foreach ($language_names as $name)
<option value="{{ $name->abbreviation }}" class="form-control">{{ $name->language }}</option>
#endforeach
</select>
</div>
<div class="form-group col-md-6">
<label class="" for="languageProficiency">proficiency level</label>
<select class="form-control form-control-alternative" name="language-proficiency" {{-- id="languageProficiency" --}} wire:model="languages.{{ $lindex }}.language_level">
<option value="" class="form-control" selected disabled>proficiency level</option>
#foreach ($language_levels as $level)
<option value="{{ $level->level }}" class="form-control">{{ $level->name }}</option>
#endforeach
</select>
</div>
</div>
</div>
#endforeach
#error('languages.*.language_level')
<small class="text-warning">{{ $message }}</small>
#enderror
#error('languages.*.language_language')
<small class="text-warning">{{ $message }}</small>
#enderror
#if (count($languages) < 3)
<div class="row">
<div class="col-md-12">
<button type="button" class="btn btn-outline-secondary btn-round btn-block" wire:click="addLanguage"><span class="btn-inner--icon"><i class="fa fa-plus fa-2x"></i></span></button>
</div>
</div>
#endif
</div>
</div>
<!-- end language section -->
<!-- other certificates section -->
<div class="card card-profile shadow-sm mt-4">
<div class="px-4 mt-4 mb-4">
<div class="h5 font-weight-bold mb-4">Other related certificates</div>
<div class="heading text-muted mb-4">if you have other related certificates in your files you can add then here.</div>
#foreach ($certificates as $cindex => $certificate)
<div class="card card-body mb-4" wire:key="{{ $cindex }}">
<div class="row">
<div class="form-group col-md-6">
<label class="" for="other-certificates-name">certificate name</label>
<input type="text" class="form-control form-control-alternative" placeholder="" name="ptherCertificatesName">
</div>
<div class="form-group col-md-6">
<label class="" for="other-certificates-school-name">School name</label>
<input type="text" class="form-control form-control-alternative" placeholder="" name="otherCertificatesSchoolName">
</div>
<div class="form-group col-md-6">
<label class="" for="other-certificates-verification-link">URL<small>(optional)</small></label>
<input type="text" class="form-control form-control-alternative text-left" placeholder="" name="otherCertificatesVerificationLink">
</div>
<div class="form-group col" dir="ltr">
<label class="" for="other-certificates-grad-date" dir="rtl">finished date</label>
<div class="input-group input-group-alternative">
<div class="input-group-prepend">
<span class="input-group-text"><i class="ni ni-calendar-grid-58"></i></span>
</div>
<input type="text" class="form-control form-control-alternative datePicker" placeholder="" name="otherCertificatesGradDate" value="">
</div>
</div>
</div>
</div>
#endforeach
#if (count($certificates) < 5)
<div class="row">
<div class="col-md-12">
<button type="button" class="btn btn-outline-secondary btn-round btn-block" wire:click="addCertificate"><span class="btn-inner--icon"><i class="fa fa-plus fa-2x"></i></span></button>
</div>
</div>
#endif
</div>
</div>
<!-- end other certificates section -->
Livewire component controller:
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\CityName;
use App\Models\Language;
use App\Models\LanguageLevel;
use App\Models\User;
use App\Models\UserLanguage;
use App\Models\UserProfile;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Auth;
class UserInfo extends Component
{
public $name;
public $email;
public $phone;
public $states = [];
public $selectedstate;
public $cities = [];
public $selectedcity;
public $jobTitle;
public $aboutMe;
public $employer;
public $position;
public $edu_course;
public $edu_school;
public $edu_level;
public $edu_start_date;
public $edu_end_date;
public $employment_looking;
public $employment_hired;
public $twitter;
public $linkedin;
public $github;
public $instagram;
public $website;
public $languages = [];
public $language_names;
public $language_levels;
public $languageToDelete = [];
public $certificates = [];
public $certificate_name;
public $certificate_school;
public $certificate_link;
public $certificate_date;
public $certificateToDelete = [];
public $skillsQuery;
public $skillResults;
public $skills;
public function render()
{
$this->retriveStates();
$this->retriveCities();
$this->retriveLanguages();
return view('livewire.user-info');
}
public function retriveStates()
{
$this->states = CityName::distinct()->get(['state']);
}
public function retriveCities()
{
$this->cities = CityName::where('state', $this->selectedstate)->get(['city']);
}
public function updatedSkillsQuery()
{
$this->skillResults = $this->skills->where('name', 'like', $this->skillsQuery) ?? collect();
}
public function retriveLanguages()
{
$this->language_names = Language::all();
$this->language_levels = LanguageLevel::all();
}
public function addLanguage()
{
if (count($this->languages) <= 3)
{
array_push($this->languages, ['language_name'=>'', 'language_level'=>'', 'id'=>'']);
}
else
{
$this->sweetAlert('error', 'you only can add 3 languages to your profile.');
}
}
public function getUserLanguages()
{
$this->languages = auth()->user()->userLanguages->toArray();
}
public function removeLanguage($languagePosition, $languageId)
{
if (isset($languageId))
{
if ($languageId == 0)
{
array_splice($this->languages, $languagePosition, 1);
}
else
{
array_push($this->languageToDelete, $languageId);
array_splice($this->languages, $languagePosition, 1);
}
}
}
public function addCertificate()
{
if (count($this->certificates) <= 5)
{
array_push($this->certificates, ['name'=>'', 'school'=>'', 'link'=>'', 'date'=>'', 'id'=>'']);
/* dd($this->languages); */
}
else
{
$this->sweetAlert('error', 'you only can add 5 certificates to your profile.');
}
}
public function mount()
{
$this->skills = collect([
['name' => 'vue'],
['name' => 'vue'],
['name' => 'vue'],
['name' => 'laravel'],
['name' => 'laravel'],
['name' => 'laravel'],
]);
$this->skillResults= [];
$this->skillsQuery = '';
/* $this->retriveLanguages();
$this->retriveStates();
$this->retriveCities(); */
$this->getUserLanguages();
$this->name = auth()->user()->name;
$this->email = auth()->user()->email;
$this->phone = auth()->user()->phone;
$this->selectedstate = auth()->user()->userprofile->state;
$this->selectedcity = auth()->user()->userprofile->city;
$this->jobTitle = auth()->user()->userprofile->job_title;
$this->aboutMe = auth()->user()->userprofile->about_me;
$this->employer = auth()->user()->userprofile->employer;
$this->position = auth()->user()->userprofile->position;
$this->edu_course = auth()->user()->userprofile->edu_course;
$this->edu_school = auth()->user()->userprofile->edu_school;
$this->edu_level = auth()->user()->userprofile->edu_level;
$this->edu_start_date = auth()->user()->userprofile->edu_start_date;
$this->edu_end_date = auth()->user()->userprofile->edu_end_date;
$this->employment_looking = auth()->user()->userprofile->employment_looking;
$this->employment_hired = auth()->user()->userprofile->employment_hired;
$this->twitter = auth()->user()->userprofile->twitter;
$this->linkedin = auth()->user()->userprofile->linkedin;
$this->github = auth()->user()->userprofile->github;
$this->instagram = auth()->user()->userprofile->instagram;
$this->website = auth()->user()->userprofile->website;
}
public function rules()
{
return [
'name' => ['required', 'string', 'max:250'],
'email' => [
'required',
'email',
'max:250',
Rule::unique('users')->ignore(auth()->id()),
],
'phone' => ['required', 'digits:11'],
'selectedstate' => 'required',
'selectedcity' => 'required',
'jobTitle' => ['required', 'string', 'max:250'],
'aboutMe' => ['required', 'string', 'max:250'],
'employer' => ['string', 'max:250'],
'position' => ['string', 'max:250'],
'edu_course' => ['nullable', 'string', 'max:250'],
'edu_school' => ['nullable', 'string', 'max:250'],
'edu_level' => ['nullable', 'string', 'max:250'],
'edu_start_date' => ['nullable', 'string'],
'edu_end_date' => ['nullable', 'string'],
'employment_looking' => ['nullable', 'boolean'],
'employment_hired' => ['nullable', 'boolean'],
'twitter' => ['nullable', 'string', 'max:250'],
'linkedin' => ['nullable', 'string', 'max:250'],
'github' => ['nullable', 'string', 'max:250'],
'instagram' => ['nullable', 'string', 'max:250'],
'website' => ['nullable', 'string', 'max:250'],
'languages.*.language_name' => ['required', 'exists:App\Models\Language,abbreviation'],
'languages.*.language_level' => ['required', 'exists:App\Models\LanguageLevel,level'],
];
}
public function submit()
{
$user = Auth::user();
$this->validate();
User::where('id', auth()->id())->update([
'name' => $this->name,
'email' => $this->email,
'phone' => $this->phone,
]);
UserProfile::where('user_id', auth()->id())->update([
'state' => $this->selectedstate,
'city' => $this->selectedcity,
'job_title' => $this->jobTitle,
'about_me' => $this->aboutMe,
'employer' => $this->employer,
'position' => $this->position,
'edu_course' => $this->edu_course,
'edu_school' => $this->edu_school,
'edu_level' => $this->edu_level,
'edu_start_date' => $this->edu_start_date,
'edu_end_date' => $this->edu_end_date,
'employment_looking' => $this->employment_looking,
'employment_hired' => $this->employment_hired,
'twitter' => $this->twitter,
'linkedin' => $this->linkedin,
'github' => $this->github,
'instagram' => $this->instagram,
'website' => $this->website,
]);
if (!empty($this->languageToDelete))
{
/* $user = Auth::user(); */
foreach ($this->languageToDelete as $delete)
{
$user->userLanguages()->where('id', $delete)->delete();
}
}
foreach ($this->languages as $language)
{
/* $user = Auth::user(); */
$user->userLanguages()->updateOrCreate([
'language_name' => $language['language_name'],
],
[
'language_name' => $language['language_name'],
'language_level' => $language['language_level']
]
);
}
$this->getUserLanguages();
$this->sweetAlert('success', 'changes saved!');
}
public function sweetAlert($type, $message)
{
$this->alert($type, $message, [
'position' => 'bottom-end',
'timer' => 5000,
'toast' => true,
'text' => null,
'showCancelButton' => false,
'showConfirmButton' => false
]);
}
}
The issue has been solved. the problem indeed was a DOM issue caused by multiple loop index. as what I thought at first the $loop->index was causing it and I was on the right track to change it to something else like $languages as $lindex => $language but apparently even by doing that those two sections will still have the same wire:key because of the loop iterations which returns 0,1,2 and so on in order. so by appending a string to the key like lang{{ $loop->index }} they will be unique and therefore problem solved.

Yii2 Dynamic Form Error on Create - The 'model' property must be set and must extend from '\yii\base\Model'

In Yii2, I have been trying to create a dynamic form using the below tutorials.
Yii2-dynamicForm - GitHub and Youtube Tutorial.
I followed the same step as mentioned in tutorials, unfortunately I am held up with an error(screenshot attached)
And I am not able to resolve this error, can any one help me to find what I am missing.
For the reference I am attaching my Models here, rest the controllers and views are same as the tutorials.
Request Model
<?php
namespace app\models;
use Yii;
class Request extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'request';
}
public function rules()
{
return [
[['req_date', 'req_on', 'req_updated_on'], 'safe'],
[['req_job', 'req_type', 'material_type', 'req_status'], 'required'],
[['req_by'], 'integer'],
[['req_status'], 'string'],
[['req_job', 'req_type'], 'string', 'max' => 100],
[['material_type'], 'string', 'max' => 150],
];
}
public function attributeLabels()
{
return [
'req_id' => 'Req ID',
'req_date' => 'Req Date',
'req_job' => 'Job No',
'req_type' => 'Type of Request',
'req_on' => 'Required On',
'material_type' => 'Material Type',
'req_by' => 'Req By',
'req_updated_on' => 'Req Updated On',
'req_status' => 'Request Status',
];
}
}
RequestItems Model
<?php
namespace app\models;
use Yii;
use yii\base\Model;
/**
* This is the model class for table "request_items".
*
* #property int $req_item_id
* #property int $req_id
* #property string $item_name
* #property string $item_qty
* #property string $item_unit
*/
class RequestItems extends \yii\db\ActiveRecord
{
/**
* {#inheritdoc}
*/
public static function tableName()
{
return 'request_items';
}
/**
* {#inheritdoc}
*/
public function rules()
{
return [
[['req_id', 'item_name', 'item_qty', 'item_unit'], 'required'],
[['req_id'], 'integer'],
[['item_name'], 'string', 'max' => 100],
[['item_qty', 'item_unit'], 'string', 'max' => 25],
];
}
/**
* {#inheritdoc}
*/
public function attributeLabels()
{
return [
'req_item_id' => 'Req Item ID',
'req_id' => 'Req ID',
'item_name' => 'Item Name',
'item_qty' => 'Item Qty',
'item_unit' => 'Item Unit',
];
}
}
_form.php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use wbraganca\dynamicform\DynamicFormWidget;
use kartik\select2\Select2;
use kartik\checkbox\CheckboxX;
/* #var $this yii\web\View */
/* #var $model app\models\Request */
/* #var $form yii\widgets\ActiveForm */
?>
<div class="request-form">
<?php $form = ActiveForm::begin(['id' => 'dynamic-form']); ?>
<?= $form->field($model, 'req_date')->hiddenInput()->label(false) ?>
<div class="row">
<div class="col-sm-6">
<?php
$data = ['1001' => '1001/Sample Job 1', '1002' => '1002/Sample Job 2', '1003' => '1003/Sample Job 3'];
echo $form->field($model, 'req_job')->widget(Select2::classname(), [
'data' => $data,
'class'=>'form-control',
'options' => ['placeholder' => 'Select a state ...'],
'pluginOptions' => [
'allowClear' => true
],
]); ?>
</div>
<div class="col-sm-6">
<?= $form->field($model, 'req_type')->radioList(array('Urgent'=>'Urgent','Normal'=>'Normal')); ?>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<?= $form->field($model, 'req_on')->input('date') ?>
</div>
<div class="col-sm-6">
<?= $form->field($model, 'material_type')->radioList(array('Civil'=>'Civil','Mechanical'=>'Mechanical', 'Manpower'=>'Manpower')); ?>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<?= $form->field($model, 'req_by')->hiddenInput()->label(false) ?>
</div>
<div class="col-sm-6">
<?= $form->field($model, 'req_updated_on')->hiddenInput()->label(false) ?>
</div>
</div>
<!-- code for dynamic form -->
<div class="panel panel-default">
<div class="panel-heading"><h4><i class="glyphicon glyphicon-envelope"></i> Request Items</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,
'formId' => 'dynamic-form',
'formFields' => [
'item_name',
'item_qty',
'item_unit',
],
]); ?>
<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">Requested Items</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}]item_name")->textInput(['maxlength' => true]) ?>
<div class="row">
<div class="col-sm-6">
<?= $form->field($modelAddress, "[{$i}]item_qty")->textInput(['maxlength' => true]) ?>
</div>
<div class="col-sm-6">
<?= $form->field($modelAddress, "[{$i}]item_unit")->textInput(['maxlength' => true]) ?>
</div>
</div><!-- .row -->
</div>
</div>
<?php endforeach; ?>
</div>
<?php DynamicFormWidget::end(); ?>
</div>
</div>
<?= $form->field($requestModel, 'req_status')->dropDownList(['Requested' => 'Requested', 'Approved' => 'Approved', 'Rejected' => 'Rejected', 'Hold' => 'Hold',], ['prompt' => '']) ?>
<div class="form-group">
<?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
You need to change the
'model'=>$modelsAddress
to
'model'=>$modelsAddress[0]
As stated in the basic example, its an array of models and the way you passing, it will be detected as array rather than a model.

yii2 dynamic form for single model

I am working yii2 advanced of dynamic form using webraganca for a single model. but i am unable to precede further due to the following error. Any help is appreciated to much.
my code is as follows:
controller.php
namespace frontend\controllers;
use Yii;
use frontend\models\Ireturn;
use frontend\models\IreturnSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\helpers\Json;
use frontend\models\Model;
/**
* IreturnController implements the CRUD actions for Ireturn model.
*/
class IreturnController extends Controller
{
/**
* #inheritdoc
*/
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
];
}
/**
* Lists all Ireturn models.
* #return mixed
*/
public function actionIndex()
{
$searchModel = new IreturnSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
if(Yii::$app->request->post('hasEditable'))
{
$ireturnId = Yii::$app->request->post('editableKey');
$ireturn = Ireturn::findOne($ireturnId);
$out = Json::encode(['output'=>'','message'=>'']);
$post = [];
$posted = current($_POST['Ireturns']);
$post['Ireturns']= $posted;
if($ireturn->load($post))
{
$ireturn->save();
$output ='my values';
$out = Json::encode(['output'=>$output,'message'=>'']);
}
return;
}
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Ireturn model.
* #param integer $id
* #return mixed
*/
public function actionView($id)
{
$model = $this->findModel($id);
$modelsItem = $model->ireturn;
return $this->render('view', [
'model' => $this->findModel($id),
'modelsItem'=>$modelsItem,
]);
}
/**
* Creates a new Ireturn model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
$model = new Ireturn();
$modelsAddress = [new Ireturn];
if ($model->load(Yii::$app->request->post())) {
$modelsAddress = Model::createMultiple(Ireturn::classname());
Model::loadMultiple($modelsAddress, Yii::$app->request->post());
// validate all models
$valid = $model->validate();
$valid = Model::validateMultiple($modelsAddress) && $valid;
if ($valid) {
$transaction = \Yii::$app->db->beginTransaction();
try {
if ($flag = $model->save(false)) {
foreach ($modelsAddress as $modelAddress) {
$modelAddress->id = $model->id;
if (! ($flag = $modelAddress->save(false))) {
$transaction->rollBack();
break;
}
}
}
if ($flag) {
$transaction->commit();
return $this->redirect(['view', 'id' => $model->id]);
}
} catch (Exception $e) {
$transaction->rollBack();
}
}
}
return $this->render('create', [
'model' => $model,
'modelsAddress' => (empty($modelsAddress)) ? [new Ireturn] : $modelsAddress
]);
/*$model = new Ireturn();
$modelsAddress = [new Ireturn];
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
'modelsAddress' => (empty($modelsAddress)) ? [new Ireturn] : $modelsAddress
]);
}*/
}
/**
* Updates an existing Ireturn 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 ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
/**
* Deletes an existing Ireturn model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* #param integer $id
* #return mixed
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the Ireturn model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* #param integer $id
* #return Ireturn the loaded model
* #throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Ireturn::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
/*public function actionEditableDemo() {
$model = new Ireturn(); // your model can be loaded here
// Check if there is an Editable ajax request
if (isset($_POST['hasEditable'])) {
// use Yii's response format to encode output as JSON
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
// read your posted model attributes
if ($model->load($_POST[])) {
// read or convert your posted information
$value = $model->functional;
// return JSON encoded output in the below format
return ['output'=>$value, 'message'=>''];
// alternatively you can return a validation error
// return ['output'=>'', 'message'=>'Validation error'];
}
// else if nothing to do always return an empty JSON encoded output
else {
return ['output'=>'', 'message'=>''];
}
}
// Else return to rendering a normal view
return $this->render('view', ['model'=>$model]);
}*/
}
_form.php
use yii\helpers\Html;
use yii\helpers\ArrayHelper;
use yii\widgets\ActiveForm;
use backend\models\Items;
use backend\models\Departments;
use kartik\select2\Select2;
use dosamigos\datepicker\DatePicker;
use yii\web\View;
use wbraganca\dynamicform\DynamicFormWidget;
use common\models\User;
/* #var $this yii\web\View */
/* #var $model backend\models\Ireturn */
/* #var $form yii\widgets\ActiveForm */
?>
<div class="ireturn-form">
<?php $form = ActiveForm::begin(['id' => 'dynamic-form','options'=>['class'=>'well'],]); ?>
<div class="panel panel-default" >
<!--<div class="panel-heading">
<h4><i class="glyphicon glyphicon-envelope"></i> Items
</h4>
</div>-->
<div class="panel-body">
<div class="row">
<div class="col-xs-4">
<?= $form->field($model, 'dept_id')->widget(Select2::classname(), [
'data' => ArrayHelper::map(Departments::find()->all(),'id','dept_name'),
'language' => 'en',
'options' => ['placeholder' => 'Select departments ...'],
'pluginOptions' => [
'allowClear' => true
],
]);
?>
</div>
<div class="col-xs-3">
<?= $form->field($model, 'voucher_no')->textInput(['maxlength' => true])?>
</div>
</div>
<!-- <div class="panel panel-default">-->
<!-- <div class="panel-heading"><h4><i class="glyphicon glyphicon-envelope"></i> Items</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' => [
'items_id',
'unit',
'amount',
'functional',
],
]); ?>
<div class="container-items"><!-- widgetContainer -->
<div class="item panel panel-default"><!-- widgetBody -->
<div class="panel-heading">
<h3 class="panel-title pull-left"> <i class="glyphicon glyphicon-envelope"></i>Item</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>
<?php foreach ($modelsAddress as $i => $modelAddress): ?>
<div class="panel-body">
<?php
// necessary for update action.
if (! $modelAddress->isNewRecord) {
echo Html::activeHiddenInput($model, "[{$i}]id");
}
?>
<div class="row">
<div class="col-sm-3">
<?= $form->field($modelAddress, "[{$i}]items_id")->dropDownList(
ArrayHelper::map(Items::find()->all(),'id','item_name'),
['prompt'=>'select items']) ?>
</div>
<div class="col-sm-3">
<?= $form->field($modelAddress, "[{$i}]unit")->textInput(['maxlength' => true]) ?>
</div>
<div class="col-sm-3">
<?= $form->field($modelAddress, "[{$i}]amount")->textInput(['maxlength' => true]) ?>
</div>
<div class="col-sm-3">
<?= $form->field($modelAddress, "[{$i}]functional")->textInput(['maxlength' => true]) ?>
</div>
</div><!-- .row -->
</div>
<?php endforeach; ?>
</div>
</div>
<?php DynamicFormWidget::end(); ?>
<!-- </div>-->
<!-- </div>-->
<div class="padding-v-md">
<div class="line line-dashed"></div>
</div>
<div class="row">
<div class="col-xs-4">
<?= $form->field($model,'user_id')->dropDownList(
ArrayHelper::map(User::find()->all(),'id','full_name'),
['prompt'=>'select employees'])
?>
<?= $form->field($model,'head_id')->dropDownList(
ArrayHelper::map(User::find()->all(),'id','full_name'),
['prompt'=>'select dept heads'])
?>
<?= $form->field($model,'man_id')->dropDownList(
ArrayHelper::map(User::find()->all(),'id','full_name'),
['prompt'=>'select stoke managers'])
?>
<?= $form->field($model,'keeper_id')->dropDownList(
ArrayHelper::map(User::find()->all(),'id','full_name'),
['prompt'=>'select stoke keepers'])
?>
</div>
<div class="col-xs-4">
<?=$form->field($model, 'emp_date')->widget(
DatePicker::className(), [
'inline' => false,
'clientOptions' => [
'autoclose' => true,
'format' => 'yyyy-mm-dd'
]
]);?>
<?=$form->field($model, 'head_date')->widget(
DatePicker::className(), [
'inline' => false,
'clientOptions' => [
'autoclose' => true,
'format' => 'yyyy-mm-dd'
]
]);?>
<?=$form->field($model, 'man_date')->widget(
DatePicker::className(), [
'inline' => false,
'clientOptions' => [
'autoclose' => true,
'format' => 'yyyy-mm-dd'
]
]);?>
<?=$form->field($model, 'keeper_date')->widget(
DatePicker::className(), [
'inline' => false,
'clientOptions' => [
'autoclose' => true,
'format' => 'yyyy-mm-dd'
]
]);?>
</div>
</div>
</div>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
</div>
<?php ActiveForm::end(); ?>
</div>
<?php
$script = <<<EOD
$(".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");
});
EOD;
$this->registerJs($script, View::POS_END);
?>
it is not clear why you want to save the child item into the same table as the parent items.
please review your code,
$modelAddress->id = $model->id;
you cannot assign the same id to both parent and child if you are saving it in the same table with unique constrain on the id attribute
alternative, u may create a new column [refference_id] to refer to parent id
$modelAddress->refference_id = $model->id;

Yii2: Config global template for all forms fields

I have this:
<?php
use app\models\Location;
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\helpers\ArrayHelper;
use app\models\Role;
?>
<?php $form = ActiveForm::begin(); ?>
<div class="row">
<div class="col-sm-6">
<?= $form->field($model, 'roleId', Yii::$app->formtemplate->fieldTemplate())->dropDownList(ArrayHelper::map(Role::find()->all(), 'id', 'name'), array('prompt' => '-- Select a role --', 'class' => 'form-control select2')); ?>
</div>
<div class="col-sm-6">
<?= $form->field($model, 'published')->checkbox(['label' => ''], true)->label($model->getAttributeLabel('published'), ['class' => 'form-label semibold']); ?>
</div>
</div>
I think this is unefficient since I have to add the template to each field (I know I can add it per form, but checkbox has a different one, any ideas how to set it globally, for all forms? not only for one form?
So far what I have done is to create a component called FormTemplate to avoid writing the template directly in the view, which is good, but I want to set it globally.
<?php
namespace app\components;
use Yii;
use yii\base\Component;
class FormTemplate extends Component {
public function fieldTemplate($option = []) {
$template = [
'template' => '',
'labelOptions' => [ 'class' => 'form-label semibold']
];
$icon = '';
$position = 'right';
if(!empty($option['position'])) {
$position = $option['position'];
}
if(!empty($option['icon'])) {
$icon = $this->_setFieldIcon($option['icon']);
}
$template['template'] = '<div class="form-group">{label}<div class="form-control-wrapper form-control-icon-'.$position.'">{input}'.$icon.'<div class="error">{error}{hint}</div></div></div>';
return $template;
}
private function _setFieldIcon($option) {
switch($option) {
case 'text':
$icon = '<i class="fa fa-text-width"></i>';
break;
case 'password':
$icon = '<i class="fa fa-key" aria-hidden="true"></i>';
break;
default:
$icon = '';
break;
}
return $icon;
}
}
Any ideas?
UPDATE
I have noticed ActiveField is a component, so maybe I could do it on global config? someone has done something like that?
$config = [
'id' => 'basic',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'components' => [
'activeField' => [
'template' => '...'
]
If you want to customize ActiveField objects for all your application, you should use Yii2 dependency injection container, e.g. :
\Yii::$container->set('yii\bootstrap\ActiveField', [
'template' => '...',
]);
Or for ActiveForm :
\Yii::$container->set('yii\bootstrap\ActiveForm', [
'inputTemplate' => '...',
]);
Read more about practical usage of DI container.
You can create
your ActiveField class as :-
<?php
namespace frontend\widgets;
use yii\helpers\ArrayHelper;
use yii\widgets\ActiveField;
class MyActiveField extends ActiveField
{
public $labelOptions = [ 'class' => 'form-label semibold'];
public function init()
{
$position = ArrayHelper::remove($this->options, 'right');
$icon = $this->_setFieldIcon($this->options);
$this->template ='
<div class="form-group">{label}
<div class="form-control-wrapper form-control-icon-'.
$position.'">
{input}'.$icon.
'<div class="error">{error}{hint}
</div>
</div>
</div>';
parent::init();
}
/**
* #param $option array
* #return string HTML
*/
private function _setFieldIcon($option) {
$icon ='';
switch(ArrayHelper::getValue($option ,'icon' ,'')) {
case 'text':
$icon = '<i class="fa fa-text-width"></i>';
break;
case 'password':
$icon = '<i class="fa fa-key" aria-hidden="true"></i>';
break;
}
return $icon;
}
}
And In ActiveForm use your class as : -
<?php $form = ActiveForm::begin([
//change this with your active field class
'fieldClass' => 'frontend\widgets\MyActiveField'
]); ?>
<div class="row">
<div class="col-sm-6">
<?= $form->field($model, 'roleId',[
'options' => ['icon' => '' ,'position' => '']]
)->dropDownList(ArrayHelper::map(Role::find()->all(), 'id', 'name'), [
'prompt' => '-- Select a role --', 'class' => 'form-control select2'
]); ?>
</div>
<div class="col-sm-6">
<?= $form->field($model, 'published' ,['icon' => '' ,'position' => ''])->checkbox(['label' => ''], true)
->label($model->getAttributeLabel('published'), ['class' => 'form-label semibold']); ?>
</div>
</div>
<?php ActiveForm::end(); ?>

Yii 2.0 Loading submodels dynamically -- how to use client-side validation?

This function was build manually, but we want to know if there is a way, using Yii 2, of getting the model JS validation function automatically. We did so just to test.
Question:
How should we do this, without manually build this for each submodel form field?
JavaScript to add dynamic submodels' fields:
var horsePreferredFoodValidateFunction = function(attribute, value, messages) {
yii.validation.required(value, messages, {"message": "mandatory field"});
};
$(window).load(function() {
$('.container').on('click', '#add-horse-preferred-food', function() {
var horsePreferredFoodsCount = (parseInt($('#horsePreferredFoodsCount').val())) + 1;
$.post(
'/horse/add-horse-preferred-food',
{index : horsePreferredFoodsCount},
function(data) {
$('#horsePreferredFoodsCount').val(horsePreferredFoodsCount);
$("#horse-preferred-food-list").append(data);
var horseForm = $('#horse-form');
console.log(horseForm.data('yiiActiveForm'));
How should we do the attributes adding if we have a submodel with many fields?
$(horseForm).yiiActiveForm('add', {
id: 'horsepreferredfood-' + horsePreferredFoodsCount + '-name',
input: '#horsepreferredfood-' + horsePreferredFoodsCount + '-name',
name: 'name',//'[' + horsePreferredFoodsCount + '][name]',
container: '.field-horsepreferredfood-' + horsePreferredFoodsCount + '-name',
validate: horsePreferredFoodValidateFunction,
error: '.help-block.help-block-error'
});
console.log(horseForm.data('yiiActiveForm'));
}
);
});
});
function removeHorsePreferredFood(id) {
$('#div-horse-preferred-food-' + id).remove();
}
Aditional info:
HorsePreferredFood's rules:
public function rules()
{
return [
[['name', 'horse_id'], 'required'],
[['horse_id'], 'integer'],
[['name'], 'string', 'max' => 255]
];
}
The main form (horse/_form.php):
<div class="horse-form">
<?php
$form = ActiveForm::begin(
[
'id' => 'horse-form',
'action' => '/horse/' . (($horse->isNewRecord) ? 'create' : 'update/' . $horse->id),
'errorCssClass' => 'errorMessage',
]
);
?>
<?= $form->field($horse, 'name')->textInput(['maxlength' => 255]) ?>
<?= $form->field($horse, 'age')->textInput() ?>
<!-- HORSE PREFERRED FOODS -->
<fieldset class="fieldset">
<legend>Comidas Preferidas:</legend>
<div id ="horse-preferred-food-list">
<?php
$horsePreferredFoodsCount = count($horsePreferredFoods);
foreach ($horsePreferredFoods as $horsePreferredFoodIndex => $horsePreferredFood) {
$this->renderPartial(
'/horsePreferredFood/_form',
array(
'index' => $horsePreferredFoodIndex,
'horsePreferredFood' => $horsePreferredFood,
'form' => $form,
)
);
echo $form->field($horsePreferredFood, 'name')->textInput(['maxLength' => 255]);
}
?>
</div>
<div class="form-group">
<?php
echo \yii\bootstrap\Button::widget(
[
'label' => 'Adicionar Comida Preferida',
'options' => [
'id' => 'add-horse-preferred-food',
'class' => 'btn-lg',
],
]
);
?>
</div>
</fieldset>
<div class="form-group">
<?= Html::submitButton(
$horse->isNewRecord ?
'Criar' : 'Alterar',
[
'class' => $horse->isNewRecord ?
'btn btn-success' : 'btn btn-primary'
]
); ?>
</div>
<?php
ActiveForm::end();
echo Html::hiddenInput(
'horsePreferredFoodsCount',
$horsePreferredFoodsCount,
[
'id' => 'horsePreferredFoodsCount',
]
);
?>
</div>
The submodel form (horsePreferredFood/_form.php):
<?php
if (isset($isAjaxCall) && ($isAjaxCall)) {
$horsePreferredFood = new \app\models\HorsePreferredFood();
} else {
$isAjaxCall = false;
}
if (! isset($form)) {
$form = \yii\bootstrap\ActiveForm::begin(
[
'id' => 'horse-form',
'enableAjaxValidation' => false,
'enableClientValidation' => true,
]
);
}
?>
<div class="form" id="div-horse-preferred-food-<?php echo $index; ?>">
<fieldset class="fieldset">
<div class="form-group">
<?php
echo \yii\helpers\Html::activeHiddenInput($horsePreferredFood, "[$index]id");
echo $form->field($horsePreferredFood, "[$index]name")->textInput(['maxlength' => 255]);
?>
</div>
</fieldset>
<div class="form-group">
<?php
echo \yii\bootstrap\Button::widget(
[
'label' => 'Remover Comida Preferida',
'options' => [
'id' => "btn-remove-horse-preferred-food-$index",
'class' => 'btn-lg',
'onClick' => "removeHorsePreferredFood($index);",
],
]
);
?>
</div>
</div>
In HorseController, the action invoked via AJAX to return the submodel fields:
public function actionAddHorsePreferredFood() {
$index = $_POST['index'];
$partialView = "/horsePreferredFood/_form";
return $this->renderPartial(
$partialView,
[
'index' => $index,
'isAjaxCall' => true,
]
);
}

Categories