/** * (non-PHPdoc). * * @see Alpha\Model\ActiveRecordProviderInterface::save() */ public function save() { self::$logger->debug('>>save()'); $config = ConfigProvider::getInstance(); $sessionProvider = $config->get('session.provider.name'); $session = SessionProviderFactory::getInstance($sessionProvider); // get the class attributes $reflection = new ReflectionClass(get_class($this->BO)); $properties = $reflection->getProperties(); $sqlQuery = ''; $stmt = null; if ($this->BO->getVersion() != $this->BO->getVersionNumber()->getValue()) { throw new LockingException('Could not save the object as it has been updated by another user. Please try saving again.'); return; } // set the "updated by" fields, we can only set the user id if someone is logged in if ($session->get('currentUser') != null) { $this->BO->set('updated_by', $session->get('currentUser')->getOID()); } $this->BO->set('updated_ts', new Timestamp(date('Y-m-d H:i:s'))); // check to see if it is a transient object that needs to be inserted if ($this->BO->isTransient()) { $savedFieldsCount = 0; $sqlQuery = 'INSERT INTO ' . $this->BO->getTableName() . ' ('; foreach ($properties as $propObj) { $propName = $propObj->name; if (!in_array($propName, $this->BO->getTransientAttributes())) { // Skip the OID, database auto number takes care of this. if ($propName != 'OID' && $propName != 'version_num') { $sqlQuery .= "{$propName},"; ++$savedFieldsCount; } if ($propName == 'version_num') { $sqlQuery .= 'version_num,'; ++$savedFieldsCount; } } } if ($this->BO->isTableOverloaded()) { $sqlQuery .= 'classname,'; } $sqlQuery = rtrim($sqlQuery, ','); $sqlQuery .= ') VALUES ('; for ($i = 0; $i < $savedFieldsCount; ++$i) { $sqlQuery .= '?,'; } if ($this->BO->isTableOverloaded()) { $sqlQuery .= '?,'; } $sqlQuery = rtrim($sqlQuery, ',') . ')'; $this->BO->setLastQuery($sqlQuery); self::$logger->debug('Query [' . $sqlQuery . ']'); $stmt = self::getConnection()->stmt_init(); if ($stmt->prepare($sqlQuery)) { $stmt = $this->bindParams($stmt); $stmt->execute(); } else { throw new FailedSaveException('Failed to save object, error is [' . $stmt->error . '], query [' . $this->BO->getLastQuery() . ']'); } } else { // assume that it is a persistent object that needs to be updated $savedFieldsCount = 0; $sqlQuery = 'UPDATE ' . $this->BO->getTableName() . ' SET '; foreach ($properties as $propObj) { $propName = $propObj->name; if (!in_array($propName, $this->BO->getTransientAttributes())) { // Skip the OID, database auto number takes care of this. if ($propName != 'OID' && $propName != 'version_num') { $sqlQuery .= "{$propName} = ?,"; ++$savedFieldsCount; } if ($propName == 'version_num') { $sqlQuery .= 'version_num = ?,'; ++$savedFieldsCount; } } } if ($this->BO->isTableOverloaded()) { $sqlQuery .= 'classname = ?,'; } $sqlQuery = rtrim($sqlQuery, ','); $sqlQuery .= ' WHERE OID=?;'; $this->BO->setLastQuery($sqlQuery); $stmt = self::getConnection()->stmt_init(); if ($stmt->prepare($sqlQuery)) { $this->bindParams($stmt); $stmt->execute(); } else { throw new FailedSaveException('Failed to save object, error is [' . $stmt->error . '], query [' . $this->BO->getLastQuery() . ']'); } } if ($stmt != null && $stmt->error == '') { // populate the updated OID in case we just done an insert if ($this->BO->isTransient()) { $this->BO->setOID(self::getConnection()->insert_id); } try { foreach ($properties as $propObj) { $propName = $propObj->name; if ($this->BO->getPropObject($propName) instanceof Relation) { $prop = $this->BO->getPropObject($propName); // handle the saving of MANY-TO-MANY relation values if ($prop->getRelationType() == 'MANY-TO-MANY' && count($prop->getRelatedOIDs()) > 0) { try { try { // check to see if the rel is on this class $side = $prop->getSide(get_class($this->BO)); } catch (IllegalArguementException $iae) { $side = $prop->getSide(get_parent_class($this->BO)); } $lookUp = $prop->getLookup(); // first delete all of the old RelationLookup objects for this rel try { if ($side == 'left') { $lookUp->deleteAllByAttribute('leftID', $this->BO->getOID()); } else { $lookUp->deleteAllByAttribute('rightID', $this->BO->getOID()); } } catch (\Exception $e) { throw new FailedSaveException('Failed to delete old RelationLookup objects on the table [' . $prop->getLookup()->getTableName() . '], error is [' . $e->getMessage() . ']'); } $OIDs = $prop->getRelatedOIDs(); if (isset($OIDs) && !empty($OIDs[0])) { // now for each posted OID, create a new RelationLookup record and save foreach ($OIDs as $oid) { $newLookUp = new RelationLookup($lookUp->get('leftClassName'), $lookUp->get('rightClassName')); if ($side == 'left') { $newLookUp->set('leftID', $this->BO->getOID()); $newLookUp->set('rightID', $oid); } else { $newLookUp->set('rightID', $this->BO->getOID()); $newLookUp->set('leftID', $oid); } $newLookUp->save(); } } } catch (\Exception $e) { throw new FailedSaveException('Failed to update a MANY-TO-MANY relation on the object, error is [' . $e->getMessage() . ']'); return; } } // handle the saving of ONE-TO-MANY relation values if ($prop->getRelationType() == 'ONE-TO-MANY') { $prop->setValue($this->BO->getOID()); } } } } catch (\Exception $e) { throw new FailedSaveException('Failed to save object, error is [' . $e->getMessage() . ']'); return; } $stmt->close(); } else { // there has been an error, so decrement the version number back $temp = $this->BO->getVersionNumber()->getValue(); $this->BO->set('version_num', $temp - 1); // check for unique violations if (self::getConnection()->errno == '1062') { throw new ValidationException('Failed to save, the value ' . $this->findOffendingValue(self::getConnection()->error) . ' is already in use!'); return; } else { throw new FailedSaveException('Failed to save object, MySql error is [' . self::getConnection()->error . '], query [' . $this->BO->getLastQuery() . ']'); } } if ($this->BO->getMaintainHistory()) { $this->BO->saveHistory(); } }