/** * Přidává k metadatům entity rozšiřující vazby * * @param string * @param MetaData * @return MetaData */ public function completeMetaData($targetEntityClass, MetaData $metaData) { // kompletujeme relace i pro všechny předky entity $classes = $this->getClasses($targetEntityClass); $relations = array(); foreach ($classes as $class) { $relations = $relations + $this->getRelations($class); } $properties = $metaData->toArray(); // 0 type // 1 property // 2 relationType // 3 repositoryClass // 4 opositProperty foreach ($relations as $data) { if (isset($properties[$data[1]])) { continue; } if ($data[2] === MetaData::OneToMany) { $metaData->addProperty($data[1], $data[0])->setOneToMany($data[3], $data[4]); } elseif ($data[2] === MetaData::ManyToMany) { $metaData->addProperty($data[1], $data[0])->setManyToMany($data[3], $data[4], RelationshipMetaDataToMany::MAPPED_THERE); } else { throw new InvalidArgumentException("Pouze relace '1:m' a 'm:m' může být přiřazdena entitě rozšiřijící entitou. Uvedenou relaci '{$data['2']}' nelze přidat."); } } return $metaData; }
/** @param DibiMapper */ public function __construct(DibiMapper $mapper) { $this->mapper = $mapper; $model = $mapper->getModel(); $repository = $mapper->getRepository(); foreach ((array) $repository->getEntityClassName() as $entityName) { foreach (MetaData::getEntityRules($entityName, $model) as $name => $rule) { if ($rule['relationship'] === MetaData::OneToMany) { $loader = $rule['relationshipParam']; if ($r = $loader->getRepository() and $p = $loader->getParam()) { $this->relationships[$name] = array($r, array('id', false), array($p, false)); } } else { if ($rule['relationship'] === MetaData::ManyToMany) { $loader = $rule['relationshipParam']; if ($r = $loader->getRepository()) { $manyToManyMapper = $loader->getMapper($repository); if ($manyToManyMapper instanceof DibiManyToManyMapper) { $this->relationships[$name] = array($r, array($manyToManyMapper->childParam, true), array('id', false), array($manyToManyMapper->table, array('id', false), array($manyToManyMapper->parentParam, true))); } } } else { if ($rule['relationship'] === MetaData::ManyToOne or $rule['relationship'] === MetaData::OneToOne) { $this->relationships[$name] = array($rule['relationshipParam'], array(NULL, false), array('id', false)); } } } } } }
/** * @return string|NULL null mean disable */ protected function getOrderProperty() { // Default ordering by property order is deprecated and in future version will be removed. if ($this->defaltOrderPropertyBCValue === false) { foreach ($this->getMetaData()->getCanConnectWithEntities($this->getModel()) as $entityClass) { $meta = MetaData::getEntityRules($entityClass, $this->getModel()); if (!isset($meta['order'])) { $this->defaltOrderPropertyBCValue = NULL; break; } $this->defaltOrderPropertyBCValue = 'order'; } } return $this->defaltOrderPropertyBCValue; }
/** * Vrati cizy klice pro tuto entitu. * @param string * @return array paramName => repositoryName */ private final function getFkForEntity($entityName) { static $fks = array(); if (!isset($fks[$entityName])) { $fk = array(); foreach (MetaData::getEntityRules($entityName, $this->model) as $name => $rule) { if ($rule['relationship'] !== MetaData::ManyToOne and $rule['relationship'] !== MetaData::OneToOne) { continue; } $fk[$name] = $rule['relationshipParam']; } $fks[$entityName] = $fk; } return $fks[$entityName]; }
/** * Vytazena z mapperu * @param IRepository * @param array */ protected function onLoad(IRepository $repository, array $data) { parent::onLoad($repository, $data); $this->rules = MetaData::getEntityRules(get_class($this), $repository->getModel()); $this->values = $data; $this->valid = []; $this->getValue('id'); // throw error if any }
/** * @param IRepositoryContainer * @param string MetaData::ManyToOne|MetaData::OneToOne|MetaData::ManyToMany|MetaData::OneToMany * @param bool * @param callback (RelationshipMetaData $parent, RelationshipMetaData $child) */ protected function checkIntegrity(IRepositoryContainer $model, $expectedType, $requiredChildParam, $callback = NULL) { $lowerEntityName = strtolower($this->parentEntityName); $classes = array_values((array) $model->getRepository($this->childRepository)->getEntityClassName()); $this->canConnectWith = array_combine(array_map('strtolower', $classes), $classes); if (!$this->childParam) { return; } foreach ($this->canConnectWith as $lowerEn => $en) { $meta = MetaData::getEntityRules($en, $model, $this->childParam); $e = NULL; if (isset($meta[$this->childParam])) { $meta = $meta[$this->childParam]; try { if ($meta['relationship'] !== $expectedType) { throw new RelationshipLoaderException("{$this->parentEntityName}::\${$this->parentParam} {{$this->type}} na druhe strane asociace {$en}::\${$this->childParam} neni asociace ktera by ukazovala zpet"); } $loader = $meta['relationshipParam']; if ($requiredChildParam and !$loader->childParam) { throw new RelationshipLoaderException("{$this->parentEntityName}::\${$this->parentParam} {{$this->type}} na druhe strane asociace {$en}::\${$this->childParam} neni vyplnen param ktery by ukazoval zpet"); } if (!isset($loader->canConnectWith[$lowerEntityName])) { do { foreach ($loader->canConnectWith as $canConnectWithEntity) { if (is_subclass_of($canConnectWithEntity, $lowerEntityName)) { break 2; // goto canConnect; } } throw new RelationshipLoaderException("{$this->parentEntityName}::\${$this->parentParam} {{$this->type}} na druhe strane asociace {$en}::\${$this->childParam} neukazuje zpet; ukazuje na jiny repository ({$loader->repository})"); } while (false); // canConnect: } if ($loader->childParam and $loader->childParam !== $this->parentParam) { throw new RelationshipLoaderException("{$this->parentEntityName}::\${$this->parentParam} {{$this->type}} na druhe strane asociace {$en}::\${$this->childParam} neukazuje zpet; ukazuje na jiny parametr ({$loader->childParam})"); } if ($callback) { call_user_func($callback, $this, $loader); } continue; } catch (Exception $e) { } } unset($this->canConnectWith[$lowerEn]); if ($e) { throw $e; } } if (!$this->canConnectWith) { throw new RelationshipLoaderException("{$this->parentEntityName}::\${$this->parentParam} {{$this->type}} na druhe strane asociace {$this->repository}::\${$this->childParam} neni asociace ktera by ukazovala zpet"); } }
/** * @param MetaData * @param string * @param MetaData::READWRITE|MetaData::READ|MetaData::WRITE * @param string */ private function addProperty(MetaData $metaData, $string, $mode, $class) { if ($mode === MetaData::READWRITE) { if (preg_match('#^(-read|-write)?\\s?(.*)$#si', $string, $match)) { $mode = $match[1]; $mode = ((!$mode or $mode === '-read') ? MetaData::READ : 0) | ((!$mode or $mode === '-write') ? MetaData::WRITE : 0); $string = $match[2]; } } if (preg_match('#^([a-z0-9_\\|\\\\]+)\\s+\\$([a-z0-9_]+)($|\\s(.*)$)#si', $string, $match)) { $property = $match[2]; $type = $match[1]; $string = $match[3]; } else { if (preg_match('#^\\$([a-z0-9_]+)\\s+([a-z0-9_\\|\\\\]+)($|\\s(.*)$)#si', $string, $match)) { $property = $match[1]; $type = $match[2]; $string = $match[3]; } else { if (preg_match('#^\\$([a-z0-9_]+)($|\\s(.*)$)#si', $string, $match)) { $property = $match[1]; $type = 'mixed'; $string = $match[2]; } else { $tmp = $mode === MetaData::READ ? '-read' : ''; throw new AnnotationMetaDataException("Invalid annotation format '@property{$tmp} {$string}' in {$class}"); } } } $propertyName = $property; $property = $metaData->addProperty($propertyName, $type, $mode, $class); $this->property = array($propertyName, $property); $string = preg_replace_callback('#\\{\\s*([^\\s\\}\\{]+)(?:\\s+([^\\}\\{]*))?\\s*\\}#si', array($this, 'callOnMacro'), $string); $this->property = NULL; if (preg_match('#\\{|\\}#', $string)) { $string = trim($string); throw new AnnotationMetaDataException("Invalid annotation format, extra curly bracket '{$string}' in {$class}::\${$propertyName}"); } }
/** * Jestli je parametr ke cteni nebo jen pro zapis * @param int MetaData::READ|MetaData::READWRITE * @param MetaData * @return MetaDataProperty $this */ protected function setAccess($access, MetaData $meta) { if ($access === NULL) { $access = MetaData::READWRITE; } if ($access === MetaData::WRITE) { throw new MetaDataException("Neni mozne vytvaret write-only polozky: {$this->class}::\${$this->name}"); } if (!in_array($access, array(MetaData::READ, MetaData::READWRITE), true)) { throw new MetaDataException(__CLASS__ . ' access is Orm\\MetaData::READ or Orm\\MetaData::READWRITE allowed'); } $methods = $meta->getMethods($this->name); if ($methods['is'] and $this->data['types'] === array('bool' => 'bool')) { $methods['get'] = $methods['is']; } $this->data['get'] = $access & MetaData::READ ? array('method' => $methods['get']) : NULL; $this->data['set'] = $access & MetaData::WRITE ? array('method' => $methods['set']) : NULL; return $this; }
/** * @param MetaData $metaData * @param string $string * @param string $mode ::READWRITE|MetaData::READ|MetaData::WRITE * @param string $class * @param ReflectionClass $r */ private function addProperty(MetaData $metaData, $string, $mode, $class, ReflectionClass $r) { if ($mode === MetaData::READWRITE) { if (preg_match('#^(-read|-write)?\\s?(.*)$#si', $string, $match)) { $mode = $match[1]; $mode = ((!$mode or $mode === '-read') ? MetaData::READ : 0) | ((!$mode or $mode === '-write') ? MetaData::WRITE : 0); $string = $match[2]; } } if (preg_match('#^([a-z0-9_\\[\\]\\|\\\\]+)\\s+\\$([a-z0-9_]+)($|\\s(.*)$)#si', $string, $match)) { $property = $match[2]; $type = $match[1]; $string = $match[3]; } else { if (preg_match('#^\\$([a-z0-9_]+)\\s+([a-z0-9_\\|\\\\]+)($|\\s(.*)$)#si', $string, $match)) { $property = $match[1]; $type = $match[2]; $string = $match[3]; } else { if (preg_match('#^\\$([a-z0-9_]+)($|\\s(.*)$)#si', $string, $match)) { $property = $match[1]; $type = 'mixed'; $string = $match[2]; } else { $tmp = $mode === MetaData::READ ? '-read' : ''; throw new AnnotationMetaDataException("Invalid annotation format '@property{$tmp} {$string}' in {$class}"); } } } if (strpos(strToLower($string), '{ignore}') !== FALSE) { return; } $propertyName = $property; // Support for simplified FQN '@property Foo' instead of '@property \App\Foo' $parts = explode('|', $type); foreach ($parts as &$part) { $fqn = NetteAnnotationsParser::expandClassName($part, $r); if (class_exists($fqn)) { $part = $fqn; } if ($part === Orm\OneToMany::class) { // Support for '@property OtM|Foo[]' instead of '@property Orm\OneToMany' $parts = [Orm\OneToMany::class]; break; } else { if ($part === Orm\ManyToMany::class) { // Support for '@property MtM|Foo[]' instead of '@property Orm\ManyToMany' $parts = [Orm\ManyToMany::class]; break; } else { if (substr($part, -2) === '[]') { $part = 'array'; } } } } $type = implode('|', $parts); $property = $metaData->addProperty($propertyName, $type, $mode, $class); $this->property = [$propertyName, $property]; $string = preg_replace_callback('#\\{\\s*([^\\s\\}\\{]+)(?:\\s+([^\\}\\{]*))?\\s*\\}#si', [$this, 'callOnMacro'], $string); $this->property = NULL; if (preg_match('#\\{|\\}#', $string)) { $string = trim($string); throw new AnnotationMetaDataException("Invalid annotation format, extra curly bracket '{$string}' in {$class}::\${$propertyName}"); } }