Perform afterSave events:
- flush needed caches
- trigger afterPropertyModelSave event of corresponding PropertyHandler

@param bool $insert
@param array $changedAttributes
10958
public function afterSave($insert, $changedAttributes)
    {
        parent::afterSave($insert, $changedAttributes);
        if (isset($changedAttributes['key'])) {
            Yii::$app->cache->delete("PropertyKeyForId:{$this->id}");
        }
        $handler = PropertyHandlerHelper::getInstance()->handlerById($this->property_handler_id);
        $handler->afterPropertyModelSave($this);

        $storage = PropertyStorageHelper::storageById($this->storage_id);

        if ($insert) {
            $storage->afterPropertyAdd($this);
        } else {
            $storage->afterPropertyChange($this, $changedAttributes);
        }
        if (in_array('storage_id', $changedAttributes) === true) {
            $apmIds = ArrayHelper::getColumn($this->propertyGroups, 'applicable_property_model_id');
            foreach (ApplicablePropertyModels::find()->select('class_name')->where(['id' => $apmIds])->column() as $className) {
                PropertyStorageHelper::clearHandlersForClass($className);
            }
        }
    }
506680
protected function flushConfigurationCachesByChangedFiles(array $changedFiles): void
    {
        $aopProxyClassRebuildIsNeeded = false;
        $aopProxyClassInfluencers = '/(?:Policy|Objects|Settings)(?:\..*)*\.yaml/';

        $objectClassesCache = $this->getCache('Flow_Object_Classes');
        $objectConfigurationCache = $this->getCache('Flow_Object_Configuration');
        $caches = [
            '/Policy\.yaml/' => ['Flow_Security_Authorization_Privilege_Method', 'Flow_Persistence_Doctrine', 'Flow_Persistence_Doctrine_Results', 'Flow_Aop_RuntimeExpressions'],
            '/Routes([^\/]*)\.yaml/' => ['Flow_Mvc_Routing_Route', 'Flow_Mvc_Routing_Resolve'],
            '/Views\.yaml/' => ['Flow_Mvc_ViewConfigurations']
        ];
        $cachesToFlush = [];
        foreach (array_keys($changedFiles) as $pathAndFilename) {
            foreach ($caches as $cacheFilePattern => $cacheNames) {
                if (preg_match($aopProxyClassInfluencers, $pathAndFilename) === 1) {
                    $aopProxyClassRebuildIsNeeded = true;
                }
                if (preg_match($cacheFilePattern, $pathAndFilename) !== 1) {
                    continue;
                }
                foreach ($caches[$cacheFilePattern] as $cacheName) {
                    $cachesToFlush[$cacheName] = $cacheFilePattern;
                }
            }
        }

        foreach ($cachesToFlush as $cacheName => $cacheFilePattern) {
            $this->logger->info(sprintf('A configuration file matching the pattern "%s" has been changed, flushing related cache "%s"', $cacheFilePattern, $cacheName), LogEnvironment::fromMethodName(__METHOD__));
            $this->getCache($cacheName)->flush();
        }

        $this->logger->info('A configuration file has been changed, refreshing compiled configuration cache', LogEnvironment::fromMethodName(__METHOD__));
        $this->configurationManager->refreshConfiguration();

        if ($aopProxyClassRebuildIsNeeded) {
            $this->logger->info('The configuration has changed, triggering an AOP proxy class rebuild.', LogEnvironment::fromMethodName(__METHOD__));
            $objectConfigurationCache->remove('allAspectClassesUpToDate');
            $objectConfigurationCache->remove('allCompiledCodeUpToDate');
            $objectClassesCache->flush();
        }
    }
1082254
public function afterSave($insert, $changedAttributes)
    {
        if ($insert) {
            //开始机器审核
            Yii::$app->queue->push(new ScanTextJob([
                'modelClass' => get_class($this), 'modelId' => $this->id, 'scenario' => 'new', 'category' => 'comment'
            ]));
            $this->source->updateCountersAsync(['comments' => 1]);
            try {
                Yii::$app->notification->send($this->source->user, $this);
            } catch (InvalidConfigException $e) {
            }
        }
        parent::afterSave($insert, $changedAttributes);
    }
666052
protected function propertyHasChanged()
    {
        foreach ($this->dbFields as $field => $value) {
            if (isset($this->fields[$field]) && ($this->fields[$field] != $value)) {
                return true;
            }
        }

        return false;
    }
1276338
public BaseBo setAttribute(String attrName, Object value, boolean triggerChange) {
        Lock lock = lockForWrite();
        try {
            if (value == null) {
                attributes.remove(attrName);
            } else {
                attributes.put(attrName, value);
            }
            if (triggerChange) {
                triggerChange(attrName);
            }
            markDirty();
            return this;
        } finally {
            lock.unlock();
        }
    }
302246
public function beforeSave($insert)
    {
        if (parent::beforeSave($insert)) {
            $attachments = upload_img('WarehouseVoucher[attachments]', true);
            if (!empty($attachments)) {
                $this->attachments = json_encode($attachments);
            } elseif (empty($attachments) && !$this->isNewRecord) {
                $model = static::findOne(['id' => $this->id]);
                if ($model) {
                    $this->attachments = $model->attachments;
                }
            }

            if (!$this->isNewRecord) {
                $this->total_price = WarehouseVoucherItems::find()
                    ->where(['warehouse_voucher' => $this->id])
                    ->sum('total_price');
            }

            return true;
        }

        return false;
    }
29447
function (change) {
        var chg = change, cpt = this.cpt;
        if (change.constructor === Array) {
            if (change.length > 0) {
                chg = change[0];
            } else {
                log.error('[CptNode] Invalid change - nbr of changes: '+change.length);
                return;
            }
        }
        this.needsRefresh=true;
        var nm = chg.name; // property name
        if (nm === "") {
            return; // doesn't make sens (!)
        }

        var callControllerCb = true; // true if the onXXXChange() callback must be called on the controller

        var att, isAttributeChange = false;
        if (cpt.$attributes) {
            att = cpt.$attributes[nm];
            isAttributeChange = (att !== undefined);
            if (isAttributeChange) {
                // adapt type if applicable
                var t = att.type;
                if (t) {
                    var v = ATTRIBUTE_TYPES[t].convert(chg.newValue, att);
                    chg.newValue = v;
                    cpt[nm] = v; // change is already raised, no need to trigger another one through json.set()
                }
            }

            if (isAttributeChange && this.nodeInstance) {
                // notify attribute changes to the node instance (and the host) if attribute has a 2-way binding
                if (att._binding === 2) {
                    chg.newValue = this.cpt[nm]; // attribute value may have been changed by the controller
                    this.nodeInstance.onAttributeChange(chg);
                }
            }

            if (isAttributeChange) {
                // check if cb must be called depending on binding mode
                if (att._binding === 0) {
                    callControllerCb = false;
                }
            }
        }

        if (callControllerCb) {
            if (this.processingChange === true) {
                // don't re-enter the change loop if we are already processing changes
                return;
            }
            this.processingChange = true;
            try {
                // calculate the callback name (e.g. onValueChange for the 'value' property)
                var cbnm='';
                if (isAttributeChange) {
                    cbnm=att.onchange;
                }
                if (!cbnm) {
                    cbnm = ["$on", nm.charAt(0).toUpperCase(), nm.slice(1), "Change"].join('');
                }

                if (cpt[cbnm]) {
                    cpt[cbnm].call(cpt, chg.newValue, chg.oldValue);
                }

            } finally {
                this.processingChange = false;
            }
        }
    }
1187116
protected void fireAttributeChangedEvent(String name, AttributeValue oldValue, AttributeValue currentValue) {
		if (this.listeners != null && isEventFirable()) {
			final AttributeChangeEvent event = new AttributeChangeEvent(
					//source
					this,
					//type
					Type.VALUE_UPDATE,
					//old name
					name,
					//old value
					oldValue,
					//current name
					name,
					//current value
					currentValue);
			for (final AttributeChangeListener listener : this.listeners) {
				listener.onAttributeChangeEvent(event);
			}
		}
	}
1300100
public function beforeSave($insert)
    {
        if (!parent::beforeSave($insert)) {
            return false;
        }

        $this->setAttribute(static::dynamicColumn(), static::dynColExpression($this->_dynamicAttributes));

        return true;
    }
295654
public void saveProperties(CmsPropertyChangeSet changes) throws CmsException {

        CmsObject cms = m_cms;
        CmsUUID structureId = changes.getTargetStructureId();
        if (m_overrideStructureId != null) {
            structureId = m_overrideStructureId;
        }
        CmsResource resource = cms.readResource(structureId, CmsResourceFilter.IGNORE_EXPIRATION);
        CmsLockActionRecord actionRecord = CmsLockUtil.ensureLock(cms, resource);
        try {
            Map<String, CmsProperty> ownProps = getPropertiesByName(cms.readPropertyObjects(resource, false));
            // determine if the title property should be changed in case of a 'NavText' change
            boolean changeOwnTitle = shouldChangeTitle(ownProps);

            String hasNavTextChange = null;
            List<CmsProperty> ownPropertyChanges = new ArrayList<CmsProperty>();
            for (CmsPropertyModification propMod : changes.getChanges()) {
                if (propMod.isFileNameProperty()) {
                    // in case of the file name property, the resource needs to be renamed
                    if ((m_overrideStructureId == null) && !resource.getStructureId().equals(propMod.getId())) {
                        if (propMod.getId() != null) {
                            throw new IllegalStateException("Invalid structure id in property changes.");
                        }
                    }
                    CmsResource.checkResourceName(propMod.getValue());
                    String oldSitePath = CmsFileUtil.removeTrailingSeparator(cms.getSitePath(resource));
                    String parentPath = CmsResource.getParentFolder(oldSitePath);
                    String newSitePath = CmsFileUtil.removeTrailingSeparator(
                        CmsStringUtil.joinPaths(parentPath, propMod.getValue()));
                    if (!oldSitePath.equals(newSitePath)) {
                        cms.moveResource(oldSitePath, newSitePath);
                    }
                    // read the resource again to update name and path
                    resource = cms.readResource(resource.getStructureId(), CmsResourceFilter.IGNORE_EXPIRATION);
                } else {
                    CmsProperty propToModify = null;
                    if ((m_overrideStructureId != null) || resource.getStructureId().equals(propMod.getId())) {

                        if (CmsPropertyDefinition.PROPERTY_NAVTEXT.equals(propMod.getName())) {
                            hasNavTextChange = propMod.getValue();
                        } else if (CmsPropertyDefinition.PROPERTY_TITLE.equals(propMod.getName())) {
                            changeOwnTitle = false;
                        }
                        propToModify = ownProps.get(propMod.getName());
                        if (propToModify == null) {
                            propToModify = new CmsProperty(propMod.getName(), null, null);
                        }
                        ownPropertyChanges.add(propToModify);
                    } else {
                        throw new IllegalStateException("Invalid structure id in property changes!");
                    }
                    String newValue = propMod.getValue();
                    if (newValue == null) {
                        newValue = "";
                    }
                    if (propMod.isStructureValue()) {
                        propToModify.setStructureValue(newValue);
                    } else {
                        propToModify.setResourceValue(newValue);
                    }
                }
            }
            if (hasNavTextChange != null) {
                if (changeOwnTitle) {
                    CmsProperty titleProp = ownProps.get(CmsPropertyDefinition.PROPERTY_TITLE);
                    if (titleProp == null) {
                        titleProp = new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, null, null);
                    }
                    titleProp.setStructureValue(hasNavTextChange);
                    ownPropertyChanges.add(titleProp);
                }
            }
            if (!ownPropertyChanges.isEmpty()) {
                cms.writePropertyObjects(resource, ownPropertyChanges);
            }
        } finally {
            if (actionRecord.getChange() == LockChange.locked) {
                cms.unlockResource(resource);
            }
        }

    }
684603
private void publishToRepository(String attributeName, String attributeType, Object oldValue, Object newValue) {
        super.sendNotification(new AttributeChangeNotification(
                        this,
                        sequenceNum.incrementAndGet(),
                        System.currentTimeMillis(),
                        "",
                        attributeName,
                        attributeType,
                        oldValue,
                        newValue));
    }
1353257
protected function afterSave($changed)
    {
        // Communicate to user
        if ($changed) {
            switch ($this->formData['change_method']) {
                case 'share':
                    $message = $this->_('Shared %s with %s as %s');
                    break;

                case 'copy':
                    $message = $this->_('Moved %s with tracks to %s as %s');
                    break;
                case 'move':
                    $message = $this->_('Moved %s to %s as %s');
                    break;
                default:

            }
            $this->addMessage(sprintf(
                    $message,
                    $this->request->getParam(\MUtil_Model::REQUEST_ID1),
                    $this->loader->getOrganization($this->formData['gr2o_id_organization'])->getName(),
                    $this->formData['gr2o_patient_nr']
                    ));

            $this->accesslog->logChange($this->request, null, $this->formData);

        } else {
            parent::afterSave($changed);
        }
    }
1255072
protected function afterSave($changed)
    {
        parent::afterSave($changed);

        $model = $this->getModel();
        if ($model instanceof \Gems_Model_AppointmentModel) {
            $count = $model->getChangedTokenCount();
            if ($count) {
                $this->addMessage(sprintf($this->plural('%d token changed', '%d tokens changed', $count), $count));
            }
        }
    }
1186325
public function beforeSave($insert)
    {
        if (parent::beforeSave($insert)) {
            $feature_img = upload_img('Product[feature_img]');
            if (!empty($feature_img)) {
                $this->feature_img = $feature_img;
            } elseif(empty($feature_img) && !$this->isNewRecord) {
                $model = static::findOne(['id' => $this->id]);
                if ($model) {
                    $this->feature_img = $model->feature_img;
                }
            }

            $gallery = upload_img('Product[gallery]', true);
            if (!empty($gallery)) {
                $this->gallery = json_encode($gallery);
            } elseif (empty($gallery) && !$this->isNewRecord) {
                $model = static::findOne(['id' => $this->id]);
                if ($model) {
                    $this->gallery = $model->gallery;
                }
            }

            return true;
        }

        return false;
    }
1154255
private function updateOldFieldAceProperty($name, array $changes)
    {
        $currentIds = array();
        foreach ($changes[1] as $field => $new) {
            for ($i = 0, $c = count($new); $i < $c; ++$i) {
                $ace = $new[$i];

                if (null !== $ace->getId()) {
                    $currentIds[$ace->getId()] = true;
                }
            }
        }

        foreach ($changes[0] as $old) {
            for ($i = 0, $c = count($old); $i < $c; ++$i) {
                $ace = $old[$i];

                if (!isset($currentIds[$ace->getId()])) {
                    $this->connection->executeQuery($this->getDeleteAccessControlEntrySql($ace->getId()));
                    unset($this->loadedAces[$ace->getId()]);
                }
            }
        }
    }
429983
protected function correctSavedData($insert)
    {
        if ($insert) {

            $tzShiftSec = intval(date('Z')); // server time zone shift in seconds: west UTC <0, east UTC >0
            if (!empty($tzShiftSec)) {//var_dump($tzShiftSec);exit;
                $now = new Expression("DATE_SUB(NOW(), INTERVAL {$tzShiftSec} SECOND)"); // UTC time
            } else {
                $now = new Expression('NOW()'); // server time
            }
            $this->create_time = $now;
            if (empty($this->show_from_time)) $this->show_from_time = $now;
            $this->owner_id = Yii::$app->user->identity->id;
        } else {
/*
            // this corrections move to controller to POST preprocessing
            if (!empty($this->timezoneshift)) {//var_dump($this->attributes);var_dump($this->timezoneshift);
                $tz = intval($this->timezoneshift);
                $this->show_from_time = new Expression("DATE_ADD('{$this->show_from_time}', INTERVAL {$tz} MINUTE)");
                $this->show_to_time   = new Expression("DATE_ADD('{$this->show_to_time}',   INTERVAL {$tz} MINUTE)");
            }//var_dump($this->attributes);exit;
*/
        }
    }
472894
protected void fireAttributeChange(AttributeChangeEvent event) {
		if (this.listeners != null && isEventFirable()) {
			final AttributeChangeListener[] list = new AttributeChangeListener[this.listeners.size()];
			this.listeners.toArray(list);
			for (final AttributeChangeListener listener : list) {
				listener.onAttributeChangeEvent(event);
			}
		}
	}