Hi there everybody,
I have a behavior class that basically adapt the date format typed by the users (‘dd/MM/yyyy’) with the expected mysql date format that is (‘yyyy-MM-dd’) the code is the following:
<?php
namespace app\behaviors;
use yii\db\ActiveRecord;
use yii\base\Behavior;
use yii\base\Exception;
/**
* Convert date attribute with the expected mysql date format
*
* @author Raffaele Tosti <raffaele AT digisin DOT it>
*/
class DateFormatBehavior extends Behavior {
/**
* @var array Attributes list to convert
*/
public $attributes = [];
/**
* @inheritdoc
*/
public function attach($owner) {
parent::attach($owner);
if(!$this->owner instanceof ActiveRecord) {
throw new Exception("This behavior is applicable only on classes that belongs or extends ActiveRecord");
}
}
/**
* @inheritdoc
*/
public function events() {
return [
ActiveRecord::EVENT_BEFORE_INSERT => 'InsertDateFormat',
ActiveRecord::EVENT_BEFORE_UPDATE => 'UpdateDateFormat',
ActiveRecord::EVENT_AFTER_FIND => 'FindDateFormat'
];
}
/**
* Convert the format before insertion
*/
public function InsertDateFormat() {
foreach ($this->attributes as $attribute) {
if(isset($this->owner->{$attribute})) {
$this->owner->{$attribute} = str_replace('/', '-', $this->owner->{$attribute});
$this->owner->{$attribute} = \Yii::$app->formatter->asDate($this->owner->{$attribute}, 'yyyy-MM-dd');
}
}
}
/**
* Convert the format before update
*/
public function UpdateDateFormat($event) {
foreach ($this->attributes as $attribute) {
if(isset($this->owner->{$attribute})) {
$old_value = $this->owner->getOldAttribute($attribute);
if($old_value != $this->owner->{$attribute}){
$this->owner->{$attribute} = \Yii::$app->formatter->asDate(str_replace('/', '-', $this->owner->{$attribute}), 'yyyy-MM-dd');
}
}
}
}
/**
* Convert the format after the model is found
*/
public function FindDateFormat() {
foreach ($this->attributes as $attribute) {
if(isset($this->owner->{$attribute})) {
$this->owner->{$attribute} = \Yii::$app->formatter->asDate($this->owner->{$attribute});
}
}
}
}
if I type and invalid date \Yii::$app->formatter->asDate() method throw two Exceptions that are the following:
[b]
Caused by: Invalid Parameter – yii\base\InvalidParamException
‘78/98/7987’ is not a valid date time value: DateTime::__construct(): Failed to parse time string (78/98/7987) at position 0 (7): Unexpected character
Array
(
[warning_count] => 0
[warnings] => Array
(
)
[error_count] => 5
[errors] => Array
(
[0] => Unexpected character
[4] => Double date specification
[7] => Unexpected character
[8] => Unexpected character
[9] => Unexpected character
)
)
in /var/www/apache/plurima.snake.lappi.virt/htdocs/vendor/yiisoft/yii2/i18n/Formatter.php at line 698
↵
Caused by: Exception
DateTime::__construct(): Failed to parse time string (78/98/7987) at position 0 (7): Unexpected character
in /var/www/apache/plurima.snake.lappi.virt/htdocs/vendor/yiisoft/yii2/i18n/Formatter.php at line 687
[/b]
I would like to catch such exception and deny the insertion/update of the model, possibly showing a validation error to the user. Since the introduction of \Trowable PHP7 predefined class, I have tried to catch the aforementioned exceptions in this way:
public function UpdateDateFormat($event) {
foreach ($this->attributes as $attribute) {
if(isset($this->owner->{$attribute})) {
$old_value = $this->owner->getOldAttribute($attribute);
if($old_value != $this->owner->{$attribute}){
try{
$this->owner->{$attribute} = \Yii::$app->formatter->asDate(str_replace('/', '-', $this->owner->{$attribute}), 'yyyy-MM-dd');
} catch (\Throwable $t) {
$this->owner->addError($attribute, 'Invalid date format');
$event->isValid = false;
} catch (\Exception $e) {
//this is for PHP5 compatibility
$this->owner->addError($attribute, 'Invalid date format');
$event->isValid = false;
}
}
}
}
}
However the exception is not handled at all and the error is displayed to the user, if i comment $event->isValid = false; the exception is handled and not raised but a wrong date is stored on the database and the attribute validation error is not taken into account at all, my question is: is there a way to gracefully catch this exception and show a validation error to the user instead of having a wrong date stored on the db or an exception not catched?
Thanks in advance