/** * @param $class Reflection_Class * @param $composite_class_name string * @return Reflection_Property_Value[] */ protected function getProperties(Reflection_Class $class, $composite_class_name = null) { $properties = []; if (isset($composite_class_name) && isA($class->name, Component::class)) { $composite_property = call_user_func([$class->name, 'getCompositeProperties'], $composite_class_name); $composite_property = reset($composite_property); } else { $composite_property = null; } if ($class->getAnnotation('link')->value) { $link_class = new Link_Class($class->name); $composite_link_property = $link_class->getCompositeProperty(); foreach ($link_class->getProperties([T_EXTENDS, T_USE]) as $property) { if ((!$composite_property || $property->name !== $composite_property->name) && (!$composite_link_property || $property->name !== $composite_link_property->name) && !$property->isStatic() && !$property->getListAnnotation('user')->has(User_Annotation::INVISIBLE)) { $properties[] = $property; } } } else { foreach ($class->getProperties([T_EXTENDS, T_USE]) as $property) { if ((empty($composite_property) || $property->name !== $composite_property->name) && !$property->isStatic() && !$property->getListAnnotation('user')->has(User_Annotation::INVISIBLE)) { $properties[] = $property; } } } return $properties; }
/** * To use this : * - Create your own writeSubClassNames() method * - Your method has no parameters * - Your method returns nothing * - Call return writeSub('sub_class_names', 'super_class_name') using your two properties names * * @param $sub string sub property name ie 'sub_class_names' * @param $super string super property name ie 'super_class_name' */ private function writeSub($sub, $super) { $written = []; // update $super_property into new $sub_properties foreach ($this->{$sub} as $sub) { if (!Dao::is($this, $sub->{$super})) { $sub->{$super} = $this; Dao::write($sub, [Dao::only($super)]); } $written[Dao::getObjectIdentifier($sub)] = true; } // empty $super_property from removed $sub_properties $subs = Dao::search([$super => $this], Link_Class::linkedClassNameOf($this)); foreach ($subs as $sub) { if (!isset($written[Dao::getObjectIdentifier($sub)])) { $sub->{$super} = null; Dao::write($sub, [Dao::only($super)]); } } }
/** * Create a clone of the object, using a built class if needed * * @param $object object * @param $class_name string if set, the new object will use the matching built class * this class name must inherit from the object's class * @param $properties_values array some properties values for the cloned object * @param $same_identifier boolean * @return object */ public static function createClone($object, $class_name = null, $properties_values = [], $same_identifier = true) { $class_name = self::className($class_name); $source_class_name = get_class($object); if (!isset($class_name)) { $class_name = self::className($source_class_name); } if ($class_name !== $source_class_name) { // initialises cloned object $clone = self::create($class_name); $destination_class = new Link_Class($class_name); // deactivate AOP if (isset($clone->_)) { $save_aop = $clone->_; unset($clone->_); } // copy official properties values from the source object $properties = (new Reflection_Class($source_class_name))->accessProperties(); foreach ($properties as $property) { if (!isset($save_aop[$property->name])) { $property->setValue($clone, $property->getValue($object)); } } // copy unofficial properties values from the source object (ie AOP properties aliases) // clone collection objects using the destination collection property type $clone_collection = []; foreach (get_object_vars($object) as $property_name => $value) { if ($property_name !== '_' && !isset($properties[$property_name])) { $clone->{$property_name} = $value; if (isset($properties[rtrim($property_name, '_')])) { $property = $properties[rtrim($property_name, '_')]; if ($property->getAnnotation('link') == Link_Annotation::COLLECTION) { $element_class_from = $property->getType()->getElementTypeAsString(); $property = $destination_class->getProperty($property->name); $element_class_to = $property->getType()->getElementTypeAsString(); if ($element_class_to != $element_class_from) { $clone_collection[substr($property_name, 0, -1)] = $element_class_to; } } } } } // reactivate AOP if (isset($save_aop)) { $clone->_ = $save_aop; } foreach ($clone_collection as $property_name => $element_class_to) { $elements = []; foreach ($object->{$property_name} as $key => $element) { $elements[$key] = Builder::createClone($element, $element_class_to, [], $same_identifier); } $clone->{$property_name} = $elements; } // linked class object to link class object : store source object to linked object $destination_class = new Link_Class($class_name); if ($linked_class_name = $destination_class->getLinkedClassName()) { if ($linked_class_name == $source_class_name) { $destination_class->getLinkProperty()->setValue($clone, $object); } } // identify destination object = source object, or disconnect destination object if ($same_identifier) { Dao::replace($clone, $object, false); } else { Dao::disconnect($clone); } } else { $clone = clone $object; } // copy added properties values to the cloned object if ($properties_values) { $properties = (new Reflection_Class($class_name))->accessProperties(); foreach ($properties_values as $property_name => $value) { $properties[$property_name]->setValue($clone, $value); } } return $clone; }
/** * Adds properties of the class name into $properties * * Please always call this instead of adding properties manually : it manages 'link' * class annotations. * * @param $path string * @param $class_name string * @param $join_mode string */ private function addProperties($path, $class_name, $join_mode = null) { $class = new Link_Class($class_name); $this->properties[$class_name] = $class->getProperties([T_EXTENDS, T_USE]); $linked_class_name = $class->getAnnotation('link')->value; if ($linked_class_name) { $this->addLinkedClass($path, $class, $linked_class_name, $join_mode); } }
/** * @param $class_name string * @param $search array * @return array */ private function createArrayReference($class_name, $search) { $array = isset($search) ? [Builder::fromArray($class_name, $search)] : null; $class = new Link_Class($class_name); $link_class = $class->getAnnotation('link')->value; if ($link_class) { $object = reset($array); $link_search = Builder::create($link_class); $composite_property_name = $class->getCompositeProperty()->name; foreach (array_keys($class->getLinkedProperties()) as $property_name) { if (isset($search[$property_name])) { $link_search->{$property_name} = $search[$property_name]; } } $object->{$composite_property_name} = Dao::searchOne($link_search) ?: $link_search; } return $array; }
/** * @param $object object * @param $property Reflection_Property * @param $map object[] */ private function writeMap($object, Reflection_Property $property, $map) { // old map $class = new Link_Class(get_class($object)); $composite_property_name = $class->getAnnotation('link')->value ? $class->getCompositeProperty()->name : null; $old_object = Search_Object::create(Link_Class::linkedClassNameOf($object)); $this->setObjectIdentifier($old_object, $this->getObjectIdentifier($object, $composite_property_name)); $aop_getter_ignore = Getter::$ignore; Getter::$ignore = false; $old_map = $property->getValue($old_object); Getter::$ignore = $aop_getter_ignore; // map properties : write each of them $insert_builder = new Map_Insert($property); $id_set = []; foreach ($map as $element) { $id = $this->getObjectIdentifier($element) ?: $this->getObjectIdentifier($this->write($element)); if (!isset($old_map[$id])) { $query = $insert_builder->buildQuery($object, $element); $this->connection->query($query); } else { $id_set[$id] = true; } } // remove old unused elements $delete_builder = new Map_Delete($property); foreach ($old_map as $old_element) { $id = $this->getObjectIdentifier($old_element); if (!isset($id_set[$id])) { $query = $delete_builder->buildQuery($object, $old_element); $this->connection->query($query); } } }
/** * Build SQL WHERE section for an object * * @param $path string Base property path pointing to the object * @param $object object The value is an object, which will be used for search * @return string */ private function buildObject($path, $object) { $class = new Link_Class(get_class($object)); $id = $this->sql_link->getObjectIdentifier($object, $class->getAnnotation('link')->value ? $class->getCompositeProperty()->name : null); if ($id) { // object is linked to stored data : search with object identifier return $this->buildValue($path, $id, $path == 'id' ? '' : 'id_'); } // object is a search object : each property is a search entry, and must join table $this->joins->add($path); $array = []; $class = new Reflection_Class(get_class($object)); foreach ($class->accessProperties() as $property_name => $property) { if (isset($object->{$property_name})) { $sub_path = $property_name; $array[$sub_path] = $object->{$property_name}; } } $sql = $this->buildArray($path, $array, 'AND'); if (!$sql) { $sql = 'FALSE'; } return $sql; }