/** * Si $resursive es true, se cargan las asociaciones de la clase y tambien se pasan a json. */ public static function toJSON(PersistentObject $po, $recursive = false, $loopDetection = NULL, $currentPath = '') { if ($loopDetection === NULL) { $loopDetection = new ArrayObject(); } // Necesito hacer que cada nodo tenga una path para poder expresar las referencias por loops detectados. // La idea es simple: (ver http://goessner.net/articles/JsonPath/) // - vacio es la path que referencia al nodo raiz // - el nombre del atributo referencia a un objeto hasOne relacionado // - el nombre del atributo con notacion de array referencia a un objeto hasMany relacionado // p.e. x.store.book[0].title donde x es el objeto raiz, entonces una path valida es: .store.book[0].title $loopDetection[$currentPath] = $po->getClass() . '_' . $po->getId(); // Marca como recorrido: TODO falta la marca de loop cuando se detecta. $json = "{"; $i = 0; $n = count($po->getAttributeTypes()) - 1; foreach ($po->getAttributeTypes() as $attr => $type) { $value = $po->aGet($attr); if (is_bool($value)) { $value ? $value = 'true' : ($value = 'false'); } // Si no esta esto, aparece 1 para true y nada para false. $json .= '"' . $attr . '" : "' . $value . '"'; // TODO: si es numero, no poner comillas if ($i < $n) { $json .= ", "; } $i++; } // Agrega errores de validacion si los hay // http://code.google.com/p/yupp/issues/detail?id=86 if ($po->getErrors()->hasErrors()) { $errors = $po->getErrors(); $json .= ', "errors": {'; foreach ($errors as $attr => $theErrors) { $json .= '"' . $attr . '": ['; foreach ($theErrors as $theError) { $json .= '"' . $theError . '", '; } $json = substr($json, 0, -2); // Saco ', ' del final $json .= '], '; } $json = substr($json, 0, -2); // Saco ', ' del final $json .= '}'; } if ($recursive) { foreach ($po->getHasOne() as $attr => $clazz) { $relObj = $po->aGet($attr); if ($relObj !== NULL) { if (!in_array($relObj->getClass() . '_' . $relObj->getId(), (array) $loopDetection)) { // FIXME: las tags de los atributos hijos de la instancia raiz deberian // tener su nombre igual al nombre del atributo, no el nombre de // la clase. Con este codigo es el nombre de la clase. $json .= ', "' . $attr . '": ' . self::toJSON($relObj, $recursive, $loopDetection, $currentPath . '.' . $attr); } else { // Agrego un objeto referencia $keys = array_keys((array) $loopDetection, $relObj->getClass() . '_' . $relObj->getId()); $path = $keys[0]; $json .= ', "' . $attr . '": "' . $path . '"'; } } } foreach ($po->getHasMany() as $attr => $clazz) { // TODO: type de la coleccion, en el de XML tengo: //$hm_node->setAttribute( 'type', $obj->getHasManyType($attr) ); // list, colection, set //$hm_node->setAttribute( 'of', $obj->getAttributeType($attr) ); // clase de las instancias que contiene la coleccion $relObjs = $po->aGet($attr); if (count($relObjs) > 0) { $json .= ', "' . $attr . '": '; $json .= self::toJSONArray($relObjs, $recursive, $attr, $loopDetection, $currentPath); /* $json .= ', "'. $attr .'": ['; echo "attr hasMany $attr<br/>"; $idx = 0; // Se usa para la referencia por loop en la JSON path foreach ($relObjs as $relObj) { echo 'relObj: '. $relObj->getClass().'_'.$relObj->getId() .'<br/>'; if(!in_array($relObj->getClass().'_'.$relObj->getId(), (array)$loopDetection)) // si no esta marcado como recorrido { $json .= self::toJSON( $relObj, $recursive, $loopDetection, $currentPath.'.'.$attr.'['.$idx.']' ) .', '; } else // referencia por loop { // Agrego un objeto referencia $keys = array_keys((array)$loopDetection, $relObj->getClass().'_'.$relObj->getId()); $path = $keys[0]; $json .= '"'.$attr.'": "'. $path .'", '; } $idx++; } $json = substr($json, 0, -2); // Saco ', ' del final $json .= ']'; */ } } } $json .= '}'; return $json; }
/** * Devuelve HTML para edicion de un objeto como una tabla con 2 columnas, la primera de nombres de campos la segunda con campos con valores para modificar. */ private static function display_edit(PersistentObject $po) { $res = '<table>'; $attrs = $po->getAttributeTypes(); foreach ($attrs as $attr => $type) { // Los atributos inyectados no se deberian poder editar! $res .= '<tr><td>'; $res .= $attr; // TODO: Habria que ver si esto es i18n, deberia haber algun "display name" asociado al nombre del campo. $res .= '</td><td>'; if ($po->isInyectedAttribute($attr)) { //$res .= $po->aGet($attr); $res .= self::field_to_html_show($attr, $type, $po->aGet($attr)); } else { if (DatabaseNormalization::isSimpleAssocName($attr)) { // Si es un fk a un id de un hasOne, quiero mostrar una lista de los posibles ids // de la clase de la relacion HO que le puedo asignar, y como esto es create o edit, // si tiene un valor actual, deberia quedar seleccionado en el select. $currentValue = $po->aGet($attr); // Puede ser NULL $role = DatabaseNormalization::getSimpleAssocName($attr); // email_id -> email $relClass = $po->getAttributeType($role); // Clase de la relacion HO // Objetos que puedo tener relacionadoss // Se puede en PHP 5.3.0... //$list = $relClass::listAll(new ArrayObject()); // Objetos que podria tener asociados // ... pero por las dudas ... $list = call_user_func_array(array($relClass, 'listAll'), array(new ArrayObject())); $select = '<select name="' . $attr . '"><option value=""></option>'; foreach ($list as $relObj) { $sel = $currentValue == $relObj->getId() ? ' selected="true"' : ''; $select .= '<option value="' . $relObj->getId() . '"' . $sel . '>' . $relClass . '[' . $relObj->getId() . ']</option>'; // TODO: Si se tuviera un toString en la clase se mostraria mejor } $select .= '</select>'; $res .= $select; } else { $maxStringLength = NULL; if ($type === Datatypes::TEXT) { $maxLengthConstraint = $po->getConstraintOfClass($attr, 'MaxLengthConstraint'); if ($maxLengthConstraint !== NULL) { $maxStringLength = $maxLengthConstraint->getValue(); } } $res .= self::field_to_html_edit($attr, $type, $po->aGet($attr), $maxStringLength); // Si el campo tiene errores, los muestro if ($po->getErrors()->hasFieldErrors($attr)) { $res .= '<div class="errors">'; $res .= self::fieldErrors($po, $attr); $res .= '</div>'; } } } $res .= '</td></tr>'; } $res .= '</table>'; return $res; }