daily pastebin goal
60%
SHARE
TWEET

Untitled

a guest Apr 17th, 2018 67 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <?php
  2. /**
  3.  * Created by PhpStorm.
  4.  * @author: svd22286@gmail.com
  5.  * @date: 13.12.16
  6.  * @time: 11:06
  7.  *
  8.  */
  9. namespace common\components;
  10.  
  11. use common\traits\ConsoleHelperTrait;
  12. use yii\base\InvalidConfigException;
  13. use yii\base\ModelEvent;
  14. use yii\db\ActiveRecord;
  15. use yii\base\Event;
  16. use yii\base\Component;
  17.  
  18. /**
  19.  * Component TimeStampUtc ensures that all active record fields timestamp in a project have a time in UTC.
  20.  * Applied for DATETIME, DATE, TIMESTAMP and INT datatypes (tested for MySql)
  21.  * Like [[yii\behaviors\TimestampBehavior]] automatically fills the specified attributes with the current UTC timestamp or convert to UTC value of a field
  22.  * (see [[common\components\TimeStampUtc::PROPERTY_DEFAULT_CONFIG]].
  23.  * By default applied on all [[yii\db\ActiveRecord]] instances of a project.
  24.  * You can configure behavior of each type of [[yii\db\ActiveRecord]] in config
  25.  *
  26.  * Config example:
  27.  * ```php
  28.  *  return [
  29.  *      'bootstrap' => ['timeStampUTC'],
  30.  *      'components' => [
  31.  *          'timeStampUTC' => [
  32.  *              'class' => 'common\components\TimeStampUTC',
  33.  *              'propertiesMap' => [
  34.  *                  'common\models\User' => [
  35.  *                      'createdAtAttribute' => [
  36.  *                          'attributeName' => 'created',
  37.  *                      ]
  38.  *                  ],
  39.  *                  'common\models\OtherModel1' => [
  40.  *                      'updatedAtAttribute' => [
  41.  *                          'value' => function($event) {
  42.  *                              return time() - 10800; //
  43.  *                           }
  44.  *                      ]
  45.  *                  ],
  46.  *                  'common\models\OtherModel2' => [
  47.  *                      'createdAtAttribute' => [
  48.  *                          'enableUTCBehavior' => false,//disable Utc behavior for created_at attribute of a model (use predefined value instead)
  49.  *                      ],
  50.  *                  ],
  51.  *                  'common\models\OtherModel3' => [
  52.  *                      'updatedAtAttribute' => [
  53.  *                          'enableUTCBehavior' => true,//enable Utc behavior
  54.  *                          'attributeName' => 'updated'//custom attribute name
  55.  *                      ]
  56.  *                  ],
  57.  *              ]
  58.  *          ]
  59.  *      ]
  60.  *  ]
  61.  *
  62.  * ```
  63.  * The configuration above means:
  64.  *  for common\models\User:
  65.  *      attribute name is `created` (for `createdAtAttribute` config section)
  66.  *      and value would be = UTC_TIMESTAMP() if its type DATETIME, DATE or TIMESTAMP and would be = UNIX_TIMESTAMP(UTC_TIMESTAMP()) if its type is INT
  67.  *      Similarly, attribute `updated_at` would be = UTC_TIMESTAMP() or UNIX_TIMESTAMP(UTC_TIMESTAMP()) according to its type
  68.  *      (Although the configuration section `updatetAtAttribute` has not been defined)
  69.  *  for common\models\OtherModel1:
  70.  *      attribute name for `createdAtAttribute` is `created_at` (by default), and behavior by default (see above example)
  71.  *      for `updatedAtAttribute` value would be calculated by \Closure given through 'value'
  72.  *  for common\models\OtherModel2:
  73.  *      disable behavior of this component for `created_at` attribute (if present in a model)
  74.  *      `updated` attribute would be  = UTC_TIMESTAMP() or UNIX_TIMESTAMP(UTC_TIMESTAMP()) according to its type (default behavior)
  75.  *  for common\models\OtherModel3
  76.  *      enable default behavior of this component for `created_at` attribute (if present in a model).
  77.  *      `updated_at` attribute would be  = UTC_TIMESTAMP() or UNIX_TIMESTAMP(UTC_TIMESTAMP()) according to its type (default behavior)
  78.  *
  79.  * If you want to disable timeStampUTC behavior for one of two attrs: createdAtAttribute or updatedAtAttribute, set the enableUTCBehavior to false
  80.  *
  81.  * @package common\components
  82.  */
  83. class TimeStampUTC extends Component {
  84.  
  85.     use ConsoleHelperTrait;
  86.  
  87.     /**
  88.      * @var string $nowStrUTC UTC time in format 'Y-m-d H:i:s'
  89.      */
  90.     protected $nowStrUTC;
  91.  
  92.     /**
  93.      * @var int $nowIntUTC UTC timestamp in seconds since 1 january 1970
  94.      */
  95.     protected $nowIntUTC;
  96.  
  97.     public $propertiesMap;
  98.  
  99.     /**
  100.      * @var string $strategy maybe one of [[TimeStampUTC::STRATAEGY_SOFT]], [[TimeStampUTC::STRATAEGY_HARD]]
  101.      * @todo Implement logic for hard strategy (This component by default monitors all AR models and attached to them)
  102.      */
  103.     public $strategy;
  104.  
  105.     const STRATAEGY_SOFT = 'soft';
  106.  
  107.     const STRATEGY_HARD = 'hard';
  108.  
  109.     const PROPERTY_DEFAULT_CONFIG = [
  110.         'createdAtAttribute' => [
  111.             'enableUTCBehavior' => true,
  112.             'attributeName' => 'created_at',
  113.             'stopPropagation' => false,
  114.             'value' => null,
  115.         ],
  116.         'updatedAtAttribute' => [
  117.             'enableUTCBehavior' => true,
  118.             'attributeName' => 'updated_at',
  119.             'stopPropagation' => false,
  120.             'value' => null,
  121.         ]
  122.     ];
  123.  
  124.     /**
  125.      * Attach event handlers
  126.      */
  127.     public function init() {
  128.         if (!$this->strategy) {
  129.             $this->strategy = self::STRATAEGY_SOFT;
  130.         }
  131.         $modelKeys = array_keys($this->propertiesMap);
  132.         foreach ($modelKeys as $k) {
  133.             Event::on($k, ActiveRecord::EVENT_BEFORE_INSERT, [$this, 'beforeInsert']);
  134.             Event::on($k, ActiveRecord::EVENT_BEFORE_UPDATE, [$this, 'beforeUpdate']);
  135.         }
  136.     }
  137.  
  138.     /**
  139.      * detache all event handlers, attached previously
  140.      */
  141.     public function detachAll() {
  142.         $modelKeys = array_keys($this->propertiesMap);
  143.         foreach ($modelKeys as $k) {
  144.             Event::off($k, ActiveRecord::EVENT_BEFORE_INSERT);
  145.             Event::off($k, ActiveRecord::EVENT_BEFORE_UPDATE);
  146.         }
  147.     }
  148.  
  149.     /**
  150.      * @param \yii\base\ModelEvent $event
  151.      */
  152.     public function beforeInsert(ModelEvent $event) {
  153.         $for = 'createdAtAttribute';
  154.         $result = $this->process($event, $for);
  155.         if (!$result) {
  156.             $event->handled = true;
  157.         }
  158.     }
  159.  
  160.     /**
  161.      * @param \yii\base\ModelEvent $event
  162.      */
  163.     public function beforeUpdate(ModelEvent $event) {
  164.         $for = 'updatedAtAttribute';
  165.         $result = $this->process($event, $for);
  166.         if (!$result) {
  167.             $event->handled = true;
  168.         }
  169.     }
  170.  
  171.     /**
  172.      * @param \yii\base\ModelEvent $event
  173.      * @param string $for
  174.      */
  175.     protected function process(ModelEvent $event, $for) {
  176.         $this->nowStrUTC = gmdate('Y-m-d H:i:s');//new Expression('UTC_TIMESTAMP()');
  177.         $this->nowIntUTC = strtotime($this->nowStrUTC);//time() + $utcDiff;//new Expression('UNIX_TIMESTAMP(UTC_TIMESTAMP())');
  178.         /**
  179.          * @var \yii\db\ActiveRecord $sender
  180.          */
  181.         $sender = $event->sender;
  182.  
  183.         $config = $this->getConfig($sender, $for);
  184.         $attributeName = $config['attributeName'];
  185.  
  186.         if($config['enableUTCBehavior']) {
  187.             if($sender->hasAttribute($attributeName)) {
  188.                 if ($config['enableUTCBehavior'] === true) {
  189.                     $this->setAttribute($event, $config);
  190.                 }
  191.             } else {
  192.  
  193.                 $className = get_class($sender);
  194.                 throw new InvalidConfigException('Attribute `'.$attributeName.'` must be present in `'.$className.'`');
  195.             }
  196.         }
  197.  
  198.         if ($config['stopPropagation'] === true) {
  199.             return false;
  200.         }
  201.         return true;
  202.     }
  203.  
  204.     /**
  205.      * @param \yii\db\ActiveRecord $sender
  206.      * @param string $for 'createdAtAttribute' | 'updatedAtAttribute'
  207.      * @return [
  208.      *  'enableUTCBehavior' => bool
  209.      *  'attributeName' => string,
  210.      *  'stopPropagation' => bool,
  211.      *  'convertToUTC' => bool,
  212.      * ]
  213.      */
  214.     protected function getConfig($sender, $for) {
  215.         /**
  216.          * @var \yii\db\ActiveRecord $sender
  217.          */
  218.         $className = get_class($sender);
  219.         $config = static::PROPERTY_DEFAULT_CONFIG[$for];
  220.         if (isset($this->propertiesMap[$className])) {
  221.             $conf = isset($this->propertiesMap[$className][$for]);
  222.             if($conf && is_array($this->propertiesMap[$className][$for])) {
  223.                 $config = array_replace($config,$this->propertiesMap[$className][$for]);
  224.             }
  225.         }
  226.         return $config;
  227.     }
  228.  
  229.     /**
  230.      * @param \yii\db\ActiveRecord $sender
  231.      * @param string $attribute
  232.      * @throws InvalidConfigException
  233.      */
  234.     protected function setAttribute($event, $config) {
  235.         $sender = $event->sender;
  236.         $attributeName = $config['attributeName'];
  237.         $closure = $config['value'];
  238.         $value = null;
  239.         if($closure) {
  240.             $value = call_user_func($closure, $event);
  241.         } else {
  242.             $phpType = $sender->getTableSchema()->columns[$attributeName]->phpType;
  243.             if ($phpType == 'string') {
  244.                 $value = $this->nowStrUTC;
  245.             } elseif ($phpType == 'integer') {
  246.                 $value = $this->nowIntUTC;
  247.             } else {
  248.                 throw new InvalidConfigException("Unknown column type of `$attributeName` in `{$event->sender}` model object#".$this->id);
  249.             }
  250.         }
  251.         $sender->$attributeName = $value;
  252.     }
  253. }
RAW Paste Data
Top