/** * Default run method for default 'write-typed' controller * * Save data from the posted form into the first parameter object using standard method. * Create a new instance of this object if no identifier was given. * * @param $parameters Parameters * @param $form array * @param $files array * @param $class_name string * @return string * @throws Exception */ public function run(Parameters $parameters, $form, $files, $class_name) { $object = $parameters->getMainObject($class_name); Dao::begin(); try { $builder = new Post_Files(); $form = $builder->appendToForm($form, $files); $builder = new Object_Builder_Array(); $builder->null_if_empty_sub_objects = true; $builder->build($form, $object); $write_objects = []; foreach ($builder->getBuiltObjects() as $write_object) { if ($write_object == $object || Dao::getObjectIdentifier($write_object)) { $write_objects[] = $write_object; } } $write_error = false; foreach ($write_objects as $write_object) { if (!Dao::write($write_object)) { $write_error = true; break; } } $write_error ? Dao::rollback() : Dao::commit(); } catch (Exception $exception) { Dao::rollback(); throw $exception; } $parameters = $this->getViewParameters($parameters, $class_name, $write_error); return View::run($parameters, $form, $files, $class_name, Feature::F_WRITE); }
/** * @param $result Table */ public function afterHtmlBuilderMultipleBuild(Table $result) { if ($this->in_multiple == 'build') { $table = $result; $length = count($table->body->rows) - 1; if ($this->count->count > $length) { // vertical scrollbar $vertical_scroll_bar = new Standard_Cell(); $vertical_scroll_bar->addClass('vertical'); $vertical_scroll_bar->addClass('scrollbar'); $vertical_scroll_bar->setAttribute('rowspan', 1000000); $vertical_scroll_bar->setData('start', 0); $vertical_scroll_bar->setData('length', $length); $vertical_scroll_bar->setData('total', $this->count->count); $link = '/Html_Edit_Multiple/output/' . Namespaces::shortClassName($this->property->getDeclaringClass()) . SL . Dao::getObjectIdentifier($this->property->getObject()) . SL . $this->property->name . SL . '?move='; $up = new Anchor($link . 'up'); $up->addClass('up'); $position = new Anchor($link . 1); $position->addClass('position'); $down = new Anchor($link . 'down'); $down->addClass('down'); $vertical_scroll_bar->setContent($up . $position . $down); // add vertical scrollbar cells to multiple (collection or map) table $table->head->rows[0]->addCell(new Header_Cell(), 0); $table->body->rows[0]->addCell($vertical_scroll_bar, 0); } $this->in_multiple = ''; } }
/** * @param $object object * @return string[] combo filters */ protected function getFilters($object) { $filters = ['id' => Func::notEqual(Dao::getObjectIdentifier($object))]; foreach ((new Reflection_Class(get_class($object)))->getProperties([T_EXTENDS, T_USE]) as $property) { if ($property->getAnnotation('replace_filter')->value) { $filters[$property->name] = Dao::getObjectIdentifier($object, $property->name); } } return $filters; }
/** * Run the default json controller * * @param $parameters Parameters * @param $form array * @param $files array * @param $class_name string * @return string */ public function run(Parameters $parameters, $form, $files, $class_name) { $parameters = $parameters->getObjects(); // read all objects corresponding to class name if (!$parameters) { return json_encode(Dao::readAll(Names::setToClass($class_name, false), [Dao::sort()])); } // read object $first_parameter = reset($parameters); if (is_object($first_parameter)) { return json_encode($first_parameter); } // search objects for autocomplete combo pull-down list if (isset($parameters['term'])) { $element_class_name = Names::setToClass($class_name, false); $search = null; if (!empty($parameters['term'])) { $search = (new Search_Array_Builder())->buildMultiple(new Reflection_Class($element_class_name), $parameters['term'], '', '%'); } if (isset($parameters['filters']) && $parameters['filters']) { if (!(is_object($search) && $search->isAnd())) { $search = Dao\Func::andOp($search ? [$search] : []); } foreach ($parameters['filters'] as $filter_name => $filter_value) { $search->arguments[$filter_name] = $filter_value[0] == '!' ? Dao\Func::notEqual(substr($filter_value, 1)) : $filter_value; } if (count($search->arguments) == 1) { reset($search->arguments); $search = [key($search->arguments) => current($search->arguments)]; } } $objects = []; // first object only if (isset($parameters['first']) && $parameters['first']) { $objects = Dao::search($search, $element_class_name, [Dao::sort(), Dao::limit(1)]); $source_object = $objects ? reset($objects) : Builder::create($element_class_name); return json_encode(new Autocomplete_Entry(Dao::getObjectIdentifier($source_object), strval($source_object))); } else { $search_options = [Dao::sort()]; if (isset($parameters['limit'])) { $search_options[] = Dao::limit($parameters['limit']); } foreach (Dao::search($search, $element_class_name, $search_options) as $source_object) { $objects[] = new Autocomplete_Entry(Dao::getObjectIdentifier($source_object), strval($source_object)); } return json_encode($objects); } } elseif (isset($parameters['id'])) { $element_class_name = Names::setToClass($class_name); $source_object = Dao::read($parameters['id'], $element_class_name); return json_encode(new Autocomplete_Entry(Dao::getObjectIdentifier($source_object), strval($source_object))); } return ''; }
/** * @param $replaced object * @param $replacement object * @return boolean true if replacement and deletion have been done */ public function deleteAndReplace($replaced, $replacement) { if (Dao::getObjectIdentifier($replaced) && Dao::getObjectIdentifier($replacement)) { if ($this->replace($replaced, $replacement)) { if ($this->delete($replaced)) { return true; } } } return false; }
/** * Gets sql elements for a mapping between two objects * * @example * list($table, $field1, $field2, $id1, $id2) = Sql_Map_Builder::sqlElementsOf( (...) ); * * @param $object object the source object * @param $property Reflection_Property the property of the source object used for the mapping * @param $foreign_object object the mapped object * @return array */ public static function sqlElementsOf($object, $property, $foreign_object) { // build table and fields $sql_link = new Link_Table($property); $table = $sql_link->table(); $field1 = $sql_link->masterColumn(); $field2 = $sql_link->foreignColumn(); // build values $id1 = Dao::getObjectIdentifier($object, 'id'); $id2 = Dao::getObjectIdentifier($foreign_object, 'id'); // return elements return [$table, $field1, $field2, $id1, $id2]; }
/** * Remove an object from an objects array * * @param $element object|object[] */ public function remove($element) { if (is_array($element)) { foreach ($element as $elem) { $key = Dao::getObjectIdentifier($elem); if (isset($this->objects[$key]) || array_key_exists($key, $this->objects)) { unset($this->objects[$key]); } } } else { $key = Dao::getObjectIdentifier($element); if (isset($this->objects[$key]) || array_key_exists($key, $this->objects)) { unset($this->objects[$key]); } } }
/** * 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)]); } } }
/** * Parse a variable / function / include and returns its return value * * @param $property_name string can be an unique var or path.of.vars * @param $format_value boolean * @return string var value after reading value / executing specs (can be an object) */ protected function parseSingleValue($property_name, $format_value = true) { $property = $source_object = reset($this->objects); if ($property instanceof Reflection_Property_Value && $property_name == 'value') { if (($builder = $property->getAnnotation('widget')->value) && is_a($builder, Property::class, true)) { $builder = Builder::create($builder, [$property, $property->value(), $this]); /** @var $builder Property */ $builder->parameters[Feature::F_EDIT] = Feature::F_EDIT; $value = $builder->buildHtml(); if ($builder instanceof Value_Widget) { $value = (new Html_Builder_Property($property, $value))->setTemplate($this)->build(); } } else { $value = $property->getType()->isBoolean() ? $property->value() : parent::parseSingleValue($property_name, false); if (($preprop = lLastParse($property->pathAsField(), '[', 1, false)) && (!isset($this->cache[self::PARSED_ID]) || !isset($this->cache[self::PARSED_ID][$this->getFormId()]) || !isset($this->cache[self::PARSED_ID][$this->getFormId()][$preprop]))) { $this->cache[self::PARSED_ID][$this->getFormId()][$preprop] = true; if ($property instanceof Reflection_Property_Value) { $parent_object = $property->getObject(); $id = isset($parent_object) ? Dao::getObjectIdentifier($parent_object) : null; $html_builder_type = new Html_Builder_Type('id', null, $id, $preprop); $id_value = $html_builder_type->setTemplate($this)->build(); } else { $id_value = ''; } } else { $id_value = ''; } if ($property->getAnnotation('output')->value == 'string') { $property->setAnnotationLocal('var')->value = 'string'; $value = isset($value) ? strval($value) : null; $id_value = ''; } $value = $id_value . (new Html_Builder_Property($property, $value))->setTemplate($this)->build(); } } else { $value = parent::parseSingleValue($property_name); } return $value; }
/** * Delete an object * * @param $parameters mixed[] * - first : the deleted object * - other parameters are not sent to the delete controller (only as_widget is kept) * @return mixed */ private function deleteObject($parameters) { $object = array_shift($parameters); $controller_uri = SL . Names::classToUri(get_class($object)) . SL . Dao::getObjectIdentifier($object) . SL . Feature::F_DELETE; return (new Main())->runController($controller_uri, $parameters); }
/** * @return string */ public function serialize() { $data = ['current' => [], 'plugins' => $this->plugins]; if (isset($this->current)) { foreach ($this->current as $class_name => $object) { if (is_object($object)) { $object = [$class_name, Dao::getObjectIdentifier($object) ?: serialize($object)]; } $data['current'][$class_name] = $object; } } return serialize($data); }
/** * Generates a link for to an object and feature, using parameters if needed * * @param $object object|string linked object or class name * @param $feature string linked feature name * @param $parameters string|string[]|object|object[] optional parameters list * @param $arguments string|string[] optional arguments list * @return string */ public function link($object, $feature = null, $parameters = null, $arguments = null) { // class name : not Built, not Set $class_names = is_string($object) ? $object : get_class($object); $class_name = Names::setToClass($class_names, false); $set_class = $class_name != $class_names; while (Builder::isBuilt($class_name)) { $class_name = get_parent_class($class_name); } if ($set_class) { $class_name = Names::classToSet($class_name); } // build uri $link = str_replace(BS, SL, is_object($object) && Dao::getObjectIdentifier($object) ? $class_name . SL . Dao::getObjectIdentifier($object) : $class_name); if (isset($feature)) { $link .= SL . $feature; } if (isset($parameters)) { if (!is_array($parameters)) { $parameters = [$parameters]; } foreach ($parameters as $key => $value) { if (!is_numeric($key)) { $link .= SL . $key; } if (is_object($value)) { $link .= SL . Names::classToUri(get_class($value)) . SL . Dao::getObjectIdentifier($value); } else { $link .= SL . $value; } } } // build arguments if (!empty($arguments)) { if (!is_array($arguments)) { $link .= '?' . urlencode($arguments); } else { $link .= '?'; $first = true; foreach ($arguments as $key => $value) { if ($first) { $first = false; } else { $link .= '&'; } $link .= $key . '=' . urlencode($value); } } } return SL . $link; }
/** * Generic getter for mapped objects * * @param $stored object[] actual value of the property (will be returned if not null) * @param $object object the parent object * @param $property string|Reflection_Property the source property (or name) for map reading * @return Component[] */ public static function &getMap(&$stored, $object, $property) { if (!(self::$ignore || isset($stored))) { if (Dao::getObjectIdentifier($object)) { if (!$property instanceof Reflection_Property) { $property = new Reflection_Property(get_class($object), $property); } $dao = ($dao = $property->getAnnotation('dao')->value) ? Dao::get($dao) : Dao::current(); $class_name = get_class($object); $linked_class_name = (new Link_Class($class_name))->getLinkedClassName(); if ($linked_class_name) { $object = (new Link_Class($class_name))->getCompositeProperty()->getValue($object); $class_name = $linked_class_name; } $element_type = $property->getType()->getElementType(); $is_abstract = $element_type->asReflectionClass()->isAbstract(); $sort = $is_abstract ? Dao::sort(['id']) : Dao::sort(); $stored = $dao->search([$class_name . '->' . $property->name => $object], $element_type->asString(), [$sort]); if ($is_abstract) { $sort->sortObjects($stored); } } else { $stored = []; } } return $stored; }
/** * Gets the URI of a class name or object * * @example class name User : '******' * @example User object of id = 1 : 'SAF/Framework/User/1' * @param $class_name object|string * @return string */ public static function classToUri($class_name) { // get object id, if object if (is_object($class_name)) { $id = Dao::getObjectIdentifier($class_name, 'id'); $class_name = get_class($class_name); } // link classes : get linked class while ((new Reflection_Class($class_name))->getAnnotation('link')->value) { $class_name = get_parent_class($class_name); } // built classes : get object class $built_path = Application::current()->getNamespace() . BS . 'Built' . BS; while (substr($class_name, 0, strlen($built_path)) == $built_path) { $class_name = get_parent_class($class_name); } // replace \ by / return str_replace(BS, SL, $class_name) . (isset($id) ? SL . $id : ''); }
/** * @param $conditions string[] the key is the name of the condition, the value is the name of the * value that enables the condition * @param $filters string[] the key is the name of the filter, the value is the name of the form * element containing its value * @return string */ public function buildObject($conditions = null, $filters = null) { $class_name = $this->type->asString(); // visible input $input = new Input(null, strval($this->value)); $input->setAttribute('autocomplete', 'off'); $input->setAttribute('data-combo-class', Names::classToSet($class_name)); if (!$this->readonly) { if ($filters) { $html_filters = []; $old_name = $this->name; foreach ($filters as $filter_name => $filter_value) { $this->name = $filter_value; $name = $this->getFieldName('', false); $html_filters[] = $filter_name . '=' . $name; } $this->name = $old_name; $input->setAttribute('data-combo-filters', join(',', $html_filters)); } if ($conditions) { $html_conditions = []; $old_name = $this->name; foreach ($conditions as $condition_name => $condition_value) { $this->name = $condition_name; $name = $this->getFieldName('', false); $html_conditions[] = $name . '=' . $condition_value; } $this->name = $old_name; $input->setAttribute('data-conditions', join(';', $html_conditions)); } $input->addClass('autowidth'); $input->addClass('combo'); // id input $id_input = new Input($this->getFieldName('id_'), $this->value ? Dao::getObjectIdentifier($this->value) : ''); $id_input->setAttribute('type', 'hidden'); $id_input->addClass('id'); // 'add' / 'edit' anchor $fill_combo = isset($this->template) ? ['fill_combo' => $this->template->getFormId() . DOT . $this->getFieldName('id_', false)] : ''; $edit = new Anchor(View::current()->link($this->value ? get_class($this->value) : $class_name, Feature::F_ADD, null, $fill_combo), 'edit'); $edit->addClass('edit'); $edit->setAttribute('target', Target::BLANK); $edit->setAttribute('title', '|Edit ¦' . Names::classToDisplay($class_name) . '¦|'); // 'more' button $more = new Button('more'); $more->addClass('more'); $more->setAttribute('tabindex', -1); $this->setOnChangeAttribute($id_input); return $id_input . $input . $more . $edit; } return $input; }