YII2 submit a form with a given model value - yii2-advanced-app

How to add default value to a model to save?
+--------------+--------------+-
| Field | Type |
+--------------+--------------+-
| id | int(11) | ->auto increment
| grant | double(12,2) |
| lcc | double(12,2) |
| encoded_by | int(11) | ->foreign key from tbl_user
+--------------+--------------+-
here is the html form code.
<?= $form->field($model, 'grant')->textInput() ?>
<?= $form->field($model, 'lcc')->textInput() ?>
the error I get on submit..
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or
update a child row: a foreign key constraint fails
(ncddp.tbl_sp_bub, CONSTRAINT tbl_sp_bub_ibfk_2 FOREIGN KEY
(encoded_by) REFERENCES user (id)) The SQL being executed was:
INSERT INTO tbl_sp_bub (grant, lcc) VALUES (2, 2)
I understand that there should be a value for the encoded by which is the current users id.
I tried this.
<?= $form->field($model, 'grant')->textInput() ?>
<?= $form->field($model, 'lcc')->textInput() ?>
<? $model->encoded_by=yii::$app->user->identity->id ?>
and also this in the controller...
public function actionCreate()
{
$model = new TblSpBub();
$model->encoded_by=yii::$app->user->identity->id;//MY CODE
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['index']);
} else {
return $this->renderAjax('create', [
'model' => $model,
]);
}
}
but to no avail...

There is built-in behavior to solve this called BlameableBehavior. Here is usage for your case. Add this to your model:
public function behaviors()
{
return [
[
'class' => BlameableBehavior::className(),
'createdByAttribute' => 'encoded_by',
'updatedByAttribute' => false, // Set it to false if you need automatically update it on create only
],
];
}
And no need to handle it manually.

Related

Troubleshooting CakePHP form submission

I recently set up the ability to tag posts on my site. I had everything working fine. Then as I was wrapping up I tested all my admin side forms again. The Add Tag form no longer does anything. It doesn't even flash an error or redirect after submission. The page just reloads at the same URL. The only changes to the site I have made since initial testing was move the forms to the admin side of the dev site. Here is some code to hopefully reveal what the mystery is. Also my edit tag form is doing similar thing. It has no flash message but redirects back to the index, like its supposed to but with no changes made to the tag. Ill include the edit code as well.
Add.ctp in src/Template/Admin/Tags/Add.ctp
<div class="tags form large-9 medium-8 columns content">
<?= $this->Form->create($tag) ?>
<div class="form-group">
<fieldset>
<h1 class="page-header">New Tag</h1>
<?php
echo $this->Form->input('name', ['class' => 'form-control']);
?>
</fieldset>
</div>
<?= $this->Form->button(__('Submit'), ['class' => 'btn btn-primary']) ?>
<?= $this->Form->end() ?>
</div>
Here is my Add funciton in my TagsController:
public function add()
{
$this->viewBuilder()->layout('admin');
$tag = $this->Tags->newEntity();
if ($this->request->is('post')) {
$tag = $this->Tags->patchEntity($tag, $this->request->data);
if ($this->Tags->save($tag)) {
$this->Flash->success(__('The tag has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The tag could not be saved. Please, try again.'));
}
$this->set(compact('tag'));
$this->set('_serialize', ['tag']);
}
Here is my Edit funciton in my TagsController:
public function edit($id = null)
{
$this->viewBuilder()->layout('admin');
$tag = $this->Tags->get($id, [
'contain' => []
]);
if ($this->request->is(['patch', 'post', 'put'])) {
$tag = $this->Tags->patchEntity($tag, $this->request->data);
if ($this->Tags->save($tag)) {
$this->Flash->success(__('The tag has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The tag could not be saved. Please, try again.'));
}
$this->set(compact('tag'));
$this->set('_serialize', ['tag']);
}
Edit.ctp in src/Template/Admin/Tags/Edit.ctp
<div class="tags form large-9 medium-8 columns content">
<?= $this->Form->create($tag) ?>
<div class="form-group">
<fieldset>
<h1 class="page-header">Edit Tag</h1>
<?php
echo $this->Form->input('name', array('class' => 'form-control'));
?>
</fieldset>
</div>
<?= $this->Form->button(__('Submit'), ['class' => 'btn btn-primary']) ?>
<?= $this->Form->end() ?>
</div>
Just as a side note. I started getting errors when creating a new post as well.
General error: 1364 Field 'section_id' doesn't have a default value
I did go into my DB and give the field a default value. But then when I fill out the form for a new post again, the error just moves to the next table column. I am assuming they are some how related since they popped up at the same time and because tags and posts are related to each other.
TagsTable:
class TagsTable extends Table
{
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config)
{
parent::initialize($config);
$this->table('tags');
$this->displayField('name');
$this->primaryKey('id');
$this->hasMany('PostsTags', [
'foreignKey' => 'tag_id'
]);
}
/**
* Default validation rules.
*
* #param \Cake\Validation\Validator $validator Validator instance.
* #return \Cake\Validation\Validator
*/
public function validationDefault(Validator $validator)
{
$validator
->integer('id')
->allowEmpty('id', 'create');
$validator
->requirePresence('name', 'create')
->notEmpty('name');
return $validator;
}
}
Tags Entity:
class Tag extends Entity
{
/**
* Fields that can be mass assigned using newEntity() or patchEntity().
*
* Note that when '*' is set to true, this allows all unspecified fields to
* be mass assigned. For security purposes, it is advised to set '*' to false
* (or remove it), and explicitly make individual fields accessible as needed.
*
* #var array
*/
protected $_accessible = [
'*' => false,
'id' => false
];
}
When I place <?php debug($tag); ?> into my add.ctp view this is the out put it gives me:
object(App\Model\Entity\Tag) {
'[new]' => true,
'[accessible]' => [],
'[dirty]' => [],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Tags'
}
Again, in question always post debug pathEntity output, in your case debug($tag), also Tag Entity, your validation code, and how looks your db tags table.
Answer:
General error: 1364 Field 'section_id' doesn't have a default value
This means that you have not passed a value for this field.
You can change that table field to accept null or empty value and/or set default if not passed from application, or make validation in your TagsTable to be sure if submitted data valid before send to db.
After question updated:
protected $_accessible = [
'*' => false, <---- should be true
'id' => false
];
This means that all fields except id are accessible

CakePHP 3 - Form data format for patchEntities()

My question is pretty straightforward: How should form data look like (e.i. what should the name keys look like) to patch multiple entities using patchEntities()?
I have read
Cookbook > Database Access & ORM > Saving Data > Converting Multiple Records, but it does not explicitly mention how to use it.
This structure works for newEntities():
$data = [
'0' => ['field1' => '...', /* ... */],
'1' => ['field1' => '...', /* ... */],
'2' => ['field1' => '...', /* ... */]
];
With a form like:
<?= $this->Form->input('0.field1', /* ... */) ?>
<?= $this->Form->input('1.field1', /* ... */) ?>
...
However, the same structure but with 'id.field1' does not make any changes to entities in patchEntities().
The form data should look the same, with the exception that it should include the records primary keys, so that the marshaller can map the data on the respective records. The leading number there in your form inputs isn't a primary key (id), but just the resulting array index.
The docs could use a little update there to include patchEntities() too, right now there's only a short section hidden in the "Patching HasMany and BelongsToMany" section.
Cookbook > Database Access & ORM > Saving Data > Patching HasMany and BelongsToMany
So the form should look something like:
<?= $this->Form->input('0.id', /* ... */) ?>
<?= $this->Form->input('0.field1', /* ... */) ?>
<?= $this->Form->input('1.id', /* ... */) ?>
<?= $this->Form->input('1.field1', /* ... */) ?>
<!-- ... -->
resulting in a dataset like:
$data = [
'0' => ['id' => '...', 'field1' => '...', /* ... */],
'1' => ['id' => '...', 'field1' => '...', /* ... */],
// ...
];
which can then be patched on the given entities:
$original = $this->Table->find()->toArray();
$patched = $this->Table->patchEntities($original, $data);

Yii2 form input field counts with value from db

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).

Avoiding trigering foreign key constraint when dropdown is not selected

I have a select dropdown using laravel form helper like so:
{!! Form::label('job_id', 'Job Titlle', ['class' => ' control-label']) !!}
{!! Form::select('job_id', $jobs, null, ['class' => 'form-control', 'placeholder' => 'Select']) !!}
The problem is this causes a foreign key constraint when I try to update the model
. I think an empty string is passed and I don't lnow how to stop that from happening.
please help Thanks!
1) Validate the data before sending it off to the database; something like this in your controller will do the trick:
class JobController extends Controller
{
public function store(Request $request)
{
$this->validate($request, [
'job_id' => 'required'
]);
// Store in the database because data is valid
}
}
2) If job_id is not really required, you can avoid the foreign key contraint failure by specifying the column as nullable
In Database table make column null
ALTER mytable MODIFY mycolumn varchar(255) null;
This did the trick for me.
if(isset($request['fk_id']) && empty($request['fk_id'])) {
$request['fk_id'] = null;
}

How to set validation rules for custom CActiveRecord attributes in Yii?

I'm working on a Yii project with a database, that contains a table, where almost all it's data is saved in a field as JSON (it's crazy, but it is so as it is):
id INTEGER
user_id INTEGER
data LONGTEXT
This "JSON field" data has following structure and contains inter alia an image:
{
"id":"1",
"foo":"bar",
...
"data":{
"baz":"buz",
...
}
}
Displaying it is no problem, but now I want to make the data ediable. My form looks like this:
<?php
$form = $this->beginWidget('CActiveForm', array(
'id' => 'my-form',
'htmlOptions' => array('enctype' => 'multipart/form-data'),
'enableAjaxValidation'=>false,
));
?>
<div class="row">
<?php echo $form->labelEx($model, 'foo'); ?>
<?php
echo $form->textField($model, 'foo', array(...));
?>
<?php echo $form->error($model, 'foo'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model, 'baz'); ?>
<?php
echo $form->textField($model, 'data[baz]', array(...));
?>
<?php echo $form->error($model, 'data[baz]'); ?>
</div>
It works. But there are multiple problems, that seem to be caused by the same thing -- that he form fields are not referenced to the model attributes/properties:
When I make fields foo and baz required (public function rules() { return array(array('foo, baz', 'required')); } -- the property $foo is defined) foo bahaves as wished, but baz causes an "foo cannot be blank" error. So I cannot set a data[*] as required.
If the form is not valid and gets reloaded, all the data[*] fields are empty.
The data[*] fields are not marked as required.
Is there a to solve this without to change the datase structure? There will not be a correct way for it, but maybe a workaround.
It's impossible to validate fields in such way. First of all if you are using field in model it must be defined or exist in table for active record. So if you want to validate such structure the only right way to do it:
class Model extends CActiveRecord {
// Define public varialble
public $data_baz;
public function rules(){
return array(
// Add it to rules
array( 'data_baz', 'required' )
);
}
public function attributeLabels(){
return array(
// Add it to list of labels
'data_baz' => 'Some field'
);
}
protected function beforeSave(){
if ( !parent::beforeSave() ) {
return false;
}
// Also you may create a list with names to automate append
$this->data['baz'] = $this->data_baz;
// And serialize data before save
$this->data = serialize( $this->data );
return true;
}
}
And your form should looks like
<div class="row">
<?php echo $form->labelEx($model, 'data_baz'); ?>
<?php echo $form->textField($model, 'data_baz'); ?>
<?php echo $form->error($model, 'data_baz'); ?>
</div>