In my project, I have an aggregation between, let's say, a University model and a Department model: a university have at least one department, while every department belongs to only one university.
I'd like to have a possibility to create a university model instance with some number of department model instances and the exact number of departments is not known in advance (but at least one must exist). So, when creating a university, I'd like to have a page with one default department and an "Add Department" button that would allow me by means of javascript to add any number of departments that I need.
The question is: how should I write the create view page using ActiveForm in order that my POST array has the following structure:
"University" => ["name" => "Sorbonne", "city" => "Paris"],
"Faculty" => [
0 => ["name" => "Medicine", "dean" => "Person A"],
1 => ["name" => "Physics", "dean" => "Person B"],
2 => ["name" => "Mathematics", "dean" => "Person C"],
...
]
that I then pass to Faculty::loadMultiple() method.
I've tried something like this
$form = ActiveForm::begin();
echo $form->field($university, 'name')->textInput();
echo $form->field($university, 'city')->textInput();
foreach ($faculties as $i => $faculty) {
echo $form->field($faculty, "[$i]name")->textInput();
echo $form->field($faculty, "[$i]dean")->textInput()
}
ActiveForm::end();
It works, but when adding new department by means of javascript (I just clone an html node that contains department input fields), I am forced to elaborate the numbers coming from variable $i of the above php script. And this is quite annoying.
Another possibility that I've tried was to get rid of variable $i and write something like
$form = ActiveForm::begin();
echo $form->field($university, 'name')->textInput();
echo $form->field($university, 'city')->textInput();
foreach ($faculties as $faculty) {
echo $form->field($faculty, "[]name")->textInput();
echo $form->field($faculty, "[]dean")->textInput()
}
ActiveForm::end();
In this way, cloning the corresponding node is very simple, but the generated POST array has wrong structure due to [] brackets.
Is it possible to modify the latter approach and to have the required structure of the POST array?
Use Yii2 dynamic form extension:
Installation
The preferred way to install this extension is through composer.
Either run:
composer require --prefer-dist wbraganca/yii2-dynamicform "dev-master"
Or add to the require section of your composer.json file:
"wbraganca/yii2-dynamicform": "dev-master"
Demo page: Nested Dynamic Form
Nested Dynamic Form Demo Source Code:
Source Code - View: _form.php
<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use wbraganca\dynamicform\DynamicFormWidget;
?>
<div class="person-form">
<?php $form = ActiveForm::begin(['id' => 'dynamic- form']); ?>
<div class="row">
<div class="col-sm-6">
<?= $form->field($modelPerson, 'first_name')->textInput(['maxlength' => true]) ?>
</div>
<div class="col-sm-6">
<?= $form->field($modelPerson, 'last_name')->textInput(['maxlength' => true]) ?>
</div>
</div>
<div class="padding-v-md">
<div class="line line-dashed"></div>
</div>
<?php DynamicFormWidget::begin([
'widgetContainer' => 'dynamicform_wrapper',
'widgetBody' => '.container-items',
'widgetItem' => '.house-item',
'limit' => 10,
'min' => 1,
'insertButton' => '.add-house',
'deleteButton' => '.remove-house',
'model' => $modelsHouse[0],
'formId' => 'dynamic-form',
'formFields' => [
'description',
],
]); ?>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Houses</th>
<th style="width: 450px;">Rooms</th>
<th class="text-center" style="width: 90px;">
<button type="button" class="add-house btn btn-success btn-xs"><span class="fa fa-plus"></span></button>
</th>
</tr>
</thead>
<tbody class="container-items">
<?php foreach ($modelsHouse as $indexHouse => $modelHouse): ?>
<tr class="house-item">
<td class="vcenter">
<?php
// necessary for update action.
if (! $modelHouse->isNewRecord) {
echo Html::activeHiddenInput($modelHouse, "[{$indexHouse}]id");
}
?>
<?= $form->field($modelHouse, "[{$indexHouse}]description")->label(false)->textInput(['maxlength' => true]) ?>
</td>
<td>
<?= $this->render('_form-rooms', [
'form' => $form,
'indexHouse' => $indexHouse,
'modelsRoom' => $modelsRoom[$indexHouse],
]) ?>
</td>
<td class="text-center vcenter" style="width: 90px; verti">
<button type="button" class="remove-house btn btn-danger btn-xs"><span class="fa fa-minus"></span></button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php DynamicFormWidget::end(); ?>
<div class="form-group">
<?= Html::submitButton($modelPerson->isNewRecord ? 'Create' : 'Update', ['class' => 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
Source Code - View: _form-rooms.php
<?php
use yii\helpers\Html;
use wbraganca\dynamicform\DynamicFormWidget;
?>
<?php DynamicFormWidget::begin([
'widgetContainer' => 'dynamicform_inner',
'widgetBody' => '.container-rooms',
'widgetItem' => '.room-item',
'limit' => 4,
'min' => 1,
'insertButton' => '.add-room',
'deleteButton' => '.remove-room',
'model' => $modelsRoom[0],
'formId' => 'dynamic-form',
'formFields' => [
'description'
],
]); ?>
<table class="table table-bordered">
<thead>
<tr>
<th>Description</th>
<th class="text-center">
<button type="button" class="add-room btn btn-success btn-xs"><span class="glyphicon glyphicon-plus"></span></button>
</th>
</tr>
</thead>
<tbody class="container-rooms">
<?php foreach ($modelsRoom as $indexRoom => $modelRoom): ?>
<tr class="room-item">
<td class="vcenter">
<?php
// necessary for update action.
if (! $modelRoom->isNewRecord) {
echo Html::activeHiddenInput($modelRoom, "[{$indexHouse}][{$indexRoom}]id");
}
?>
<?= $form->field($modelRoom, "[{$indexHouse}][{$indexRoom}]description")->label(false)->textInput(['maxlength' => true]) ?>
</td>
<td class="text-center vcenter" style="width: 90px;">
<button type="button" class="remove-room btn btn-danger btn-xs"><span class="glyphicon glyphicon-minus"></span></button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
Source Code - Controller
<?php
namespace app\modules\yii2extensions\controllers;
use Yii;
use yii\helpers\ArrayHelper;
use yii\web\NotFoundHttpException;
use yii\web\Response;
use yii\widgets\ActiveForm;
use app\base\Model;
use app\base\Controller;
use app\modules\yii2extensions\models\House;
use app\modules\yii2extensions\models\Person;
use app\modules\yii2extensions\models\Room;
use app\modules\yii2extensions\models\query\PersonQuery;
/**
* DynamicformDemo3Controller implements the CRUD actions for Person model.
*/
class DynamicformDemo3Controller extends Controller
{
/**
* Lists all Person models.
* #return mixed
*/
public function actionIndex()
{
$searchModel = new PersonQuery();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Person model.
* #param integer $id
* #return mixed
*/
public function actionView($id)
{
$model = $this->findModel($id);
$houses = $model->houses;
return $this->render('view', [
'model' => $model,
'houses' => $houses,
]);
}
/**
* Creates a new Person model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
$modelPerson = new Person;
$modelsHouse = [new House];
$modelsRoom = [[new Room]];
if ($modelPerson->load(Yii::$app->request->post())) {
$modelsHouse = Model::createMultiple(House::classname());
Model::loadMultiple($modelsHouse, Yii::$app->request->post());
// validate person and houses models
$valid = $modelPerson->validate();
$valid = Model::validateMultiple($modelsHouse) && $valid;
if (isset($_POST['Room'][0][0])) {
foreach ($_POST['Room'] as $indexHouse => $rooms) {
foreach ($rooms as $indexRoom => $room) {
$data['Room'] = $room;
$modelRoom = new Room;
$modelRoom->load($data);
$modelsRoom[$indexHouse][$indexRoom] = $modelRoom;
$valid = $modelRoom->validate();
}
}
}
if ($valid) {
$transaction = Yii::$app->db->beginTransaction();
try {
if ($flag = $modelPerson->save(false)) {
foreach ($modelsHouse as $indexHouse => $modelHouse) {
if ($flag === false) {
break;
}
$modelHouse->person_id = $modelPerson->id;
if (!($flag = $modelHouse->save(false))) {
break;
}
if (isset($modelsRoom[$indexHouse]) && is_array($modelsRoom[$indexHouse])) {
foreach ($modelsRoom[$indexHouse] as $indexRoom => $modelRoom) {
$modelRoom->house_id = $modelHouse->id;
if (!($flag = $modelRoom->save(false))) {
break;
}
}
}
}
}
if ($flag) {
$transaction->commit();
return $this->redirect(['view', 'id' => $modelPerson->id]);
} else {
$transaction->rollBack();
}
} catch (Exception $e) {
$transaction->rollBack();
}
}
}
return $this->render('create', [
'modelPerson' => $modelPerson,
'modelsHouse' => (empty($modelsHouse)) ? [new House] : $modelsHouse,
'modelsRoom' => (empty($modelsRoom)) ? [[new Room]] : $modelsRoom,
]);
}
/**
* Updates an existing Person model.
* If update is successful, the browser will be redirected to the 'view' page.
* #param integer $id
* #return mixed
*/
public function actionUpdate($id)
{
$modelPerson = $this->findModel($id);
$modelsHouse = $modelPerson->houses;
$modelsRoom = [];
$oldRooms = [];
if (!empty($modelsHouse)) {
foreach ($modelsHouse as $indexHouse => $modelHouse) {
$rooms = $modelHouse->rooms;
$modelsRoom[$indexHouse] = $rooms;
$oldRooms = ArrayHelper::merge(ArrayHelper::index($rooms, 'id'), $oldRooms);
}
}
if ($modelPerson->load(Yii::$app->request->post())) {
// reset
$modelsRoom = [];
$oldHouseIDs = ArrayHelper::map($modelsHouse, 'id', 'id');
$modelsHouse = Model::createMultiple(House::classname(), $modelsHouse);
Model::loadMultiple($modelsHouse, Yii::$app->request->post());
$deletedHouseIDs = array_diff($oldHouseIDs, array_filter(ArrayHelper::map($modelsHouse, 'id', 'id')));
// validate person and houses models
$valid = $modelPerson->validate();
$valid = Model::validateMultiple($modelsHouse) && $valid;
$roomsIDs = [];
if (isset($_POST['Room'][0][0])) {
foreach ($_POST['Room'] as $indexHouse => $rooms) {
$roomsIDs = ArrayHelper::merge($roomsIDs, array_filter(ArrayHelper::getColumn($rooms, 'id')));
foreach ($rooms as $indexRoom => $room) {
$data['Room'] = $room;
$modelRoom = (isset($room['id']) && isset($oldRooms[$room['id']])) ? $oldRooms[$room['id']] : new Room;
$modelRoom->load($data);
$modelsRoom[$indexHouse][$indexRoom] = $modelRoom;
$valid = $modelRoom->validate();
}
}
}
$oldRoomsIDs = ArrayHelper::getColumn($oldRooms, 'id');
$deletedRoomsIDs = array_diff($oldRoomsIDs, $roomsIDs);
if ($valid) {
$transaction = Yii::$app->db->beginTransaction();
try {
if ($flag = $modelPerson->save(false)) {
if (! empty($deletedRoomsIDs)) {
Room::deleteAll(['id' => $deletedRoomsIDs]);
}
if (! empty($deletedHouseIDs)) {
House::deleteAll(['id' => $deletedHouseIDs]);
}
foreach ($modelsHouse as $indexHouse => $modelHouse) {
if ($flag === false) {
break;
}
$modelHouse->person_id = $modelPerson->id;
if (!($flag = $modelHouse->save(false))) {
break;
}
if (isset($modelsRoom[$indexHouse]) && is_array($modelsRoom[$indexHouse])) {
foreach ($modelsRoom[$indexHouse] as $indexRoom => $modelRoom) {
$modelRoom->house_id = $modelHouse->id;
if (!($flag = $modelRoom->save(false))) {
break;
}
}
}
}
}
if ($flag) {
$transaction->commit();
return $this->redirect(['view', 'id' => $modelPerson->id]);
} else {
$transaction->rollBack();
}
} catch (Exception $e) {
$transaction->rollBack();
}
}
}
return $this->render('update', [
'modelPerson' => $modelPerson,
'modelsHouse' => (empty($modelsHouse)) ? [new House] : $modelsHouse,
'modelsRoom' => (empty($modelsRoom)) ? [[new Room]] : $modelsRoom
]);
}
/**
* Deletes an existing Person model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* #param integer $id
* #return mixed
*/
public function actionDelete($id)
{
$model = $this->findModel($id);
$name = $model->first_name;
if ($model->delete()) {
Yii::$app->session->setFlash('success', 'Record <strong>"' . $name . '"</strong> deleted successfully.');
}
return $this->redirect(['index']);
}
/**
* Finds the Person model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* #param integer $id
* #return Person the loaded model
* #throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Person::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}
Related
Key question is, does the \yii\widgets\ListView actually support filters with Pjax, like it's \yii\widgets\GridView counterpart? Here's what I have tried that has led to a duplicate url params issue:
I have a Gii-created search model with a custom param, $userRoleFilter:
<?php
namespace common\models;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use yii\rbac\Item;
/**
* UserSearch represents the model behind the search form of `common\models\User`.
*/
class UserSearch extends User
{
public $userRoleFilter = null;
/**
* {#inheritdoc}
*/
public function rules()
{
return [
[['id', 'flags', 'confirmed_at', 'blocked_at', 'updated_at', 'created_at', 'last_login_at', 'auth_tf_enabled', 'password_changed_at', 'gdpr_consent', 'gdpr_consent_date', 'gdpr_deleted'], 'integer'],
[['username', 'email', 'password_hash', 'auth_key', 'unconfirmed_email', 'registration_ip', 'last_login_ip', 'auth_tf_key', 'userRoleFilter'], 'safe'],
];
}
/**
* {#inheritdoc}
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* #param array $params
*
* #return ActiveDataProvider
*/
public function search($params)
{
$query = User::find();
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 20,
],
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'id' => $this->id,
'flags' => $this->flags,
'confirmed_at' => $this->confirmed_at,
'blocked_at' => $this->blocked_at,
'updated_at' => $this->updated_at,
'created_at' => $this->created_at,
'last_login_at' => $this->last_login_at,
'auth_tf_enabled' => $this->auth_tf_enabled,
'password_changed_at' => $this->password_changed_at,
'gdpr_consent' => $this->gdpr_consent,
'gdpr_consent_date' => $this->gdpr_consent_date,
'gdpr_deleted' => $this->gdpr_deleted,
]);
$query->andFilterWhere(['like', 'username', $this->username])
->andFilterWhere(['like', 'email', $this->email])
->andFilterWhere(['like', 'password_hash', $this->password_hash])
->andFilterWhere(['like', 'auth_key', $this->auth_key])
->andFilterWhere(['like', 'unconfirmed_email', $this->unconfirmed_email])
->andFilterWhere(['like', 'registration_ip', $this->registration_ip])
->andFilterWhere(['like', 'last_login_ip', $this->last_login_ip])
->andFilterWhere(['like', 'auth_tf_key', $this->auth_tf_key]);
if (!empty($params['UserSearch']['userRoleFilter'])) {
$userRoleFilter = $params['UserSearch']['userRoleFilter'];
// inner join to the user role items to filter by assigned role
$query->alias('u')
->innerJoin(
'auth_assignment AS aa',
'u.id = aa.user_id AND aa.item_name = :roleName',
['roleName' => $userRoleFilter]
)
->leftJoin(
'auth_item AS ai',
'aa.item_name = ai.name AND ai.type = :typeRole',
['typeRole' => Item::TYPE_ROLE]
);
}
return $dataProvider;
}
}
Controller method:
/**
* Displays pricing calculation & data exports index.
*
* #return string
*/
public function actionDisplayUsers()
{
$searchModel = new UserSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('display-users', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
And manually wrapped my custom yii\bootstrap\ActiveForm in the Pjax tags:
<?php
/* #var $this yii\web\View */
/* #var $searchModel common\models\UserSearch */
/* #var $dataProvider yii\data\ActiveDataProvider */
use common\components\rbac\UserManager;
use yii\bootstrap\ActiveForm;
use yii\widgets\ListView;
use yii\widgets\Pjax;
$this->title = Yii::t('app', 'Display Users');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-display-users">
<div class="body-content">
<h1><?= $this->title ?></h1>
<?php Pjax::begin([
'options' => [
'id' => 'site-display-users-pjax-container',
],
'enablePushState' => true,
'enableReplaceState' => false,
]); ?>
<div class="well center-block meta-control">
<?php $form = ActiveForm::begin([
'id' => 'displayUsersForm',
'method' => 'get',
'options' => [
'data-pjax' => 1,
],
]); ?>
<div class="row row-grid">
<div class="col-xs-6">
<?=
$form
->field($searchModel, 'userRoleFilter')
->dropDownList(UserManager::getAvailableRoles(), [
'prompt' => 'Select User Role',
'id' => 'userRoleFilter',
])
?>
</div>
<div class="col-xs-6">
</div>
</div>
<?php ActiveForm::end(); ?>
</div>
<div class="row">
<div class="col-lg-12">
<?php
echo ListView::widget([
'dataProvider' => $dataProvider,
'itemView' => '_display-user',
'viewParams' => [
// add params to pass into view here
],
]);
?>
</div>
</div>
<?php Pjax::end(); ?>
</div>
</div>
This works fine and filters the users in the ListView according to the selected role. But it is creating a duplicate url param after each time the filter is changed:
/site/display-users
/site/display-users?UserSearch%5BuserRoleFilter%5D=admin
/site/display-users?UserSearch%5BuserRoleFilter%5D=admin&UserSearch%5BuserRoleFilter%5D=role-importer-user
/site/display-users?UserSearch%5BuserRoleFilter%5D=admin&UserSearch%5BuserRoleFilter%5D=role-importer-user&UserSearch%5BuserRoleFilter%5D=admin
and so on...
I know that I can set the Pjax enablePushState and enableReplaceState values to false and then it does not keep creating history items and modifying the url in the browser, but just sends the same ever-lengthening url in the ajax request...
What can be done? Is there a better way to handle this? A setting I am missing to stop this duplication of get param keys stacking up in the url?
Found out the solution... turns out that the ActiveForm form action parameter needs to be explicitly defined so that this URI is used for each form submission rather than relying on the URL from the address bar.
<?php $form = ActiveForm::begin([
'id' => 'displayUsersForm',
'method' => 'get',
'action' => Url::to(['site/display-users']),
'options' => [
'data-pjax' => 1,
],
]); ?>
See here for more details.
I want to search field in my application. I have penerimaan_table. There are rm_code, quantity, bulan, price fields.
How to show my result like this:
rm_code bulan
k123 7
k123 4
k123 5
When I filter I want just show bulan 7 & 5
k123 7
k123 5
How can I do this?
This is my view:
<?php
use yii\helpers\Html;
use kartik\grid\GridView;
use kartik\export\ExportMenu;
/* #var $this yii\web\View */
/* #var $searchModel backend\models\PenerimaanSearch */
/* #var $dataProvider yii\data\ActiveDataProvider */
$this->title = 'Penerimaan Raw Material';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="penerimaan-index">
<h1><?= Html::encode($this->title) ?></h1>
<?php // echo $this->render('_search', ['model' => $searchModel]); ?>
<p>
<?= Html::a('Tambah Penerimaan Raw Material', ['create'], ['class' => 'btn btn-success']) ?>
<?= Html::a('Import Penerimaan', ['import'], ['class' => 'btn btn-success']) ?>
</p>
<p><font color="red">Deskripsi Barang yang kosong merupakan Barang Baru!!</p></font>
<?php
echo ExportMenu::widget([
'dataProvider' => $dataProvider,
'columns' => [
// 'id',
'rm_code',
'rmCode.deskripsi_barang',
'saldo_awal',
'quantity',
'bulan',
//'price',
'total'
],
'showPageSummary' => true,
]); ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'kartik\grid\SerialColumn'],
// 'id',
'rm_code',
'rmCode.deskripsi_barang',
// 'saldo_awal',
'quantity',
'bulan',
[
'attribute' => 'price',
'label' => 'Price',
'footer' => $myAverage,
],
//'price',
[
'label' => 'Total',
'attribute' => 'total',
'pageSummary' => true,
// 'pageSummaryOptions' => ['id_layanan' => 'deskripsi'],
'value' => function ($model) {
if($model)
return $model->total ;
else
return 0;
}
],
['class' => 'kartik\grid\ActionColumn'],
],
'showPageSummary' => true,
]); ?>
<?php echo"Harga Rata-rata Barang adalah:";
$myAverage = 0;
$myTot =0;
$myCnt = 0;
$data = $dataProvider->getModels();
foreach ($data as $key => $value) {
$myTot += $value['price'];
$myCnt++;
}
if ($myCnt>0){
$myAverage = $myTot/$myCnt;
}
echo $myAverage; // your average displayed herre, you can place it wherever you want.
?>
<?php
$this->registerJs("
// 1. Dapatkan handle dari tabel gridview
table = $('div.grid-view > table');
// 2. Hitung kolom tertentu, pada contoh ini menghitung nilai total pada kolom ke 3
var sum = 0;
table.find('tbody').find('tr').each(function() {
sum += parseInt($(this).find('td:nth-child(4)').text());
});
// 3. Tampilkan hasil perhitungan pada tabel baris terakhir kolom ke 3
table.find('tfoot > tr > td:nth-child(4)').text(sum);
");
?>
</div>
Problem 1: the validators
Within your PenerimaanSearch-model remove the integer-validator for the bulan-field. And add two new ones as follows:
public function rules() {
//...
[['bulan'], 'integer', 'when'=>function($model) {
return strpos($model->bulan, '&') === false;
}],
[['bulan'], 'string', 'when'=>function($model) {
return strpos($model->bulan, '&') !== false;
}],
//...
}
This will only validate your field as an integer when no &is present. Otherwise it will use a string validator. The optimal way would be a regex-v& onle, but I'm sure you'll figure out how to do so.
Problem 2: functionality within the Search model
Again in the BulanSearch you can find the search()-method which is responsible for filtering the result by attributes. Remove all operations concerning the bulan-field and instead use the following:
public function search($params)
{
//...
if (!empty($this->bulan)) {
if (strpos($this->bulan, '&') !== false) {
$bulan = $this->bulan;
} else {
//filter with int
$bulan = explode('&', $this->bulan);
}
$this->andFilterWhere(['bulan'=>$bulan]);
}
//...
}
That should do the trick. The andFilterWhere decides itself whether to use an IN-condition (multiple values) or a regular comparison (single value).
This should do the trick! However, tell me if you have problems. I'll be happy to help!
js setup in Laravel 5.1 project doesn't work.
I can't understand how i can pass my input text so i can compare the texts.
Here is my Controller:
public function getStudentDataTutorschool(){
if (Auth::check()) {
$user = Auth::user();
$user_id = $user->getTutorschoolId(Auth::user()->id);
$tutorschool = Tutorschool::find($user_id)->first();
$student_ids = array();
$students = array();
$results = array();
$finals = array();
$queries = DB::table('student_tutorschool')
->select('student_id')
->where('tutorschool_id', '=', $tutorschool->user_id)
->get();
//Adds in the student_ids the USER_ID
foreach ($queries as $query) {
$student_ids[] = ['id' => $query->student_id];
}
//Αdds in the $students array the id of student
foreach ($student_ids as $student_id) {
$user = User::find($student_id)->first();
$studentdatas = $user->students;
foreach ($studentdatas as $studentdata) {
$students[] = ['id' => $studentdata->id];
}
}
$get = Input::get('search');
foreach ($students as $student) {
$thestudent = Student::find($student)->first();
$results[] = ['name' => $thestudent->name, 'surname' => $thestudent->surname, 'user_id' => $thestudent->user_id];
}
return response()->json($results);
}else{
return redirect('403');
}
}
Here is my Form:
<form method="get">
<div class="col-md-12">
<input name="search" class="scholio-input typeahead" type="text" placeholder="Μαθητές">
</div>
</form>
Here is my Root:
Route::get('tutorchool/getstudentdatadashboardsearch','SearchController#getStudentDataTutorschool');
Here is my localhost/tutorchool/getstudentdatadashboardsearch link return
I've added a textinput field in my production form. The unitprice fills up when I select productname field drop down. But when I'm saving the data, I'm getting following error -
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'unitprice' cannot be null
The SQL being executed was: INSERT INTO `bottle` (`usedate`, `useqty`, `productname`, `bottlename`, `unitprice`) VALUES ('2016-04-21', '12', 'CEFO', 'Enter', NULL)
The last "NULL" is the value for unitprice.
actionCreate in productionController-
public function actionCreate()
{
$model = new Production();
$productname = new Productnames();
$bottle = new Bottle();
$bottlename = new Bottlename();
if ($model->load(Yii::$app->request->post()) && $productname->load(Yii::$app->request->post()))
{
$model->save();
//$bottle->attributes = $model->attributes;
$bottle->usedate = $model->productiondate;
$bottle->useqty = $model->prodqty;
$bottle->productname = $model->productname;
$bottle->bottlename = $productname->bottletype;
$bottle->unitprice = $bottlename->unitprice;
// $employee->emp_mobile = $model->emp_mobile;
$bottle->save();
return $this->redirect(['create']);
} else {
return $this->render('create', [
'model' => $model,
'bottle' => $bottle,
'productname' => $productname,
'bottlename' => $bottlename,
]);
}
}
Production _form
<?php
use yii\helpers\Html;
use yii\helpers\Url;
use yii\widgets\ActiveForm;
use yii\web\View;
use frontend\assets\XyzAsset;
use yii\helpers\ArrayHelper;
use dosamigos\datepicker\DatePicker;
use kartik\select2\Select2;
use frontend\modules\production\models\Productbatch;
use frontend\modules\production\models\Productnames;
use kartik\depdrop\DepDrop;
use yii\helpers\Json;
use frontend\modules\production\models\Bottlename;
//XyzAsset::register($this);
/* #var $this yii\web\View */
/* #var $model frontend\modules\production\models\Production */
/* #var $form yii\widgets\ActiveForm */
?>
<div class="production-form">
<?php $form = ActiveForm::begin(); ?>
<!--<?= Html::a('Select Product', ['/production/productbatch/index'], ['class'=>'btn btn-primary']) ?> -->
<?= $form->field($model, 'productiondate')->widget(
DatePicker::className(), [
// inline too, not bad
'inline' => false,
// modify template for custom rendering
//'template' => '<div class="well well-sm" style="background-color: #fff; width:250px">{input}</div>',
'clientOptions' => [
'autoclose' => true,
'format' => 'yyyy-mm-dd'
]
]);?>
<!-- echo CHtml::button("(+)",array('title'=>"Select Product",'onclick'=>'js:selectproductforproduction();')); -->
<?= $form->field($model, 'productname')->widget(Select2::classname(), [
'data' => ArrayHelper::map(Productnames::find()->all(),'productnames_productname','productnames_productname'),
'language' => 'en',
'options' => ['placeholder' => 'Select Product Name', 'id' => 'catid'],
'pluginOptions' => [
'allowClear' => true
],
]); ?>
<?= $form->field($model, 'batchno')->widget(DepDrop::classname(), [
'options'=>['id'=>'subcat-id'],
'pluginOptions'=>[
'depends'=>['catid'],
'placeholder'=>'Select BatchNo',
'url'=>Url::to(['/production/productbatch/subcat'])
]
]); ?>
<?= $form->field($model, 'prodqty')->textInput() ?>
<?= $form->field($productname, 'bottletype')->textInput() ?>
<?= $form->field($bottlename, 'unitprice')->textInput() ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
<?php
$script = <<< JS
$('#catid').change(function(){
var catid = $(this).val();
$.get('index.php?r=production/productnames/get-for-production',{ catid : catid }, function(data){
//alert(data);
var data = $.parseJSON(data);
$('#productnames-bottletype').attr('value',data.bottletype);
$('#bottlename-unitprice').attr('value',data.bottletype0.unitprice);
});
});
JS;
$this->registerJs($script);
?>
The action to get the data array
public function actionGetForProduction($catid)
{
$bottle = Productnames::find()->with('bottletype0')->where(['productnames_productname'=>$catid])->asArray()->one();
//$bottle -> select(['productnames.productnames_productname','productnames.bottletype','bottlename.unitprice'])->from('Productnames')->leftJoin('bottlename','productnames.bottletype = bottlename.bottlename')->where(['productnames_productname'=>$catid])->limit(1);
echo Json::encode($bottle);
This code works fine except the last unitprice. Please help.
You fotgot to add $bottlename->load(Yii::$app->request->post()) in if condition. So add like as,
if ($model->load(Yii::$app->request->post()) && $productname->load(Yii::$app->request->post()) && $bottlename->load(Yii::$app->request->post())) {
.......
}
Is it possible to compare start and end time in my below form in yii2 client/ajax validation.
My view file code like this:
<?php foreach ($model->weekDaysList as $index => $value) : ?>
<div class="row">
<div class="col-sm-1">
</div>
<div class="col-sm-2">
<?= $form->field($model, "[$index]td_day")->checkbox(['label' => $value]) ?>
</div>
<div class="col-sm-3">
<?= $form->field($model, "[$index]td_from") ?>
</div>
<div class="col-sm-3">
<?= $form->field($model, "[$index]td_to") ?>
</div>
</div>
<?php endforeach; ?>
controller code:
public function actionSchedule()
{
$model = new TimetableDetails();
$model->scenario = 'MultiSchedule';
$model->attributes = Yii::$app->request->get('sd');
if ($model->load(Yii::$app->request->post())) {
if (Yii::$app->request->isAjax) {
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return \yii\widgets\ActiveForm::validate($model);
}
}
if (Yii::$app->request->isAjax) {
return $this->renderAjax('schedule', [
'model' => $model,
]);
} else {
return $this->render('schedule', [
'model' => $model,
]);
}
}
You can define a rule for comparing two dates.
First you need to convert them to integer in order to be able to use integrated validator. The best way to do it to cast date to unix timestamp before validation and to the format you need after validation.
Add these in your model:
public function beforeValidate() {
$this->td_to = strtotime($this->td_to);
$this->td_from = strtotime($this->td_from);
return parent::beforeValidate();
}
public function afterValidate() {
$this->td_to = date(FORMAT, $this->td_to);
$this->td_from = date(FORMAT, $this->td_from);
}
Add new rule inside your rules method
return [
// rules
['td_to', 'compare', 'operator' => '<', 'type' => 'number', 'compareAttribute' => 'td_from', 'whenClient' => 'js:function () { /* validate values with jQuery or js here and if valid return true */ return true; }'],
];
This would work on ajax validation. In order to make client validation you need to add js function which validates the values and assign it to whenClient key of the rule.