How can I set a date to NULL in Yii? - php

I have a date column that usually takes values as dd.MM.yyyy. Its validation rule in the model's rules() is this:
array('start, end', 'date', 'format' => 'dd.MM.yyyy'),
I'm populating the database from a CSV file, and I'd like to be able to set the date to NULL (i.e. nothing) if the CSV record is empty. So, I'm doing:
if (empty($csv_data)) {
$user->start = new CDbExpression('NULL');
} else {
$user->start = $csv_data;
}
But I get an error that the date format is invalid. Why is that?
The CDateValidator documentation says that the allowEmpty property is true by default, so it should be able to set this to NULL, right? Note that if I just assing the "" string to the date, it'll convert it to a 0000-00-00 00:00:00 timestamp, which is not NULL.

in model rules():
array('start, end', 'date', 'format' => 'dd.MM.yyyy'),
array('start, end', 'default', 'setOnEmpty' => true, 'value' => null),
also,
if (empty($csv_data)) {
$user->start = null;
} ...
should do the trick too.

The trivial fix for this is not to set the value at all during creation:
if (!empty($csv_data)) {
$user->start = $csv_data;
}
This way, the date will not be set and thus appear as empty, which also passes validation.

Assigning a CDbExpression to the field will (and should) never pass validation; the validator allows null but it definitely cannot allow an arbitrary CDbExpression as the value of the field; this should not be surprising.
If you wanted to write null to the database then do it simply with $user->start = null -- there is no reason to involve CDbExpression at all here.
Another approach you could use in case you did need to use CDbExpression would be to tell save not to validate the record and do it manually, as in:
$attributes = $user->attributeNames();
if (empty($csv_data)) {
$user->start = new CDbExpression('NULL');
$attributes = array_diff($attributes, array('start')); // don't validate this
} else {
$user->start = $csv_data;
}
if ($user->validate($attributes)) { // validate only attributes we want here
$user->save(false); // no validation at all here
}

Related

Database data field check before Data insertion

I have a data coming from the HTML Page. And i want to check whether the date and the place values already exists. If they exists, it should throw an error saying Data is already present, if those date and place data is not there it should allow the user to save it.
Here is the code which i have written to save it,
public function StoreSampling(Request $request)
{
$date = Carbon::createFromFormat('d-m-Y', $request->input('date'))->format('Y-m-d');
$doctorname = Input::get('doctorselected');
$product = Input::get('product');
$product= implode(',', $product);
$quantity = Input::get('qty');
$quantity =implode(',',$quantity);
$representativeid = Input::get('representativeid');
//Store all the parameters.
$samplingOrder = new SamplingOrder();
$samplingOrder->date = $date;
$samplingOrder->doctorselected = $doctorname;
$samplingOrder->products = $product;
$samplingOrder->quantity = $quantity;
$samplingOrder->representativeid = $representativeid;
$samplingOrder->save();
return redirect()->back()->with('success',true);
}
I searched some of the Stack over flow pages. And came across finding the existence through the ID And here is the sample,
$count = DB::table('teammembersall')
->where('TeamId', $teamNameSelectBoxInTeamMembers)
->where('UserId', $userNameSelectBoxInTeamMembers)
->count();
if ($count > 0){
// This user already in a team
//send error message
} else {
DB::table('teammembersall')->insert($data);
}
But i want to compare the date and the place. And if they are not present, i want to let the user to save it. Basically trying to stop the duplicate entries.
Please help me with this.
There are very good helper functions for this called firstOrNew and firstOrCreate, the latter will directly create it, while the first one you will need to explicitly call save. So I would go with the following:
$order = SamplingOrder::firstOrNew([
'date' => $date,
'place' => $place
], [
'doctorname' => Input::get('doctorselected'),
'product' => implode(',', Input::get('product')),
'quantity' => implode(',',Input::get('qty')),
'representativeid' => Input::get('representativeid')
]);
if($order->exists()) {
// throw error
return;
}
$order->save();
// success
You need to modify your query to something like this:
$userAlreadyInTeam = SamplingOrder::where('date', $date)
->where('place', $place) // I'm not sure what the attribute name is for this as not mentioned in question
// any other conditions
->exists();
if (userAlreadyInTeam) {
// Handle error
} else {
// Create
}
You do not need to use count() as your only trying to determine existence.
Also consider adding a multi column unique attribute to your database, to guarantee that you don't have a member with the same data and place.
The best way is to use the laravel unique validation on multiple columns. Take a look at this.
I'm presuming that id is your primary key and in the sampling_orders table. The validation rule looks like this:
'date' => ['unique:sampling_orders,date,'.$date.',NULL,id,place,'.$place]
p.s: I do not see any place input in your StoreSampling()

php date format does not insert time in database

I am using the date format in my php controller like
$meter->created_at = date('Y-m-d H:i:s');
But the output I am getting is 2017-11-29 00:00:00
I have also checked this solution.
Update 1
I am inserting some data via excel file.
foreach($final_data as $key=>$value)
{
if($key <= $header_index) continue;
$meter = new Meters;
foreach($value as $k=>$v){
$v = preg_replace('/\s+/', ' ', trim($v));
if(isset($fieldSet[0]['meter_msn']) && $fieldSet[0]['meter_msn']==$k){
$meter->meter_msn =$v."";
// echo $v.", ";
}
if(isset($fieldSet[0]['description']) && $fieldSet[0]['description']==$k){
$meter->description = $v;
}
if (isset($fieldSet[0]['status']) && $fieldSet[0]['status'] == $k) {
$meter->status = $v;
}
if (isset($fieldSet[0]['meter_status']) && $fieldSet[0]['meter_status'] == $k) {
$meter->meter_status = $v;
}
if (isset($fieldSet[0]['historic']) && $fieldSet[0]['historic'] == $k) {
$meter->historic = $v;
}
}
$meter->created_at = date('Y-m-d H:i:s');
if($meter->save())
$ok_count++;
else
$status_arr[] = $meter->errors;
}
Any help would be highly appreciated.
Keep the datatype for date fields like created_at and update_at as int and use TimestampBehaviour in your model.
By default, TimestampBehavior will fill the created_at and updated_at attributes with the current timestamp when the associated AR object is being inserted. it will fill the updated_at attribute with the timestamp when the AR object is being updated. The timestamp value is obtained by time() and whenever and where ever you want to show the date you can format it using php's date() function no need to make it complex when the framework itself can do it via conventional approach.
You might have to make little change for renaming the columns to created_at and updated_at and then include the TimestampBehaviour like below in your model.
use yii\behaviors\TimestampBehavior;
public function behaviors() {
return [
TimestampBehavior::className(),
];
}
Or if you want to use the column names as is you can use the following approach to map you field names
use yii\db\Expression;
public function behaviors()
{
return [
[
'class' => TimestampBehavior::className(),
'createdAtAttribute' => 'create_time',
'updatedAtAttribute' => 'update_time',
'value' => new Expression('NOW()'),
],
];
}
TimestampBehavior also provides a method named touch() that allows you to assign the current timestamp to the specified attribute(s) and save them to the database. For example,
$model->touch('creation_time');

Yii2: get value of attribute that has a behavior

I'm using the TimestampBehaviour like this:
public function behaviors() {
return [
[
'class' => \yii\behaviors\TimestampBehavior::className(),
'createdAtAttribute' => 'usr_date_added',
'updatedAtAttribute' => 'usr_date_modified',
'value' => new \yii\db\Expression('UTC_TIMESTAMP()'),
],
];
}
Then if access it through the authenticated user like this...
Yii::$app->user->identity->usr_date_modified;
...I get the expression instead of the actual value:
object(yii\db\Expression)#38 (2) {
["expression"]=>
string(15) "UTC_TIMESTAMP()"
["params"]=>
array(0) {
}
}
If I do it like this I correctly get the actual value:
$user = app\models\User::findOne(51);
echo $user->usr_date_modified;
Why is that? And how can I get the actual value through the identity property?
The value while not saved in the database holds the information about how this actual value should be created - in your case it uses UTC_TIMESTAMP() MySQL function that will be called at the moment of saving this object into DB.
When you are retrieving object already saved in the database it takes whatever has been saved there (in your case the result of this MySQL function), so it gives you actual timestamp.
Before saving the object there is no point of checking this value.
I realized that I have modified an attribute and then called the save() method. You would think it should then hold the actual value after calling save() but it doesn't - I guess because doing the UPDATE query the database doesn't return the value. You need to call the refresh() method (see documentation) to make another SELECT query to get the new value. So you would need to do it like this:
Yii::$app->user->identity->usr_last_access = gmdate('Y-m-d H:i:s');
Yii::$app->user->identity->save();
Yii::$app->user->identity->refresh();

IS NOT NULL not working in Yii Active Record

I am trying something like this:
public function search() {
$criteria = new CDbCriteria;
$criteria->compare('user_details_id', $this->user_details_id);
$criteria->compare('user_type_id', $this->user_type_id);
$criteria->compare('customer_basics_id', $this->customer_basics_id);
$criteria->compare('user_address_id', $this->user_address_id);
$criteria->compare('user_city_id', $this->user_city_id);
$criteria->compare('is_active', $this->is_active);
$criteria->compare('create_dttm', $this->create_dttm, true);
$criteria->compare('update_dttm', $this->update_dttm, true);
// if condition is working
if (isset($_GET['ulip'])) {
$criteria->addCondition(
"customer_basics_id=" . CustomerBasics::getCustomerBasicsId(Yii::app()->session['user_id']), "AND"
);
$criteria->addCondition("user_city_id IS NULL");
// else condition is working
} else {
$criteria->addCondition(
"customer_basics_id=" . CustomerBasics::getCustomerBasicsId(Yii::app()->session['user_id']), "AND"
);
$criteria->addCondition("user_city_id IS NOT NULL");
}
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
'pagination' => array(
'pageSize' => 10,
),
));
}
Here the issue is if condition is working fine and showing results according to the condition but else part is not working and it returns nothing. I think IS NOT NULL is not working here.
What is the issue ?
The Main reason.
In your database table column(user_city_id), you have Empty values not NULL values.
So your query is unable to operate "IS NULL" and "IS NOT NULL" on the corresponding column.
1. NULL is Special Data Type.
2. Where as Empty means a string/value which is empty.
You can read more here
No need to add operator for first addCondition
For your information, When you are adding a condition to your criteria, no need to add "AND" operator becoz by default "AND" is the operator in addConditon. And no use if you add operation for first addCondition, You should add this for your next addConditions if you have any.
//AND is not required here as AND is default operation.
//This operator (AND) wont help in first condition.
$criteria->addCondition('condition1=1','AND');
//This bellow condition will be concatenate with above condition so, here operator is required.
$criteria->addCondition('condition2=1','OR');
The Solution is
I dont like to disturb default search method in my model. As i'm using MVC Framework, i should follow at least some MVC rules. Otherwise there is no meaning using this MVC. So, accessing $_GET stuff in my Model is not good here. so i'm creating a new method in my Model with two parameters.
function yourModelMethod($isParamExist,$customer_basics_id)
{
$criteria = new CDbCriteria;
$criteria->condition = "customer_basics_id=$customer_basics_id";
if($isParamExist)
{
$criteria->condition='TRIM(user_city_id) =""';
}
else
{
$criteria->condition='TRIM(return_date) !=""';
}
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
));
}
Now, I'm using this model method from my controller
function actionYourController()
{
$model=new MyModel();
$isParamExist=isset($_GET['ulip']);
$customer_basics_id=CustomerBasics::getCustomerBasicsId(Yii::app()->session['user_id']);
$activeData=$model->yourModelMethod($isParamExist,$customer_basics_id);
$this->render('myView',array('activeData'=>$activeData));
}
I hope, it will definitely solve your problem.
I'm not really sure this will really take care of your problem. But at least it adheres to safe coding practices:
public function search()
{
$criteria = new CDbCriteria;
$criteria->compare('user_details_id', $this->user_details_id);
$criteria->compare('user_type_id', $this->user_type_id);
$criteria->compare('user_address_id', $this->user_address_id);
$criteria->compare('is_active', $this->is_active);
$criteria->compare('create_dttm', $this->create_dttm, true);
$criteria->compare('update_dttm', $this->update_dttm, true);
$criteria->compare('customer_basics_id', CustomerBasics::getCustomerBasicsId(Yii::app()->session['user_id']));
if(isset($_GET['ulip']))
$criteria->addCondition('user_city_id IS NULL');
else
$criteria->addCondition('user_city_id IS NOT NULL');
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
'pagination' => array(
'pageSize' => 10,
),
));
}
I've just created table in MySQL, model, crud, based on your fields and your search criteria, plus some test data. In my tests all works, both conditions generate some results.
If your need more information about my tests don't hesitate to ask. I suppose, error is connected with input data, hence I suggest to turn on debug mode, add log component to your config and after that look on sql queries that's yii generate.
Maybe $criteria->addCondition("user_city_id <> '' "); solve the probleme because IS NOT NULL interpreted as true and NULL as false.

Lithium: Run filter after find() to format output

I wanted to specify the output of a field from within my model so I added a date key to my $_schema:
models/Tags.php
<?php
protected $_schema = array(
'id' => array('type' => 'integer', 'key' => 'primary'),
'title' => array('type' => 'string'),
'created' => array('type' => 'integer', 'date' => 'F jS, Y - g:i a'),
'modified' => array('type' => 'integer')
);
?>
I store my time as an unsigned integer in the db (output of time()).
I want my base model to format any field that has the date key for output. I thought the best place to do that would be right after a find:
extensions/data/Model.php
<?php
static::applyFilter('find', function($self, $params, $chain) {
$schema = $self::schema();
$entity = $chain->next($self, $params, $chain);
foreach ($schema as $field => $options) {
if (array_key_exists('date', $options)) {
//format as a date
$params['data'][$field] = $entity->formatDate($field, $options['date']);
}
}
return $entity;
});
public function formatdate($entity, $field, $format, $timezone = 'Asia/Colombo') {
$dt = new \DateTime();
$tz = new \DateTimeZone($timezone);
$dt->setTimestamp($entity->$field);
$dt->setTimezone($tz);
return $dt->format($format);
}
?>
This doesn't seem to be working. When I execute a find all, this filter seems to get hit twice. The first time, $entity contains a count() of the results and only on the second hit does it contain the Records object.
What am I doing wrong? How do I alter this so that simply doing <?= $tag->created; ?> in my view will format the date the way I want? This, essentially, needs to be an 'after filter', of sorts.
EDIT
If I can find a way to access the current model entity object (not the full namespaced path, $self contains that), I can probably solve my problem.
Regardless of a small fix for your after find filter, I would do it differently.
Every time you'll do a find, you'll override your date format, even if you don't want to display it, but only do a business logic like comparing dates etc ...
Since you want to format your output only in your views (we are not talking about formatting on the fly json responses for an API, etc.), why not using a helper method ?
An other way is to add an instance method in your model (or a BaseModel), called created_at(). Then, you will call it from a view with <?= $entity->created_at() ?>
You can still force a format fetched from your $_schema, or pass it as a param, etc ...
A helper seems cleaner as we are talking about presenting data in your views.
I'm reading the OP's problem as the find filter executes twice. If that's right, then why not just check to see if the $entity contains a recordset?

Categories