Re Auto-Update Denormalized Attributes With Mongodb And Yii2

Hmm, "you are too new to post comments" so a Forum post instead.

Referring to by @edoardo849

Thanks for the excellent article and code!

Minor nitpick: in Mongodb 2.6 timeout has been renamed to socketTimeout, so in mongoCommand use this:

   'socketTimeoutMS' => $this->mongoTimeout

However… I couldn’t wrap my head around the way the errorOnDelete mechanism is supposed to work.

What I want is to block deletion of an item from the collection to which this behavior is attached as long as the specific item is referenced from any document in a related collection. With the current implementation this condition is not tested (only if any of the related documents would need the referenced value changed/updated).

The following method implements what I need. Maybe this can be done much more elegant, so please take this as a starting point. Or if I’m mistaken I would like to know as well.

  1. Modified method onBeforeDelete:

	public function onBeforeDelete()


		if ($this->errorOnDelete) {

			$matches = $this->findDependencies();

			if (!empty($matches)) {

				throw new ErrorException('The following models have dependencies which prevent deletion: ' . Json::encode($matches), 500);




  1. New method findDependencies:

	private function findDependencies()


		$fields = $this->owner->attributes;

		$matches = [];

		foreach($fields as $key => $value) {

			if (!isset($this->attributes[$key]))


			$dependencies = $this->attributes[$key];

			foreach ($dependencies as $dependency) {


				 * @var \yii\mongodb\ActiveRecord $model


				$model = new $dependency['class'];

				$targetAttribute = $dependency['targetAttribute'];

				$collectionName = $model::collectionName();

				if ($model instanceof MongoGridFsAR) {

					$collectionName = $collectionName . '.files';



				 * @var \MongoCollection $mongoCommand


				$mongoCommand = Yii::$app->mongodb->getCollection($collectionName);

				$res = $mongoCommand->find(

						[$targetAttribute => $value], [$targetAttribute])->count();

				if (!empty($res)) {

					$matches[$model->collectionName()] = "$targetAttribute ($res)";




		return $matches;