• Модуль: tasks
  • Путь к файлу: ~/bitrix/modules/tasks/lib/item.php
  • Класс: BitrixTasksItem
  • Вызов: Item::save
public function save($settings = array())
{
	$dc = static::getDataSourceClass();

	if($this->isImmutable())
	{
		$result = new Result();
		$result->getErrors()->add('IS_IMMUTABLE', 'Item is read-only');

		return $result;
	}

	$state = $this->getTransitionState();
	if($state->isInProgress())
	{
		$result = new Result();
		$result->getErrors()->add('IN_TRANSITION', 'Item is in transition state, no overlapping operations available');

		return $result;
	}

	$map = $this->getMap();
	$ufc = $this->getUserFieldController();

	$accessResult = new Result();

	// first - check access
	if($this->id) // we want update
	{
		$canPerform = $this->canUpdate($accessResult);
	}
	else
	{
		$canPerform = $this->canCreate($accessResult);
	}

	if($canPerform)
	{
		$state->enter(
			array(),
			$this->id ? State::MODE_UPDATE : State::MODE_CREATE
		);
		/** @var Result $result */
		$result = $state->getResult();

		/** @var FieldScalar $field */
		foreach($map as $field)
		{
			$name = $field->getName();

			if(!$this->isFieldModified($name))
			{
				// assign default values here, like they were modified...
				if(!$this->id && $field->hasDefaultValue($name, $this))
				{
					$field->setValue($field->getDefaultValue($name, $this), $name, $this);
					$this->setFieldModified($name); // mark as modified, to be saved
					continue;
				}
			}
		}

		// if we can, then run deep into structure and prepare data
		$this->prepareData($result);

		// todo: onBeforeSave event here

		// after that, run deep one more time and check data before saving
		$this->checkData($result);

		if($result->isSuccess() && $this->doPreActions($state) && $this->executeHooksBefore($state))
		{
			$tablet = array();
			$extra = array();

			/** @var ItemFieldScalar $field */
			foreach($map as $field)
			{
				$name = $field->getName();

				// skip non-writable fields
				// skip unchanged fields
				// skip non-cache-able fields that can NOT be written to the database
				if(!$field->isDBWritable() || !($this->isFieldModified($name) || (!$field->isCacheable() && $field->isDBWritable())))
				{
					continue;
				}

				$dbName = $field->getDBName();
				$value = $this[$name];

				$isTablet = $field->isSourceTablet();
				$isUf = $ufc && $field->isSourceUserField();
				$isCustom = $field->isSourceCustom();

				if ($value !== null)
				{
					if ($isTablet || $isUf)
					{
						$tablet[$dbName] = $field->translateValueToDatabase($value, $name, $this);
					}
					elseif ($isCustom)
					{
						$extra[$name] = $value; // the field will save data by itself
					}
				}
			}

			$tablet = $this->modifyTabletDataBeforeSave($tablet);

			$authContext = new BitrixMainAuthenticationContext();
			$authContext->setUserId($this->getUserId());

			unset($tablet['ID']);

			$tablet = array("fields" => $tablet, "auth_context" => $authContext);

			if($this->id)
			{
				$dbResult = $dc::update($this->id, $tablet);
			}
			else
			{
				$dbResult = $dc::add($tablet);
			}

			if($dbResult->isSuccess())
			{
				if(!$this->id)
				{
					$this->setId($dbResult->getId()); // bind current instance to the newly created item
				}

				// now save each extra field separately
				// todo: not only custom fields could have saveValueToDataBase() implemented!!!
				// todo: for example, task`s PARENT_ID can create additional structures with saveValueToDataBase()
				foreach($extra as $k => $v)
				{
					/** @var FieldScalar $fld */
					$fld = $map[$k];
					$subSaveResult = $fld->saveValueToDataBase($v, $k, $this);

					$result->adoptErrors($subSaveResult, array(
						'CODE' => $k.'.#CODE#',
						'MESSAGE' => Loc::getMessage('TASKS_ITEM_SUBITEM_SAVE_ERROR', array(
							'#ENTITY_NAME#' => $fld->getTitle()
						)).': #MESSAGE#',
					));
				}

				$this->executeHooksAfter($state);
				$this->doPostActions($state);

				// todo: onAfterSave event here
			}
			else
			{
				$result->adoptErrors($dbResult);
			}
		}

		$result->setInstance($this);
		$state->leave();
	}
	else
	{
		$result = new Result();
	}

	$result->adoptErrors($accessResult);

	if(
		$result->isSuccess()
		&&
		(
			!isset($settings['KEEP_DATA'])
			|| $settings['KEEP_DATA'] !== true
		)
	)
	{
		$this->clearData();
	}

	return $result;
}