Yii2 form input field counts with value from db - forms

I do my project in yii2.
I have form which get data from db to dropdown list(using kartik depdtop widget).
First field is "type_of_goods", depending on "type_of_goods" customer receive "goods".
After they are two text input field with "goods_amount" and "total_cost" of goods depends on "goods_amount" (goods_amount * price).
Customer inputs goods amount he wants to buy, or money amount to which he wants to buy and js script shows him value in another field in both cases.
Price value is in goods table in DB.
Can I get goods_id or some information about goods from "goods"-field(to perform db query and get price and put it into js function), or maybe even price to put it into js script which do things that I wrote above.
How can I realize it? Is it right way to do it?
Code:
View
<?php $form = ActiveForm::begin([
'options' => [
'class' => 'form-horizontal col-lg-11',
'enctype' => 'multipart/form-data'
],
]); ?>
<?= $form->field($type_of_goods, 'id')
->dropDownList(
ArrayHelper::map(Type_of_goods::find()->all(), 'id', 'name'),
['id'=>'type_of_goods_id']
);
?>
<?= $form->field($goods, 'id')->widget(DepDrop::classname(), [
'options' => ['id' => 'id'],
'pluginOptions' => [
'depends' => ['type_of_goods_id'],
'placeholder' => 'Choose your goods',
'url' => \yii\helpers\Url::to(['goods/goods-dep-type'])
]
]);
?>
<?= $form->field($goods, 'price')->textInput();
?>
<?= $form->field($order, 'amount')->textInput();
?>
<?php ActiveForm::end(); ?>
Controller:
public function actionGoodsDepType()
{
$out = [];
if (isset($_POST['depdrop_parents'])) {
$parents = $_POST['depdrop_parents'];
if ($parents != null) {
$game_id = $parents[0];
$out = \app\models\Goods::gelGoodsList($goods_type_id);
echo Json::encode(['output' => $out, 'selected' => '']);
return;
}
}
echo Json::encode(['output' => '', 'selected' => '']);
}
Model:
public static function gelGoodsList($type_id)
{
$query = self::find()
->where(['type_id' => $type_id])
->select(['id', 'name'])->asArray()->all();
return $query;
}
public static function getPrice($id)
{
$query = self::find()
->where(['id' => $id])
->select(['price'])->asArray()->one();
return $query;
}

You would need to create an AJAX request each time user inputs new value. This might work well (place at the bottom of the view file):
$this->registerJs("
$('#Type_of_goods-price').on('change', function() { // Should be ID of a field where user writes something
$.post('index.php?r=controller/get-price', { // Controller name and action
input_price: $('#Type_of_goods-price').val() // We need to pass parameter 'input_price'
}, function(data) {
total_price = $.parseJSON(data)['result']; // Let's say, controller returns: json_encode(['result' => $totalPrice]);
$('#Type_of_goods-total').value = total_price; // Assign returned value to a different field that displays total amount of price
}
});
");
You only need to set correct elements' correct IDs and write a method in controller (using this example, it would be controller controller name and actionGetPrice method name).

Related

Invalid argument supplied for foreach() in \zendframework\zend-form\src\Form.php on line 773 when using $form->prepare() Zend Framework 3

I'm newbie ZF3
I had finished my searching form and implementing the autocomplete suggestion using zend framework 3 but I got an error message warning : in Invalid argument supplied for foreach() in \zendframework\zend-form\src\Form.php on line 773
Then I do remove $form->prepare(); and message error doesn't appear but once the button is clicked and verified then the error message back
my addkelas.phtml
<?php
$form->setAttribute('action',$this->url('kelasbimbingan',['action'=>'addkelas']));
$form->prepare();
echo $this->form()->openTag($form);
?>
<p>Type students names:</p>
<div id="prefetch">
<?= $this->formElement($form->get('nama')); ?>
</div>
<br>
<div>
<?php
echo $this->formSubmit($form->get('submit'))."<br>";
echo $this->form()->closeTag();
?>
</div>
<?php
echo "<script language='javascript'> var country_list =".$data.";</script>";
$this->headScript()
->appendFile('/js/typeahead.bundle.js', 'text/javascript')
->appendFile('/js/bloodhound.js', 'text/javascript')
->appendFile('/js/autocompletejavascript.js', 'text/javascript');
?>
my addkelasAction()
public function addkelasAction()
{
$form = new CarimahasiswaForm();
$data = \Zend\Json\Json::encode($this->getMahasiswaData());
if ($this->getRequest()->isPost()) {
//get data, fill in the form with POST data
// Fill in the form with POST data
$dataMahasiswa = $this->params()->fromPost();
$form->setData($dataMahasiswa);
//validate form
// print_r($data);
if($form->isValid()){
$dataMahasiswa = $form->getData();
print_r($dataMahasiswa);
}
}
return new ViewModel(['form'=>$form,'data'=>$data]);
}
and my form
<?php
namespace Skripsiku\Form;
use Zend\Form\Form;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilter;
use Zend\Form\Element;
class CarimahasiswaForm extends Form
{
public function __construct()
{
parent::__construct('tambah-kelas');
$this->getElements();
$this->add([
'name'=>'submit',
'type'=>'submit',
'attributes'=>[
'value'=>'Cari Mahasiswa',
'id'=>'SaveButton',
'class'=>'btn btn-info btn-md',
],
]);
}
public function getElements()
{
$this->add([
'name'=>'nama',
'type'=>'text',
'attributes'=>[
'placeholder'=>'Ketik Nama Mahasiswa',
'class'=>'typeahead',
],
'options'=>[
'label'=>'Cari Mahasiswa :',
]
]);
}
private function addInputFilter()
{
// Create main input filter
$inputFilter = new InputFilter();
$this->setInputFilter($inputFilter);
$inputFilter->add([
'name' => 'nama',
'required' => true,
'filters' => [
['name' => 'StringTrim'],
['name' => 'StripTags'],
],
'validators' => [
[
'name' => 'StringLength',
'options' => [
'encoding' => 'UTF-8',
'min' => 5,
'max' => 255,
],
],
],
]);
}
}
?>
please suggest..
Sorry for my bad English.. Thanks
Zend Form extends Fieldset which declares a getElements function
public function getElements()
{
return $this->elements;
}
The line which throws the exception/error tries to loop through a value retrieved from:
$elements = $fieldset->getElements();
You might think that $fieldset != $this in the given context, but zend handles forms like fieldset:
$this->attachInputFilterDefaults($this->filter, $this);
Solution: Don't overwrite the getElements function, either rename your current function or add that piece of code to your constructor.

yii2: Undefined variable: model

I am just starting Yii2 framework.
I want to create a dropdown list which is 1 to 10 and a submit button
Once select the option and click the button should go to next page to show the number I choose.
In my view file : index.php
use yii\widgets\ActiveForm;
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'QTY')->dropDownList(range(1, 10)) ?>
<?= Html::submitButton('Buy', ['class' => 'btn btn-primary']) ?>
<?php ActiveForm::end(); ?>
Then when I go to the page it gave me 'Undefined variable: model' at dropdown list there.
What should I do to make it correct?
And what is the different between Html and CHtml?
Thanks.
this code is form.php not index.php.
because we can see, there are active form.
your model is undefined maybe you write the wrong code
this is example of controller index.php
public function actionIndex()
{
$searchModel = new PersediaanBarangSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
Html and Chtml is the same
in Yii1=CHtml
in Yii2=Html
This is ment to be pagination? If yes use default functionality of the grid view.
This goes to controller:
$query = Post::find()->where(['status' => 1]);
$provider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
'sort' => [
'defaultOrder' => [
'created_at' => SORT_DESC,
'title' => SORT_ASC,
]
],
]);
return $this->render('path_to_view',['dataProvider'=>$provider]);
Read more
This goes to view:
GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
'id',
'name',
'created_at:datetime',
// ...
],
]);
Read more
Actually you model is not loaded, Please check below example.
public function actionIndex($id = Null)
{
$data=array();
$data['model'] = !empty($id) ? \app\models\YourModel::findOne($id) : new \app\models\YourModel();
return $this->render('index', $data);
}

Yii2 remember sort option after submit the searchForm

I use an ActiveForm in Yii2 for my SearchModel. After click on search button the form fields remember previous values but SorterDropdown is refreshed.
<?php echo SorterDropdown::widget(['sort' => $dataProvider->sort,
'label' => $model->sortedBy($dataProvider->sort->attributes),])
?>
SorterDropdown is just a wrapper of ButtonDropdown.
How can I forse the SorterDropdown to remember sort order (and show it) after the form submition?
class SorterDropdown extends LinkSorter
{
public $label;
protected function renderSortLinks()
{
$attributes = empty($this->attributes) ? array_keys($this->sort->attributes) : $this->attributes;
$links = [];
foreach ($attributes as $name) {
$links[] = Html::tag('li', $this->sort->link($name, ['tabindex' => '-1']));
}
if (empty($this->label))
$this->label = 'Sort';
return \yii\bootstrap\ButtonDropdown::widget([
'encodeLabel' => false,
'label' => $this->label,
'dropdown' => [
'items' => $links,
],
]);
}
You should add some class to li element, which indicate active state of it and special style in CSS file.

Load Form from module into custom page template

I have successfully added my own form (from the same module) into my custom template, but now I wish to load the taxonomy add term form (used by ubercart I think for product categories in the catalog vocab) into my template.
I have gotten this far with my module - filename simpleadmin.module
/**
* #file
* A module to simplify the admin by replacing add/edit node pages
*/
function simpleadmin_menu() {
$items['admin/products/categories/add'] = array(
'title' => 'Add Category',
'page callback' => 'simpleadmin_category_add',
'access arguments' => array('access administration pages'),
'menu_name' => 'menu-store',
);
return $items;
}
function simpleadmin_category_add() {
module_load_include('inc', 'taxonomy', 'taxonomy.admin');
$output = drupal_get_form('taxonomy_form_term');
return theme('simpleadmin_category_add', array('categoryform' => $output));
}
function simpleadmin_theme() {
return array(
'simpleadmin_category_add' => array(
'template' => 'simpleadmin-template',
'variables' => array('categoryform' => NULL),
'render element' => 'form',
),
);
}
?>
And as for the theme file itself - filename simpleadmin-template.tpl.php, only very simple at the moment until I get the form to load into it:
<div>
This is the form template ABOVE the form
</div>
<?php
dpm($categoryform);
print drupal_render($categoryform);
?>
<div>
This is the form template BELOW the form
</div>
Its telling me that it is
Trying to get property of non-object in taxonomy_form_term()
and throwing up an error. Should I be using node_add() and passing the nodetype?
To render a taxonomy term form, the function should be able to know the vocabulary to which it belongs to. Otherwise how would it know which form to show? I think this is the proper way to do it.
module_load_include('inc', 'taxonomy', 'taxonomy.admin');
if ($vocabulary = taxonomy_vocabulary_machine_name_load('vocabulary_name')) {
$form = drupal_get_form('taxonomy_form_term', $vocabulary);
return theme('simpleadmin_category_add', array('categoryform' => $form));
}
To redirect your form use hook_form_alter
function yourmodule_form_alter(&$form, &$form_state, $form_id) {
//get your vocabulary id or use print_r or dpm for proper validation
if($form_id == 'taxonomy_form_term' && $form['#vocabulary']['vid'] = '7' ){
$form['#submit'][] = 'onix_sections_form_submit';
}
}
function yourmodule_form_submit($form, &$form_state) {
$form_state['redirect'] = 'user';
}

Yii CJuiAutoComplete for Multiple values

I am a Yii Beginner and I am currently working on a Tagging system where I have 3 tables:
Issue (id,content,create_d,...etc)
Tag (id,tag)
Issue_tag_map (id,tag_id_fk,issue_id_fk)
In my /Views/Issue/_form I have added a MultiComplete Extension to retrieve multiple tag ids and labels,
I have used an afterSave function in order to directly store the Issue_id and the autocompleted Tag_ids in the Issue_tag_map table, where it is a HAS_MANY relation.
Unfortunately Nothing is being returned.
I wondered if there might be a way to store the autocompleted Tag_ids in a temporary attribute and then pass it to the model's afterSave() function.
I have been searching for a while, and this has been driving me crazy because I feel I have missed a very simple step!
Any Help or advices of any kind are deeply appreciated!
MultiComplete in Views/Issue/_form:
<?php
echo $form->labelEx($model, 'Tag');
$this->widget('application.extension.MultiComplete', array(
'model' => $model,
'attribute' => '', //Was thinking of creating a temporary here
'name' => 'tag_autocomplete',
'splitter' => ',',
'sourceUrl' => $this->createUrl('Issue/tagAutoComplete'),
// Controller/Action path for action we created in step 4.
// additional javascript options for the autocomplete plugin
'options' => array(
'minLength' => '2',
),
'htmlOptions' => array(
'style' => 'height:20px;',
),
));
echo $form->error($model, 'issue_comment_id_fk');
?>
AfterSave in /model/Issue:
protected function afterSave() {
parent::afterSave();
$issue_id = Yii::app()->db->getLastInsertID();
$tag; //here I would explode the attribute retrieved by the view form
// an SQL with two placeholders ":issue_id" and ":tag_id"
if (is_array($tag))
foreach ($tag as $tag_id) {
$sql = "INSERT INTO issue_tag_map (issue_id_fk, tag_id_fk)VALUES(:issue_id,:tag_id)";
$command = Yii::app()->db->createCommand($sql);
// replace the placeholder ":issue_id" with the actual issue value
$command->bindValue(":issue_id", $issue_id, PDO::PARAM_STR);
// replace the placeholder ":tag_id" with the actual tag_id value
$command->bindValue(":tag_id", $tag_id, PDO::PARAM_STR);
$command->execute();
}
}
And this is the Auto Complete sourceUrl in the Issue model for populating the tags:
public static function tagAutoComplete($name = '') {
$sql = 'SELECT id ,tag AS label FROM tag WHERE tag LIKE :tag';
$name = $name . '%';
return Yii::app()->db->createCommand($sql)->queryAll(true, array(':tag' => $name));
actionTagAutoComplete in /controllers/IssueController:
// This function will echo a JSON object
// of this format:
// [{id:id, name: 'name'}]
function actionTagAutocomplete() {
$term = trim($_GET['term']);
if ($term != '') {
$tags = issue::tagAutoComplete($term);
echo CJSON::encode($tags);
Yii::app()->end();
}
}
EDIT
Widget in form:
<div class="row" id="checks" >
<?php
echo $form->labelEx($model, 'company',array('title'=>'File Company Distrubution; Companies can be edited by Admins'));
?>
<?php
$this->widget('application.extension.MultiComplete', array(
'model' => $model,
'attribute' => 'company',
'splitter' => ',',
'name' => 'company_autocomplete',
'sourceUrl' => $this->createUrl('becomEn/CompanyAutocomplete'),
'options' => array(
'minLength' => '1',
),
'htmlOptions' => array(
'style' => 'height:20px;',
'size' => '45',
),
));
echo $form->error($model, 'company');
?>
</div>
Update function:
$model = $this->loadModel($id);
.....
if (isset($_POST['News'])) {
$model->attributes = $_POST['News'];
$model->companies = $this->getRecordsFromAutocompleteString($_POST['News']
['company']);
......
......
getRecordsFromAutocompleteString():
public static cordsFromAutocompleteString($string) {
$string = trim($string);
$stringArray = explode(", ", $string);
$stringArray[count($stringArray) - 1] = str_replace(",", "", $stringArray[count($stringArray) - 1]);
$criteria = new CDbCriteria();
$criteria->select = 'id';
$criteria->condition = 'company =:company';
$companies = array();
foreach ($stringArray as $company) {
$criteria->params = array(':company' => $company);
$companies[] = Company::model()->find($criteria);
}
return $companies;
}
UPDATE
since the "value" porperty is not implemented properly in this extension I referred to extending this function to the model:
public function afterFind() {
//tag is the attribute used in form
$this->tag = $this->getAllTagNames();
parent::afterFind();
}
You should have a relation between Issue and Tags defined in both Issue and Tag models ( should be a many_many relation).
So in IssueController when you send the data to create or update the model Issue, you'll get the related tags (in my case I get a string like 'bug, problem, ...').
Then you need to parse this string in your controller, get the corresponding models and assigned them to the related tags.
Here's a generic example:
//In the controller's method where you add/update the record
$issue->tags = getRecordsFromAutocompleteString($_POST['autocompleteAttribute'], 'Tag', 'tag');
Here the method I'm calling:
//parse your string ang fetch the related models
public static function getRecordsFromAutocompleteString($string, $model, $field)
{
$string = trim($string);
$stringArray = explode(", ", $string);
$stringArray[count($stringArray) - 1] = str_replace(",", "", $stringArray[count($stringArray) - 1]);
return CActiveRecord::model($model)->findAllByAttributes(array($field => $stringArray));
}
So now your $issue->tags is an array containing all the related Tags object.
In your afterSave method you'll be able to do:
protected function afterSave() {
parent::afterSave();
//$issue_id = Yii::app()->db->getLastInsertID(); Don't need it, yii is already doing it
foreach ($this->tags as $tag) {
$sql = "INSERT INTO issue_tag_map (issue_id_fk, tag_id_fk)VALUES(:issue_id,:tag_id)";
$command = Yii::app()->db->createCommand($sql);
$command->bindValue(":issue_id", $this->id, PDO::PARAM_INT);
$command->bindValue(":tag_id", $tag->id, PDO::PARAM_INT);
$command->execute();
}
}
Now the above code is a basic solution. I encourage you to use activerecord-relation-behavior's extension to save the related model.
Using this extension you won't have to define anything in the afterSave method, you'll simply have to do:
$issue->tags = getRecordsFromAutocompleteString($_POST['autocompleteAttribute'], 'Tag', 'tag');
$issue->save(); // all the related models are saved by the extension, no afterSave defined!
Then you can optimize the script by fetching the id along with the tag in your autocomplete and store the selected id's in a Json array. This way you won't have to perform the sql query getRecordsFromAutocompleteString to obtain the ids. With the extension mentioned above you'll be able to do:
$issue->tags = CJSON::Decode($_POST['idTags']);//will obtain array(1, 13, ...)
$issue->save(); // all the related models are saved by the extension, the extension is handling both models and array for the relation!
Edit:
If you want to fill the autocomplete field you could define the following function:
public static function appendModelstoString($models, $fieldName)
{
$list = array();
foreach($models as $model)
{
$list[] = $model->$fieldName;
}
return implode(', ', $list);
}
You give the name of the field (in your case tag) and the list of related models and it will generate the appropriate string. Then you pass the string to the view and put it as the default value of your autocomplete field.
Answer to your edit:
In your controller you say that the companies of this model are the one that you added from the Autocomplete form:
$model->companies = $this->getRecordsFromAutocompleteString($_POST['News']
['company']);
So if the related company is not in the form it won't be saved as a related model.
You have 2 solutions:
Each time you put the already existing related model in you autocomplete field in the form before displaying it so they will be saved again as a related model and it won't disapear from the related models
$this->widget('application.extensions.multicomplete.MultiComplete', array(
'name' => 'people',
'value' => (isset($people))?$people:'',
'sourceUrl' => array('searchAutocompletePeople'),
));
In your controller before calling the getRecordsFromAutocompleteString you add the already existing models of the model.
$model->companies = array_merge(
$model->companies,
$this->getRecordsFromAutocompleteString($_POST['News']['company'])
);