/** * Get an ID for the given data * * @return string */ public function getId() { $values = []; foreach ($this->metadata->getIdColumns() as $column) { $values[] = $this->getPropertyValue($column->getProperty()); } if (!count($values)) { throw new InvalidEntityException('Entity "' . $this->metadata->getClassName() . '" has no ID column'); } return implode(self::ID_DELIMITER, $values); }
/** * Hydrate a relationship * * @param Relationship $relative * @return $this */ public function hydrateRelative(Relationship $relative) { $this->entity_manager->getDriver()->debugLog("Hydrating relative for " . $this->metadata->getTableName() . "[" . $this->getReader()->getId() . "]::" . $relative->getName()); $setter = $relative->getSetter(); $key = $this->entity_manager->getKeyScheme()->getRelationshipKey($relative, $this->entity_manager->getMapper()->getEntityMetadata($relative->getSource())->getTableName(), $this->entity_manager->getMapper()->getEntityMetadata($relative->getTarget())->getTableName(), $this->getReader()->getId()); if (RelationshipType::isMultiIndex($relative->getRelationshipType())) { $items = []; $ids = $this->entity_manager->getDriver()->getMultiValueIndex($key); foreach ($ids as $id) { $items[] = $this->entity_manager->retrieveEntityOrNew($relative->getTarget(), $id); } $this->proxy->{$setter}($items); } else { $id = $this->entity_manager->getDriver()->getSingleValueIndex($key); if ($id) { $this->proxy->{$setter}($this->entity_manager->retrieve($relative->getTarget(), $id)); } } return $this; }
/** * Traverse an array of relationships and persist them * * @param Entity $metadata * @param object $entity * @param Reader $reader * @param string $local_id */ private function persistRelationshipsTraversal(Entity $metadata, $entity, Reader $reader, $local_id) { $relationships = $metadata->getRelationships(); $is_proxy = $entity instanceof OrmProxyInterface; foreach ($relationships as $relationship) { $key = $this->getRelationshipKey($relationship, $local_id); $value = $reader->getPropertyValue($relationship->getName()); // Skip relationship rules: // If the entity is not a proxy (i.e. a new entity) we still must allow for the scenario in which a new // entity is created over the top of an existing entity (same ID), as such, we still need to check every // relationship attached to the entity if (!$this->entity_manager->getMaintenanceMode() && $is_proxy) { /** @var OrmProxyInterface $entity */ // Check if we can skip the update if (!$entity->isRelativeModified($relationship->getName())) { // Looks like we can skip, but check inverted sort indices first - if ($relationship->getInversedBy()) { $inverse_relationship = $this->invertRelationship($relationship); if ($inverse_relationship->getSortableBy()) { // The inverse relationship has sortable columns, we need to check for local property // changes that might have impacted this - list(, , $maintain) = $this->getRelationshipDeltas($key, $relationship, $value); $this->updateMaintainedRelationshipSortIndices($inverse_relationship, $maintain, $reader, $local_id); } } // Nothing else to update on this relationship continue; } } // This condition allows NEW (not a proxy) entities that have NOT set a relationship to inherit existing // relationships which could be useful if the relationship was set by a foreign entity // See: docs/RaceConditions.md if ($is_proxy || $value || $this->entity_manager->getMaintenanceMode()) { $this->persistForwardRelationship($relationship, $key, $value); if (count($relationship->getSortableBy())) { $this->persistForwardSortIndices($relationship, $local_id, $value); } // Modify the inversed relationships if ($relationship->getInversedBy()) { $this->persistInversedRelationship($relationship, $key, $value, $local_id, $reader); } else { $this->persistRefs($relationship, $key, $value, $local_id); } } } }
/** * Get the Entity metadata object * * @return Entity */ public function getEntityMetadata() { $entity = new Entity($this->reflection_obj->name, $this->getTableName()); $entity->setColumns($this->getColumns()); $entity->setRelationships($this->getRelationships()); $entity->setIndices($this->getIndices()); $entity->setSortables($this->getSortables()); return $entity; }
/** * Deserialise the entity * * @param Entity $metadata Metadata object to match the entity * @param SerialisedData $data Data to deserialise * @param object $entity Entity to hydrate */ public function deserialise(Entity $metadata, SerialisedData $data, $entity) { // Using $assoc = true is ~ 10-20% quicker on PHP 5.3 // Source: http://stackoverflow.com/questions/8498114/should-i-use-an-associative-array-or-an-object $raw = json_decode($data->getData(), true, self::ENCODE_DEPTH); foreach ($metadata->getColumns() as $column) { $setter = $column->getSetter(); $field = $column->getName(); $value = isset($raw[$field]) ? $raw[$field] : null; switch ($column->getType()) { default: break; case FieldType::DATETIME(): $entity->{$setter}($this->deserialiseDateTime($value)); break; case FieldType::INT(): $entity->{$setter}((int) $value); break; case FieldType::STRING(): $entity->{$setter}((string) $value); break; case FieldType::DECIMAL(): $entity->{$setter}((double) $value); break; case FieldType::BOOL(): $entity->{$setter}((bool) $value); break; case FieldType::SET(): $entity->{$setter}(json_decode($value, true)); break; case FieldType::OBJECT(): $entity->{$setter}($this->deserialiseObject($value, $column->getClassName())); break; } } }
/** * Compiles all columns for an entity * * @param Entity $md * @return array */ private function compileColumns(Entity $md) { $out = []; $columns = $md->getColumns(); foreach ($columns as $column) { $data = [Schema::COLUMN_TYPE => $column->getType()->value()]; if ($column->isId()) { $data[Schema::COLUMN_ID] = true; } $default_getter = 'get' . Inflector::classify($column->getProperty()); if ($column->getGetter() && $column->getGetter() != $default_getter) { $data[Schema::GETTER] = $column->getGetter(); } $default_setter = 'set' . Inflector::classify($column->getProperty()); if ($column->getSetter() && $column->getSetter() != $default_setter) { $data[Schema::SETTER] = $column->getSetter(); } if ($column->getClassName()) { $data[Schema::COLUMN_CLASS] = $column->getClassName(); } $out[$column->getName()] = $data; } $relationships = $md->getRelationships(); foreach ($relationships as $relationship) { $data = [Schema::REL_ASSOCIATION => $relationship->getRelationshipType()->value(), Schema::REL_TARGET => $relationship->getTarget()]; $default_getter = 'get' . Inflector::classify($relationship->getName()); if ($relationship->getGetter() && $relationship->getGetter() != $default_getter) { $data[Schema::GETTER] = $relationship->getGetter(); } $default_setter = 'set' . Inflector::classify($relationship->getName()); if ($relationship->getSetter() && $relationship->getSetter() != $default_setter) { $data[Schema::SETTER] = $relationship->getSetter(); } if ($relationship->getInversedBy()) { $data[Schema::REL_INVERSED_BY] = $relationship->getInversedBy(); } if ($relationship->getSortableBy()) { $data[Schema::SORT_INDICES] = $this->compileSortables($relationship->getSortableBy()); } $out[$relationship->getName()] = $data; } return $out; }
/** * Parse a map file, loading metadata from YAML * * @param string $fn * @return $this */ protected function loadMap($fn) { $map = $this->getParser()->parse(file_get_contents($fn)); foreach ($map as $class => $schema) { $table = $this->getNode($schema, Schema::TABLE_NAME); $entity = new Entity($class, $table); // Columns $columns = $this->getNode($schema, Schema::COLUMNS); foreach ($columns as $property => $column_schema) { if ($this->getNode($column_schema, Schema::REL_ASSOCIATION, false)) { $rel = $this->createRelationship($property, $column_schema); $rel->setSource($class); $entity->addRelationship($rel); } else { $entity->addColumn($this->createColumn($property, $column_schema)); } } // Table sortables $entity->setSortables($this->createSortables($schema)); // Table indices $indices = $this->getNode($schema, Schema::STD_INDICES, false, []); foreach ($indices as $name => $index_schema) { $index = new Index($table, $name); $index->setColumns($this->getNode($index_schema, Schema::INDEX_COLUMNS, false, [])); $index->setMethods($this->getNode($index_schema, Schema::INDEX_METHODS, false, [])); $entity->addIndex($index); } // Add to map $this->entities[$class] = $entity; } return $this; }