I am working on yii2. I have a form in which there are 3 dropdowns.
use kartik\select2\Select2;
<div class="allow-area-form">
<?php $form = ActiveForm::begin(); ?>
<?=
$form->field($model, 'city_name')
->widget(Select2::className(),[
'data' => \common\models\AllowArea::toCityArrayList(),
'options' => ['placeholder'=>'Select Area'],
'pluginOptions' => [
'allowClear' => true,
'multiple' => true
],
]);
?>
<?=
$form->field($model, 'user_id')
->widget(Select2::className(),[
'data' => \app\models\User::toArrayList(),
'options' => ['placeholder'=>'Select a User'],
'pluginOptions' => [
'allowClear' => true
],
]);
?>
<?=
$form->field($model, 'salesman_code')
->widget(Select2::className(),[
'data' => \common\models\AllowArea::toArrayList(),
'options' => ['placeholder'=>'Select A Booker'],
'pluginOptions' => [
'allowClear' => true,
],
]);
?>
<div class="form-group">
<?= Html::submitButton(Yii::t('app', 'Save'), ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
Interface
In, the above the areas can be selected multiple. Users can only be one and a booker is also one. A single area have multiple sub-areas. So when the main area is selected all of its sub-areas are allotted to a user. So for example, if I select 3 Main-Areas and each of them has 10 sub-areas, a single user is allowed 30 sub-areas. For this purpose, I have implemented the following in my controller
public function actionCreate()
{
$model = new AllowArea();
if (isset($_REQUEST['AllowArea'])) {
try {
$m = new AllowArea();
foreach ($_REQUEST['AllowArea']['city_name'] as $a=>$b)
{
$sql = "select AreaCode from area where CityNameFull = '$b'";
$res = Yii::$app->sds->createCommand($sql)->queryAll();
foreach ($res as $r)
{
$m->load(Yii::$app->request->post());
$areacode = $r['AreaCode'];
$query = "select AreaNameFull from area where AreaCode = '$areacode'";
$rst = Yii::$app->sds->createCommand($query)->queryOne();
$areaname = $rst['AreaNameFull'];
$m->area_code = $areacode;
$m->area_name = $areaname;
$m->user_id = $m['user_id'];
$m->user_name = AllowArea::idTouser($m['user_id']);
$m->salesman_code = $m['salesman_code'];
$m->salesman_name = AllowArea::idTousersalesman($m['salesman_code']);
$m->city_name =$b;
$m->created_by = Yii::$app->user->id;
$m->created_at = date('Y-m-d H:i:s');
$m->save();
}
}
}catch (Exception $e) {
return $this->redirect(['index']);
}
return $this->redirect(['index']);
}
return $this->render('create', [
'model' => $model
]);
}
Data
Below is the database table result of a single selected main area.
So if I select ALLAMA IQBAL TOWN-01(ALL-BLOCK) in the main area, all of the 20 sub-areas should be saved. But my controller only saves the last ZEENAT BLOCK AIT sub-area and leaves the rest of the remaining.
I have also checked the data inside the 3rd foreach loop, it does get the first item but saves the last one so it means that all of the 20 sub-areas are inside the loop.
There must be some mistake that my code is not saving the data and I am stuck in it.
How to save all the data?
Any help would be highly appreciated.
You are reusing the same AllowArea model $m so it is adding a single entry to your table and updating it 19 times. Move the $m = new AllowArea() into the loop:
try {
foreach ($_REQUEST['AllowArea']['city_name'] as $a=>$b)
{
$sql = "select AreaCode from area where CityNameFull = ':cityName'";
$res = Yii::$app->sds->createCommand($sql, [':cityName' => $b])->queryAll();
foreach ($res as $r)
{
$m = new AllowArea();
...
As an aside, your code is vulnerable to SQL injection since you aren't validating the value of $b so you should use a prepared statement instead as in the code above or consider using the ActiveRecord functions to fetch the AreaCodes.
Related
I am quite new to Yii 2 and I am currently trying to display data from the Teams model in the User view in a GridView. In Yii1 I used a function with the $data variable. In Yii2 it does not seem to work that way.
I have tried the following in the /user/index.php
<?php
use yii\helpers\Html;
use yii\grid\GridView;
/* #var $this yii\web\View */
/* #var $dataProvider yii\data\ActiveDataProvider */
$this->title = Yii::t('app', 'Users');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="user-index">
<h1><?= Html::encode($this->title) ?></h1>
<p>
<?= Html::a(Yii::t('app', 'Create User'), ['create'], ['class' => 'btn btn-success']) ?>
</p>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
// ['class' => 'yii\grid\SerialColumn'],
// 'iduser',
'username',
'surname',
'givenname',
'email:email',
'mobile',
'admin:boolean',
'language',
// Below is what I have been trying to do so far...
['class' => 'yii\grid\DataColumn',
'label' => 'Teams',
'value' => 'getTeams($data-teams)'
],
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
</div>
<?php
// And this is the function I use. In Yii1 it worked just fine.
function getTeams($data) {
$tmp = "";foreach ($data as $team) {
$tmp .=$team['name'].", ";
}
return trim($tmp, ", ");
}
?>
EDIT
In the User model this is what I have set the relation as followed:
public function getTeamIdteams()
{
return $this->hasMany(Team::className(), ['idteam' => 'team_idteam'])->viaTable('user_has_team', ['user_iduser' => 'iduser']);
}
While in the Team model I have set following relations:
public function getUserIdusers()
{
return $this->hasMany(User::className(), ['iduser' => 'user_iduser'])->viaTable('user_has_team', ['team_idteam' => 'idteam']);
}
I have been looking around but can't seem to find a solution for this one. Basically I simply want the Team to be displayed in which the user is a part of. There is a table called UserHasTeam in which the id's of the teams and users are saved.
I am a little lost on how to go about the entire thing. Any help is greatly appreciated.
I assume you have properly set relation to Team model in User model. See the Relations via Junction Table part of guide if you don't know how to set the relation.
You can use closure in $value property of DataColumn to control what value is used for column.
<?= GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
// ... other columns ...
[
'label' => 'Teams',
'value' => function($model) {
$tmp = "";
foreach ($model->teams as $team) {
$tmp .= $team->name .", ";
}
return trim($tmp, ", ");
}
],
],
]); ?>
You might also want to consider using eager loading to load all related at once instead of running query for each user in grid.
I have 2 select2 dropdownlist the second depends on data from the first one
the first one code :
<?= $form->field($model, 'patient_id')->widget(select2::className(),[
'data'=> arrayhelper::map(patient::find()->all(),'patient_id','patient_name'),
'options'=>['placeholder'=>'select patient Name ... '],
'pluginOptions'=>[
'allowClear'=>true
],
])?>
the second one code :
<?= $form->field($model, 'doctor_id')->widget(select2::className(),[
'data'=> arrayhelper::map(doctors::find()->all(),'doctor_id','doctor_name'),
'options'=>['placeholder'=>'أختر اسم الطبيب '],
'pluginOptions'=>[
'allowClear'=>true
],
])?>
i know the sql code in the second one is :
select doctor_name from doctors
so i need it to be :
SELECT DISTINCT doctor_name from doctors where doctor_id in (SELECT doctor_id from patient_services WHERE patient_id="THE VALUE FROM THE 1st DROPDOWNLIST" )
in the regular dropdownlist it work by like this way Yii2 Lesson - 20 Dependent Drop Down Lists By DoingITeasyChannel but in select2 i didn't find how to do it .
------------------------------- after update ----
as the comments there is DepDrop but i got confused how to use it.
i've changed
<?= $form->field($model, 'patient_id')->widget(Select2::classname(), [
'data' => ArrayHelper::map(patient::find()->asArray()->all(), 'patient_id', 'patient_name')]);
?>
and the other one is :
<?= $form->field($model, 'doctor_id')->widget(DepDrop::classname(), [
'options' => ['placeholder' => 'Select ...'],
'type' => DepDrop::TYPE_SELECT2,
'select2Options'=>['pluginOptions'=>['allowClear'=>true]],
'pluginOptions'=>[
'depends'=>['receipts-doctor_id'], // here i got confused
'url' => Url::to(['/receipts/child']),
'loadingText' => 'Loading child level 1 ...',
]
]);
?>
in controller
public function actionChild() {
$out = [];
if (isset($_POST['depdrop_parents'])) { // what they meaning by depdrop_parents or what i should change it ?
$id = end($_POST['depdrop_parents']);
$list = Account::find()->andWhere(['parent'=>$id])->asArray()->all();
$selected = null;
if ($id != null && count($list) > 0) {
$selected = '';
foreach ($list as $i => $account) {
$out[] = ['id' => $account['id'], 'name' => $account['name']];
if ($i == 0) {
$selected = $account['id'];
}
}
// Shows how you can preselect a value
echo Json::encode(['output' => $out, 'selected'=>$selected]);
return;
}
}
echo Json::encode(['output' => '', 'selected'=>'']);
}
First field (Select2):
<?= $form->field($model, 'patient_id')->widget(Select2::classname(), [
'data' => ArrayHelper::map(patient::find()->asArray()->all(), 'patient_id', 'patient_name')]);
?>
Second field (DepDrop):
<?= $form->field($model, 'doctor_id')->widget(DepDrop::classname(), [
'options' => ['placeholder' => 'Select ...'],
'type' => DepDrop::TYPE_SELECT2,
'select2Options'=> ['pluginOptions' => ['allowClear' => true]],
'pluginOptions'=> [
'depends' => ['receipts-doctor_id'],
'url' => Url::to(['/receipts/child']),
'loadingText' => 'Loading child level 1 ...',
]
]);
?>
Plugin option 'depends' => ['receipts-doctor_id'], shows which element (takes element's ID) must be changed (on click, on select, etc.) in order to send Ajax request and retrieve results from controller. In this case, an element with ID receipts-doctor_id should be present. If you don't know or you want to set ID for parent element, you can use 'options' => ['id' => 'receipts-doctor_id'], for your parent element.
And for controller, you can retrieve value like this:
public function actionChild()
{
$depdropParents = Yii::$app->request->post('depdrop_parents');
if ($depdropParents !== null) {
$value = $depdropParents[0];
// Now your $value contains what was being selected in parent element
}
}
I need to organize multiselect dropdownlist. I tried like this:
<?= $form->field($model, 'receiver_id')->widget(Select2::classname(),
[ 'data' => ArrayHelper::map(User::find()->all(),'id','username'),
'options' =>
['placeholder' => 'Select receivers...', 'multiple' => true],
'pluginOptions' =>
[ 'tags' => true,
'maximumInputLength' => 10
],
]);
?>
In the view it seems correctly, in the textfield receivers appear one by one, but when I press "Send" button it says that Receiver ID must be an integer. How can I solve this issue? I need to duplicate one db record for different receivers which I select using select2 dropdown list. For example, I choose in dropdownlist user1 and user2, "Send" action should work twice accordingly. In the db table named as 'letter' should be two same records with different id and receiver_id.
My actionCreate function in the Controller class:
public function actionCreate()
{
$model = new Letter();
if ($model->load(Yii::$app->request->post())) {
foreach($model->receiver_id as $r_id){
$save = new Letter();
$save->type_id = $model->type_id;
$save->subject = $model->subject;
$save->body = $model->body;
$save->sender_id = $model->sender_id;
$save->start_date = $model->start_date;
$save->end_date = $model->end_date;
$save->receiver_id = $r_id;
$save->save();
}
$model->attachment = UploadedFile::getInstance($model, 'attachment');
$filename = pathinfo($model->attachment , PATHINFO_FILENAME);
$ext = pathinfo($model->attachment , PATHINFO_EXTENSION);
$newFname = $filename.'.'.$ext;
$path=Yii::getAlias('#webroot').'/uploads/';
if(!empty($newFname)){
$model->attachment->saveAs($path.$newFname);
$model->attachment = $newFname;
if($model->save()){
return $this->redirect(['view', 'id' => $model->id]);
}
}
}
return $this->render('create', [
'model' => $model,
]);
}
My IDE says on "$model->receiver_id" that "Expected types array or object, Actual: int"
Thanks in advance.
do receiver_id as array on your model; and
foreach($model->receiver_id as $r_id){
$save = new YourModel();
$save->yourProperty = $model->yourProperty;
....
$save->receiver_id = $r_id;
$save->save();
}
the below coding might have logic error which I cannot identify since I'm not so good in logic.
public function actionSave()
{
//initiate connection
$request = Yii::$app->request;
if (Yii::$app->request->post())
{
//receive from POST, insert into $student
$student=Yii::$app->request->post('sudent');
//receive from POST, insert into $selection
$selection=Yii::$app->request->post('selection');
//looping foreach and set $student as $key
foreach ($student as $key => $value)
{
if($key==0 )
{
$check=$selection[$key];
}
elseif($key)
{
$check=empty($selection[$key])?'':$selection[$key];
}
else{
//don't know what to do here
}
$model=new Attendance();
if($check || $check==0)
{
$model->student_id=$value;
$model->attendance_check=1;
$model->attendance_date=time();
// $model->attendance_reason=$value;
$model->save();
}
else
{
$model->student_id=$value;
$model->attendance_check=0;
$model->attendance_date=time();
// $model->attendance_reason=$value;
$model->save();
}
}
}
else
{
//don't know what to do here
}
}
the flow is, when this controller receive from POST (checked box and some texfields) it will go to foreach and check if the checkbox==1 (means it is ticked) it wil insert to databse. if not, still insert into database but with value 0.
one from 7 checkbox i did not tick. so, in database 6 attendance_check will hold value 1 and 1 attendance_check hold value 0.
what i get? all hold value 1.
what are the error?
Thanks.
--update 22/3/2016--
the form view (named index.php)
<?php
use yii\grid\GridView;
use yii\data\ActiveDataProvide;
use yii\grid\CheckboxColumn;
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\helpers\Url;
//active form start
$form = ActiveForm::begin([
//send to 'key-in-attendance/save'
'action' => Url::to(['key-in-attendance/save']),
]) ?>
<?php
//keluarkan grifview
echo GridView::widget([
'dataProvider' =>$dataProvider,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
'student_name',
'class_id',
//checkbox untuk kehadiran
['class' => 'yii\grid\CheckboxColumn',
'checkboxOptions'=>['style'=>'display: block;margin-right: auto;margin-left: auto;'],
'header' => Html::checkBox('selection_all', false, [
'class' => 'select-on-check-all pull-right',
'label' => '<span class="pull-left">Check Attend Only</span>',//pull left the label
]),
'checkboxOptions' => function ($data, $key, $index, $column)
{
return ['value' => $index];
}
],
//text box utk masukkan sebab tak hadir
[
'class' => 'yii\grid\DataColumn',
'label'=> 'Reason not attend',
'value' => function ($data)
{
return Html::textarea("reason".$data->id);
},
'format'=>'raw'
],
[
'class' => 'yii\grid\DataColumn',
'value' => function ($data)
{
return Html::hiddeninput("sudent[]",$data->id);
},
'format'=>'raw'
],
],
]);
?>
<!-- butang submit dan tutup active form -->
<div class="form-group">
<div class="col-lg-offset-1 col-lg-11">
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>
</div>
</div>
<?php ActiveForm::end() ?>
--update 23/3/2016--
selection that I made
in the database
Select is already going to be indexed by the ID of your data provider, so the sudent array is unnecessary.
The following should work.
public function actionSave()
{
//initiate connection
$request = Yii::$app->request;
if (Yii::$app->request->post())
{
//receive from POST, insert into $selection
$selection=Yii::$app->request->post('selection');
//looping foreach and set $student as key
foreach ($selection as $value => $check)
{
$model=new Attendance();
$model->student_id=$value;
$model->attendance_check=$check ? 1 : 0;
$model->attendance_date=time();
// $model->attendance_reason=$value;
$model->save();
}
}
else
{
//don't know what to do here
}
}
One more thing to consider is that you are creating new Attendance records but responses might already have been recorded, you want to load first.
$model = Attendance::findOne(['student_id'=>$value]);
if(!$model) $model = new Attendance();
Ok, ill explain what i have first,
--A database table called cases.
This holds all of the cases to i need to display in a gridview
--Three tables called Category, Subcategory and ChildCategory
The cases from table case will be linked to a childcategory.
So i have made three DropDownLists that are populated from the individual category tables in the database. For example:
_categories.php
yii\widgets\Pjax::begin(['id' => 'categories']);
$form = ActiveForm::begin();
echo $form->field($searchModel, 'category')
->dropDownList(
ArrayHelper::map($allCategory, 'id', 'name'),
[
'onchange'=>'getSubcategory()',
]
);
//To stop errors, if first category not chosen make subcategory and empty drop down.
$subcategory = array(
"empty" => ""
);
echo $form->field($searchModel, 'subcategory')
->dropDownList(
ArrayHelper::map($subcategory, 'id', 'name'),
[
'onchange'=>'getChildcategory()',
]
);
//To stop errors, if second category not chosen make childcategory and empty drop down.
$childcategory = array(
"empty" => ""
);
echo $form->field($searchModel, 'childcategory')
->dropDownList(
ArrayHelper::map($childcategory, 'id', 'name'),
[
//'onchange'=>'getChildCategory()',
'onchange'=>'submitNow()',
]
);
ActiveForm::end();
yii\widgets\Pjax::end();
So what happens is when the first category is selected it runs "onchange" => getSubcategory. This will basically send an Ajax request to my controller with the value of the selected option. It will then pull back the subcategories where the subcategory_id = the value of the selected option. It then populated the subcategory drop down with this information.
this function is on _categories.php with the category drop downlists above
function getSubcategory(){
//#casesearch-category is the first drop down list
var firstcategory = $('#casesearch-category').val();
var childcategory = document.getElementById('casesearch-childcategory');
childcategory.options.length = 0;
$.ajax({
url: '<?php echo \Yii::$app->getUrlManager()->createUrl('cases/subcategories') ?>',
type: 'POST',
dataType: 'json',
data: {
firstcategory: firstcategory
},
success: function(data) {
var subcategory = document.getElementById('casesearch-subcategory');
//if select is changed again, make the options length 0 so that it gets rid of previous appends.
subcategory.options.length = 0;
for(var i=0; i<data.length; i++){
subcategory.options[i] = new Option (data[i].name);
subcategory.options[i].value = data[i].subcategory_id;
}
subcategory.options.selectedIndex = -1;
if(subcategory.options.length === 1){
getChildcategory();
}
}
});
}
So when this ajax request reaches my controller it does this:
CasesController.php
public function actionSubcategories()
{
if(isset($_POST['firstcategory'])){
$firstcategory = $_POST['firstcategory'];
// SELECT * FROM `subcategory` WHERE `parent_id` = $firstcategory
$subcategory = Subcategory::findSubcategory($firstcategory);
}
return \yii\helpers\Json::encode($subcategory);
}
Okay so that was just a little bit to help you understand the category side of things. Now i have a gridview that is populated from the database when the page is submitted. However as i have done ajax to get my categories i need the gridview to change with pjax when ever the categories are changed.
So in my controller actionIndex is sends through the searchModel and dataprovider for the gridview like so:
CasesController.php
public function actionIndex()
{
$model = new Cases;
$searchModel = new CaseSearch();
$allCategory = Category::find()->all();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'allCategory' => $allCategory,
'model' => $model
]);
}
Then on my index page where it displays the grid view is here::
NOTE::Index.php renders the category dropdownlists seen above _categories.php
<?= $this->render('_categories', [
'model' => $model,
'searchModel' => $searchModel,
'allCategory' => $allCategory
]) ?>
<?php Pjax::begin(['id' => 'cases']) ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
'case_id',
'name',
'judgement_date',
'year',
'neutral_citation',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
<?php Pjax::end() ?>
Ok So here is the part where i am stuck! I assume what i am meant to do is somehow update the searchModel and dataProvider for the gridview but i am unsure how to do this. As if i send an ajax request to controller to change it it will then have to render the page again which defeats the objective.
at the top of _categories.php
function submitNow(){
$.pjax.reload({container:"#cases"}); //Reload GridView
}
This function is called when the last childcategory is selected.I know something will have to happen here to do this but i do not know what.
Can anyone help?
<?php use yii\widgets\Pjax; ?>
<?php Pjax::begin() ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
'case_id',
'name',
'judgement_date',
'year',
'neutral_citation',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
<?php Pjax::end() ?>