Yii2 Cannot insert attribute(FK) with id(PK) - php

i have id there is PK(Auto Inc) from models/order relation with FK id_order from models/preorder, In my CRUD action for example actionCreate, i can't insert attibutes preorder to table because id(PK) from order always null. how do i fix this ?.
here's my controller
$cartPositions = Yii::$app->cart->getPositions();
if (!$cartPositions or $cartPositions === null) {
return $this->redirect(['index']);
}
$dataProvider = new ArrayDataProvider([
'allModels' => $cartPositions,
]);
$model = new Order();
$model_po = new Preorder();
$postData = Yii::$app->request->post();
if ($model->load($postData) && $model_po->load($postData)) {
//model->save(false);
$model->status = 5;
$model_po->id_order = $model->id;
$model->total_cost = Yii::$app->cart->getCost();
$model->date = date('Y-m-d H:i');
$model->data = Yii::$app->cart->getSerialized();
$model_po->name = $model->name;
$model_po->phone = $model->phone;
$model_po->remarks = $model->message;
$model_po->created_at = $model->date;
//$model_po->save();
if (Model::validateMultiple([$model, $model_po]) && $model->save(false) && $model_po->save()) {
Yii::$app->session->setFlash('success', 'Thank You');
Yii::$app->mailer->compose('order/html', [
'model' => $model,
//'model_po' => $model_po,
'dataProvider' => $dataProvider,
])
//->setFrom(Yii::$app->params['email']['from'])
// ->setTo(Yii::$app->params['email']['to'])
// ->setSubject('The site posted a new order')
// ->send();
->setFrom(Yii::$app->params['email']['from'])
->setTo(Yii::$app->params['email']['to'])
->setSubject('The site posted a new Preorder')
->send();
Yii::$app->cart->removeAll();
return $this->render('orderSuccess', [
'model' => $model,
//'model_po' => $model_po,
]);
}
} else
{return $this->render('create_po', [
'model' => $model,
'model_po' => $model_po,
'dataProvider' => $dataProvider,
]);}
}
models/order
public function rules()
{
return [
[['status', 'total_cost', 'date', 'data', 'name', 'phone'], 'required'],
[['code_order'], 'autonumber', 'format'=>'orderNum', 'digit'=>4],
[['status', 'total_cost'], 'integer'],
[['date'], 'safe'],
[['data', 'message'], 'string'],
[['name', 'email', 'phone'], 'string', 'max' => 255]
];
}
model/preorders
public function rules()
{
return [
[['id_order', 'address'], 'required'],
[['id_order'], 'integer'],
[['created_at', 'updated_at'], 'safe'],
[['address', 'remarks'], 'string', 'max' => 500],
[['id_order'], 'exist', 'skipOnError' => true, 'targetClass' => Order::className(), 'targetAttribute' => ['id_order' => 'id']],
];
}
error info :
Integrity constraint violation – yii\db\IntegrityException
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'id_order' cannot be null
The SQL being executed was: INSERT INTO preorder (address, id_order, name, phone, remarks, created_at) VALUES ('', NULL, 'Name', '121324325', '', '2016-07-23 17:01')
Error Info: Array
(
[0] => 23000
[1] => 1048
[2] => Column 'id_order' cannot be null
i was try getPrimaryKey() and last insert id() not work and i was try to remove $modelpo->id_order = $model->id the result is two tables filled but id_order is 0

$model->id is null until successful $model->save() so you should not assign its value to $model_po->id_order before that.
This is a good place for transaction so you can validate data first, then make sure Order is saved and then save Preorder with proper id_order.
Take a look at link() method as well.

why don't you use afterSave()? it will definetely solve your problem

Related

CakePHP Save Primary Key of associated table as Foreign Key of another

So i'm currently figuring out how to add users. Adding an email record then using the Auto-incremented Emails id to assign to email_id in Users table with that id being a dependent.
I'm doing it all from the users controller
public function add()
{
if ($this->request->is('post')) {//specify we are getting PostData
$data = [
//'id' => '111',
'address' => $this->request->data['Emails']['address'],
'created' => Time::now(),
'last_modified' => Time::now(),
'last_contacted' => Time::now() ];
$emails = TableRegistry::get('Emails');
$email = $emails->newEntity($data, [ 'associated' => ['Users']]);
$save = $emails->save($email, ['associated' => 'Users']);
$email_id = $save->id;
$users = TableRegistry::get('Users');
$user = $users->newEntity($this->request->getData(), ['associated' => ['Emails.id']]);
$user->dob = date('Y-m-d', strtotime($user->dob));
$user->email['id'] = $email_id;
$user->is_admin = 'n';
$user->last_login = Time::now();
$user->created = Time::now();
$user->last_modified = Time::now();
if ($emails->save($email, ['associated' => 'Users'])) {
if($users->save($user, ['associated' => 'Emails'])) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('User could not be saved. Please, try again.'));
}
}else {
$this->Flash->error(__('Email save failure, please try again.'));
}
}
$emails = $this->Users->Emails->find('list', ['limit' => 200]);
$this->set(compact('user', 'emails'));
$this->set('_serialize', ['user']);
}
which takes data in from the form:
` Form->create( ) ?>
<?php
echo $this->Form->control('Emails.address', ['required' => true, 'label' => 'Email']);
echo $this->Form->control('Users.password', ['required' => true, 'label' => 'Password']);
echo $this->Form->label('Gender');
echo $this->Form->radio('Users.gender',['m'=>'Male', 'f' => 'Female'] , [ 'required' => true]);
echo $this->Form->control('Users.family_name', [ 'required' => true, 'label' => 'Given Name']);
echo $this->Form->control('Users.given_name', [ 'required' => true, 'label' => 'Family Name']);
echo $this->Form->control('Users.dob', [ //birth date between 1900 and current year
'data-format'=>'d m Y',
'class' => 'dateinput',
'required' => true,
'data-default-date'=> '03-30-1993',
'maxYear' => date('Y') - 17,
]);
echo $this->Form->control('Users.phone', [ 'required' => true]);
?>
</fieldset>
<?= $this->Form->button(__('Submit'), ['class'=>'text-right']) ?>
<?= $this->Form->end() ?>`
SO my problem is that i can create email addresses and make them save - great.
EMails and Users are a hasOne. defined in emails table
`$this->hasOne('Users', [
'foreignKey' => 'emails_id', 'bindingKey' => 'id', ])
->setDependent(true)
->setName('Emails');
`
I just cant seem to take the users input and add that into the table. Errors include:
Cannot marshal data for "Emails" association. It is not associated with "Users" which happens when i only put the hasone in the UsersTable. Same if I only have it in EmailsTable. Dp it when the other model contains the fk
ERROR 1452: Cannot add or update a child row: a foreign key constraint fails which happens when both have hasOne in it and i make the email first. It still says theres no fk relation

Yii2 Date Range Search

i got this error when create date range search in search model.
Integrity constraint violation – yii\db\IntegrityException
SQLSTATE[23000]: Integrity constraint violation: 1052 Column
'created_at' in where clause is ambiguous
here's my model
public $start_date;
public $end_date;
public function rules()
{
return [
[['attachment', 'id_client', 'delete_on', 'created_at', 'created_by', 'updated_at', 'updated_by', 'from_location', 'to_location','printed_at', 'lock','verify','verify_by','approved','approved_by'], 'integer'],
[['policy_num'], 'autonumber', 'format'=>'formatPolicy'],
[['policy_num','premium_policy'], 'string'],
[['start_date','end_date'], 'date', 'format'=>'dd-MM-yyyy'],
[['from_location', 'to_location'], 'string', 'max' => 55],
[['location_address'], 'string', 'max' => 100],
[['attachment'], 'required'],
[['deductible'], 'string', 'max' => 100],
[['lock'], 'default', 'value' => '0'],
[['lock'], 'mootensai\components\OptimisticLockValidator']
];
}
here's my search model
public function rules()
{
return [
[['id', 'policy_num', 'attachment', 'id_client', 'delete_on','created_by', 'updated_by', 'printed_at'], 'integer'],
[['cover_rate'], 'number'],
[['start_date','end_date','created_at','updated_at'], 'date','format'=>'yyyy-mm-dd'],
];
}
public function search2($params)
{
$query = AskPolicy::find();
$query->joinWith(['client'])->where(['id_client'=>Yii::$app->user->identity->id_client]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$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;
}
$query->andFilterWhere([
'id' => $this->id,
'policy_num' => $this->policy_num,
'ask_policy.created_at' => $this->created_at,
'ask_policy.updated_at' => $this->updated_at,
'printed_at' => $this->printed_at,
]);
// $query->andFilterWhere(['>=', 'ask_policy.created_at', $this->start_date]);
// $query->andFilterWhere(['<=', 'ask_policy.created_at', $this->end_date]);
$query->andFilterWhere(['like',"(date_format(FROM_UNIXTIME(`created_at` ), '%Y-%m-%d' ))", $this->start_date])
->andFilterWhere(['like', "(date_format(FROM_UNIXTIME(`updated_at` ), '%Y-%m-%d' ))", $this->end_date]);
return $dataProvider;
}
if i use below code : search start date and end date not working
$query->andFilterWhere(['>=', 'ask_policy.created_at', $this->start_date]);
$query->andFilterWhere(['<=', 'ask_policy.created_at', $this->end_date]);
how to the best way convert integer datetime in Yii2 for date range search ? i was searching but not find tutorial with good explanation.
your query is joined with client and both models has created_at field.
you can set alias for current model with
$query->alias('t');
and alias joined table with
$query->joinWith(['client as cli']);
then if you want to use created_at from main model you can use t.created_at and if you want to use created_at from joined model you can use cli.created_at.
you have not given table alias try like this
$query->andFilterWhere(['like',"(date_format(FROM_UNIXTIME(ask_policy.`created_at` ), '%Y-%m-%d' ))", $this->start_date])
->andFilterWhere(['like', "(date_format(FROM_UNIXTIME(ask_policy.`updated_at` ), '%Y-%m-%d' ))", $this->end_date]);

How to do IS NULL and IS NOT NULL with Yii 2 ActiveRecord?

I have a table which has a field `activated_at` timestamp NULL DEFAULT NULL, which means that it can contain a timestamp or it can be null and it's null by default.
I have another [gii-generated] search model with a following configuration in the search() method:
public function search($params)
{
$query = User::find();
// add conditions that should always apply here
$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;
}
$andFilterWhere = [
'id' => $this->id,
'status' => $this->status,
'role' => $this->role,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'completed_files' => $this->completed_files,
// 'activated_at' => null,
];
if(!isset($_GET['deleted'])) {
$query->where(['deleted_at' => null]);
$andFilterWhere['deleted_at'] = null;
} else if($_GET['deleted'] === 'true') {
$query->where(['not', ['deleted_at' => null]]);
}
// grid filtering conditions
$query->andFilterWhere(
$andFilterWhere
);
$query->andFilterWhere(['like', 'first_name', $this->username])
->andFilterWhere(['like', 'auth_key', $this->auth_key])
->andFilterWhere(['like', 'password_hash', $this->password_hash])
->andFilterWhere(['like', 'password_reset_token', $this->password_reset_token])
->andFilterWhere(['like', 'email', $this->email])
->andFilterWhere(['like', 'first_name', $this->first_name])
->andFilterWhere(['like', 'last_name', $this->last_name]);
if($this->activated || $this->activated === "0") {
#die(var_dump($this->activated));
if($this->activated === '1') {
// this doesn't filter
$query->andFilterWhere(['not', ['activated_at' => null]]);
} else if($this->activated === '0') {
// this doesn't either
$query->andFilterWhere(['activated_at', null]);
}
}
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
return $dataProvider;
}
Yes, I have set the activated property in my class:
public $activated;
And my rules() method is as following:
public function rules()
{
return [
[['id', 'status', 'role', 'created_at', 'updated_at', 'completed_files'], 'integer'],
['activated', 'string'],
[['username', 'first_name', 'last_name', 'auth_key', 'password_hash', 'password_reset_token', 'email', 'deleted_at', 'completed_files', 'activated_at'], 'safe'],
];
}
What I was trying to set in the search() method is to filter on field activated_at depending on the $activated value (see above code):
if($this->activated || $this->activated === "0") {
#die(var_dump($this->activated));
if($this->activated === '1') {
// this doesn't filter
$query->andFilterWhere(['not', ['activated_at' => null]]);
} else if($this->activated === '0') {
// this doesn't either
$query->andFilterWhere(['activated_at', null]);
$andFilterWhere['activated_at'] = null;
}
}
I use it with GridView - every other filter works except this one.
What am I doing wrong here?
Aand how to properly do this sort of queries:
IS NULL something
IS NOT NULL something
With Yii 2's ActiveRecord query builder?
EDIT: Line: if(!isset($_GET['deleted'])) is used for something else and this works normally.
If i understand right you can use andWhere
->andWhere(['not', ['activated_at' => null]])
but andFilterWhere in execute where the related value is not null
from doc http://www.yiiframework.com/doc-2.0/yii-db-query.html
andFilterWhere() Adds an additional WHERE condition to the
existing one but ignores empty operands.
for this expression:
WHERE activated_at IS NULL
try this (it's working):
->andWhere(['is', 'activated_at', new \yii\db\Expression('null')]),
$null = new Expression('NULL');
$query->andFilterWhere(['is not', 'asp_id', $null]);
OR
$query->andFilterWhere(['is', 'asp_id', $null]);
this solution check if column_name is empty or NULL
WHERE (LENGTH(`column_name`) > 0)
->andWhere(['>', 'LENGTH(column_name)', 0]) //check if empty or null
Another variant - check only for NULL
WHERE column_name IS NOT NULL
->andWhere(['IS NOT', 'column_name', null]); // check on null
// ...WHERE (`status` = 10) AND (`type` IS NULL) AND (`info` IS NOT NULL)
$query->where([
'status' => 10,
'type' => null,
])
->andWhere(['not', ['info' => null]]);

ajax validation unique on update

I have ajax validation on unique on create it work fine but when I want to update i cant becouse this show message that this name is already used but i not owerwrite this name it is name from database on update click. Its does not matter whcich record i want to update it always show me message that name is already used. Whan can i do to disable message when i not change my input to name which is in base. Now it is so update action automatically filed my inputs but when i not change anythink i have this error on save
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data'], 'id'=>$model->formName(), 'enableAjaxValidation'=>true, 'validationUrl'=>Url::toRoute('category/validation')]) ?>
My controller:
public function actionValidation(){
$model= new SmCategory;
if(Yii::$app->request->isAjax && $model->load(Yii::$app->request->post()))
{
Yii::$app->response->format='json';
return ActiveForm::validate($model);
}
}
my rules:
public function rules()
{
return [
[['Name'], 'required'],
['Name', 'unique', 'targetClass' => 'common\models\Smcategory', 'message' => 'This name has already been taken.'],
[['Rel_Category', 'IsDeleted'], 'integer'],
[['File'],'file'],
[['Name', 'Label'], 'string', 'max' => 45],
[['Picture'], 'string', 'max' => 255]
];
}
The problem is here :
$model= new SmCategory;
This code is ok for create, not for update since it will not use the existing model for validation, it could be (just an example and assuming id is the primary key) :
public function actionValidation($id = null)
{
$model = $id===null ? new SmCategory : SmCategory::findOne($id);
if(Yii::$app->request->isAjax && $model->load(Yii::$app->request->post()))
{
Yii::$app->response->format='json';
return ActiveForm::validate($model);
}
}
And you could update validationUrl in your view :
$validationUrl = ['category/validation'];
if (!$model->isNewRecord)
$validationUrl['id'] = $model->id;
$form = ActiveForm::begin([
'options' => ['enctype' => 'multipart/form-data'],
'id' => $model->formName(),
'enableAjaxValidation' => true,
'validationUrl' => $validationUrl,
]);

Sorting calculated fields in Yii2 (Grid View)

i am need to sort some fields (asc,desc) in GridView, but same fields are calculated. Look at code below:
SearchModel:
class ObjectSearch extends Object {
use SearchModelTrait;
public function rules()
{
return [
['id', 'integer', 'min' => 1],
];
}
public function search($params)
{
$this->company_id = \Yii::$app->user->identity->companyId;
$query = Object::find()->where(['company_id' => $this->company_id]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => false,
]);
$dataProvider->setSort([
'attributes' => [
'id',
'name',
'lastReportResult' => [
'asc' => ['lastReportResult' =>SORT_ASC ],
'desc' => ['lastReportResult' => SORT_DESC],
'default' => SORT_ASC
],
'reportPercentDiff'
]
]);
if (!($this->load($params,'ObjectSearch') && $this->validate())) {
return $dataProvider;
}
$this->addCondition($query, 'id');
return $dataProvider;
}
Methods in Object model:
public function getLastReportResult()
{
$lastReport = $this->getLastReport();
$message = 0;
if (!empty($lastReport)) {
$statistic = new ReportStatistic($lastReport);
$message = $statistic->getPercent();
}
return $message;
}
/**
* #return int
*/
public function getReportPercentDiff()
{
$lastReport = $this->getLastReport();
$message = 0;
if (!empty($lastReport)) {
$statistic = $lastReport->getReportDiff();
if (!empty($statistic['diff'])) {
$message = $statistic['diff']['right_answers_percent_diff'];
} elseif (!empty($statistic['message'])) {
$message = $statistic['message'];
}
}
return $message;
}
So, by this methods, i am calculating a values of two fields, which are need's sorting. This way doesn't working, i have a Database Exception, because object table hasn't this fields. exception
How to do sorting of this fields ?
Update: I am the author of this answer and this answer is not accurate. Preferred way is to use database view
Add two public properties to ObjectSearch.php and mark it as safe
class ObjectSearch extends Object {
use SearchModelTrait;
public $lastReportResult, $reportPercentDiff;
public function rules()
{
return [
['id', 'integer', 'min' => 1],
[['lastReportResult', 'reportPercentDiff'], 'safe']
];
}
public function search($params)
{
$this->company_id = \Yii::$app->user->identity->companyId;
$query = Object::find()->where(['company_id' => $this->company_id]);
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => false,
]);
$dataProvider->setSort([
'attributes' => [
'id',
'name',
'lastReportResult' => [
'asc' => ['lastReportResult' =>SORT_ASC ],
'desc' => ['lastReportResult' => SORT_DESC],
'default' => SORT_ASC
],
'reportPercentDiff' => [
'asc' => ['reportPercentDiff' =>SORT_ASC ],
'desc' => ['reportPercentDiff' => SORT_DESC],
'default' => SORT_ASC
],
]
]);
if (!($this->load($params,'ObjectSearch') && $this->validate())) {
return $dataProvider;
}
$this->addCondition($query, 'id');
return $dataProvider;
}
Then in index.php (view file in which you are having grid view) add lastReportResult and reportPercentDiff in array of all attributes (list of all attributes ob Object model)
...
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
// your other attribute here
'lastReportResult',
'reportPercentDiff',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
...
For more info you can visit Kartik's blog at Yii
Though this is an old thread, stumbled upon this and tried to find other method to achieve sorting of purely calculated field to no avail... and this post unfortunately is not an answer as well... It just that I feel the need to post it here to give a heads up to those that still looking for the solution so as not to scratch their heads when trying the solution given and still fail.
The given example from documentation or referred links as far as I have tested only works if you have a column within the database schema (whether in the main table or the related tables). It will not work if the virtual attribute/calculated field you create is based on calculating (as an example multiplication of 2 column on the table)
e.g:
table purchase: | purchase_id | product_id | quantity |
table product: | product_id | unit_price |
then, if we use a virtual attribute 'purchase_total' for model 'purchase' which is the multiplication of quantity and unit_price (from the join table of purchase and product on product_id), eventually you will hit an error saying 'purchase_total' column can not be found when you tried to sort them using the method discussed so far.

Categories