Complex classes like UbiquityMyAdminViewer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use UbiquityMyAdminViewer, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 27 | */ |
||
| 28 | class UbiquityMyAdminViewer { |
||
| 29 | /** |
||
| 30 | * @var JsUtils |
||
| 31 | */ |
||
| 32 | private $jquery; |
||
| 33 | |||
| 34 | /** |
||
| 35 | * @var UbiquityMyAdminBaseController |
||
| 36 | */ |
||
| 37 | private $controller; |
||
| 38 | |||
| 39 | public function __construct(UbiquityMyAdminBaseController $controller){ |
||
| 40 | $this->jquery=$controller->jquery; |
||
| 41 | $this->controller=$controller; |
||
| 42 | } |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Returns the form for adding or modifying an object |
||
| 46 | * @param string $identifier |
||
| 47 | * @param object $instance the object to add or modify |
||
| 48 | * @return DataForm |
||
| 49 | */ |
||
| 50 | public function getForm($identifier,$instance){ |
||
| 51 | $form=$this->jquery->semantic()->dataForm($identifier, $instance); |
||
| 52 | $className=\get_class($instance); |
||
| 53 | $fields=$this->controller->_getAdminData()->getFormFieldNames($className); |
||
| 54 | $form->setFields($fields); |
||
| 55 | |||
| 56 | $fieldTypes=OrmUtils::getFieldTypes($className); |
||
| 57 | foreach ($fieldTypes as $property=>$type){ |
||
| 58 | switch ($type){ |
||
| 59 | case "boolean": case "bool": |
||
| 60 | $form->fieldAsCheckbox($property); |
||
| 61 | break; |
||
| 62 | case "int": case "integer": |
||
| 63 | $form->fieldAsInput($property,["inputType"=>"number"]); |
||
| 64 | break; |
||
| 65 | } |
||
| 66 | } |
||
| 67 | $this->relationMembersInForm($form, $instance, $className); |
||
| 68 | $form->setCaptions($this->getFormCaptions($form->getInstanceViewer()->getVisibleProperties(),$className,$instance)); |
||
| 69 | $form->setSubmitParams($this->controller->_getAdminFiles()->getAdminBaseRoute()."/update", "#table-details"); |
||
| 70 | return $form; |
||
| 71 | } |
||
| 72 | |||
| 73 | /** |
||
| 74 | * Condition to determine if the edit or add form is modal for $model objects |
||
| 75 | * @param array $objects |
||
| 76 | * @param string $model |
||
| 77 | * @return boolean |
||
| 78 | */ |
||
| 79 | public function isModal($objects,$model){ |
||
| 80 | return \count($objects)>20; |
||
| 81 | } |
||
| 82 | |||
| 83 | /** |
||
| 84 | * Returns the captions for list fields in showTable action |
||
| 85 | * @param array $captions |
||
| 86 | * @param string $className |
||
| 87 | */ |
||
| 88 | public function getCaptions($captions,$className){ |
||
| 89 | return array_map("ucfirst", $captions); |
||
| 90 | } |
||
| 91 | |||
| 92 | /** |
||
| 93 | * Returns the captions for form fields |
||
| 94 | * @param array $captions |
||
| 95 | * @param string $className |
||
| 96 | */ |
||
| 97 | public function getFormCaptions($captions,$className,$instance){ |
||
| 98 | return array_map("ucfirst", $captions); |
||
| 99 | } |
||
| 100 | |||
| 101 | public function getFkHeaderElement($member,$className,$object){ |
||
| 102 | return new HtmlHeader("",4,$member,"content"); |
||
| 103 | } |
||
| 104 | |||
| 105 | public function getFkHeaderList($member,$className,$list){ |
||
| 106 | return new HtmlHeader("",4,$member." (".\count($list).")","content"); |
||
| 107 | } |
||
| 108 | |||
| 109 | /** |
||
| 110 | * @param string $member |
||
| 111 | * @param string $className |
||
| 112 | * @param object $object |
||
| 113 | * @return BaseHtml |
||
| 114 | */ |
||
| 115 | public function getFkElement($member,$className,$object){ |
||
| 116 | return $this->jquery->semantic()->htmlLabel("element-".$className.".".$member,$object.""); |
||
| 117 | } |
||
| 118 | |||
| 119 | /** |
||
| 120 | * @param string $member |
||
| 121 | * @param string $className |
||
| 122 | * @param array|\Traversable $list |
||
| 123 | * @return HtmlCollection |
||
| 124 | */ |
||
| 125 | public function getFkList($member,$className,$list){ |
||
| 126 | $element=$this->jquery->semantic()->htmlList("list-".$className.".".$member); |
||
| 127 | return $element->addClass("animated divided celled"); |
||
| 128 | } |
||
| 129 | |||
| 130 | public function displayFkElementList($element,$member,$className,$object){ |
||
| 131 | |||
| 132 | } |
||
| 133 | |||
| 134 | public function getMainMenuElements(){ |
||
| 135 | return ["models"=>["Models","sticky note","Used to perform CRUD operations on data."], |
||
| 136 | "routes"=>["Routes","car","Displays defined routes with annotations"], |
||
| 137 | "controllers"=>["Controllers","heartbeat","Displays controllers and actions"], |
||
| 138 | "cache"=>["Cache","lightning","Annotations, models, router and controller cache"], |
||
| 139 | "config"=>["Config","settings","Configuration variables"] |
||
| 140 | ]; |
||
| 141 | } |
||
| 142 | |||
| 143 | public function getRoutesDataTable($routes){ |
||
| 144 | $dt=$this->jquery->semantic()->dataTable("dtRoutes", "micro\controllers\admin\popo\Route", $routes); |
||
| 145 | $dt->setFields(["path","methods","controller","action","parameters","cache","duration","name","expired"]); |
||
| 146 | $dt->setCaptions(["Path","Methods","Controller","Action","Parameters","Cache","Duration","Name","Expired",""]); |
||
| 147 | $dt->fieldAsLabel("path","car"); |
||
| 148 | $dt->fieldAsCheckbox("cache",["disabled"=>"disabled"]); |
||
| 149 | $dt->setValueFunction("methods", function($v){return (\is_array($v))?"[".\implode(", ", $v)."]":$v;}); |
||
| 150 | $dt->setValueFunction("parameters", function($v){return (\is_array($v))?"[".\implode(", ", $v)."]":$v;}); |
||
| 151 | $dt->setValueFunction("expired", function($v,$instance,$index){ |
||
| 152 | $icon=null;$expired=null; |
||
| 153 | if($instance->getCache()){ |
||
| 154 | if(\sizeof($instance->getParameters())===0 || $instance->getParameters()===null) |
||
| 155 | $expired=CacheManager::isExpired($instance->getPath(),$instance->getDuration()); |
||
| 156 | if($expired===false){ |
||
| 157 | $icon=new HtmlIcon("", "toggle on"); |
||
| 158 | }elseif($expired===true){ |
||
| 159 | $icon=new HtmlIcon("", "toggle off"); |
||
| 160 | }else{ |
||
| 161 | $icon=new HtmlIcon("", "help"); |
||
| 162 | } |
||
| 163 | } |
||
| 164 | |||
| 165 | return $icon; |
||
| 166 | }); |
||
| 167 | $dt->onRowClick('$("#filter-routes").val($(this).find(".ui.label").text());'); |
||
| 168 | $dt->onPreCompile(function($dTable){ |
||
| 169 | $dTable->setColAlignment(6, TextAlignment::RIGHT); |
||
| 170 | $dTable->setColAlignment(8, TextAlignment::CENTER); |
||
| 171 | }); |
||
| 172 | $dt->addFieldButton("Get",true,function(&$bt,$instance){$bt->addClass("_get")->setProperty("data-url",$instance->getPath());}); |
||
| 173 | $dt->insertInFieldButton(9,"Post",true,function(&$bt,$instance){$bt->addClass("_post")->setProperty("data-url",$instance->getPath());}); |
||
| 174 | $dt->setActiveRowSelector("warning"); |
||
| 175 | return $dt; |
||
| 176 | } |
||
| 177 | |||
| 178 | public function getControllersDataTable($controllers){ |
||
| 179 | $dt=$this->jquery->semantic()->dataTable("dtControllers", "micro\controllers\admin\popo\ControllerAction", $controllers); |
||
| 180 | $dt->setFields(["controller","action","dValues"]); |
||
| 181 | $dt->setIdentifierFunction(function($i,$instance){return \urlencode($instance->getController());}); |
||
| 182 | $dt->setCaptions(["Controller","Action [routes]","Default values",""]); |
||
| 183 | $dt->setValueFunction("controller", function($v,$instance,$index){ |
||
| 184 | $bt=new HtmlButton("bt-".\urlencode($v),$v); |
||
| 185 | $bt->setSize("large"); |
||
| 186 | $bt->addIcon("heartbeat",true,true); |
||
| 187 | $bt->setToggle(); |
||
| 188 | $bt->onClick("$(\"tr[data-ajax='".\urlencode($instance->getController())."'] td:not([rowspan])\").toggle(!$(this).hasClass('active'));"); |
||
| 189 | return $bt; |
||
| 190 | }); |
||
| 191 | $dt->setValueFunction("action", function($v,$instance,$index){ |
||
| 192 | $params=$instance->getParameters(); |
||
| 193 | \array_walk($params, function(&$item){ $item= $item->name;}); |
||
| 194 | $params= " (".\implode(" , ", $params).")"; |
||
| 195 | $v=new HtmlSemDoubleElement("","span","","<b>".$v."</b>"); |
||
| 196 | $v->setProperty("style", "color: #3B83C0;"); |
||
| 197 | $v->addIcon("lightning"); |
||
| 198 | $v.=new HtmlSemDoubleElement("","span","",$params); |
||
| 199 | $annots=$instance->getAnnots(); |
||
| 200 | foreach ($annots as $path=>$annotDetail){ |
||
| 201 | $lbl=new HtmlLabel("",$path,"car"); |
||
| 202 | $lbl->setProperty("data-ajax", \htmlspecialchars(($path))); |
||
| 203 | $lbl->addClass("_route"); |
||
| 204 | $v.=" ".$lbl; |
||
| 205 | } |
||
| 206 | return $v; |
||
| 207 | }); |
||
| 208 | $dt->onPreCompile(function($dt){ |
||
| 209 | $dt->getHtmlComponent()->mergeIdentiqualValues(0); |
||
| 210 | }); |
||
| 211 | $dt->setEdition(true); |
||
| 212 | $dt->addClass("compact"); |
||
| 213 | $dt->addFieldButton("Get",true,function(&$bt,$instance){$bt->addClass("_get")->setProperty("data-url",$instance->getPath());}); |
||
| 214 | $dt->insertInFieldButton(3,"Post",true,function(&$bt,$instance){$bt->addClass("_post")->setProperty("data-url",$instance->getPath());}); |
||
| 215 | return $dt; |
||
| 216 | } |
||
| 217 | |||
| 218 | public function getCacheDataTable($cacheFiles){ |
||
| 219 | $dt=$this->jquery->semantic()->dataTable("dtCacheFiles", "micro\controllers\admin\popo\CacheFile", $cacheFiles); |
||
| 220 | $dt->setFields(["type","name","timestamp","size"]); |
||
| 221 | $dt->setCaptions(["Type","Name","Timestamp","Size",""]); |
||
| 222 | $dt->setValueFunction("type", function($v,$instance,$index){ |
||
| 223 | $item=$this->jquery->semantic()->htmlDropdown("dd-type-".$v,$v); |
||
| 224 | $item->addItems(["Delete all","(Re-)Init cache"]); |
||
| 225 | $item->setPropertyValues("data-ajax", $v); |
||
| 226 | $item->getItem(0)->addClass("_delete-all"); |
||
| 227 | if($instance->getFile()==="") |
||
| 228 | $item->getItem(0)->setDisabled(); |
||
| 229 | $item->getItem(1)->addClass("_init"); |
||
| 230 | if($instance->getType()!=="Models" && $instance->getType()!=="Controllers") |
||
| 231 | $item->getItem(1)->setDisabled(); |
||
| 232 | $item->asButton()->addIcon("folder",true,true); |
||
| 233 | return $item; |
||
| 234 | }); |
||
| 235 | $dt->addDeleteButton(true,[],function($o,$instance){if($instance->getFile()=="")$o->setDisabled();}); |
||
| 236 | $dt->setIdentifierFunction("getFile"); |
||
| 237 | $dt->setValueFunction("timestamp", function($v){ |
||
| 238 | if($v!=="") |
||
| 239 | return date(DATE_RFC2822,$v); |
||
| 240 | }); |
||
| 241 | $dt->setValueFunction("size", function($v){ |
||
| 242 | if($v!=="") |
||
| 243 | return self::formatBytes($v); |
||
| 244 | }); |
||
| 245 | $dt->setValueFunction("name", function($name,$instance,$i){ |
||
| 246 | $link=new HtmlLink("lnl-".$i); |
||
| 247 | $link->setContent($name); |
||
| 248 | $link->addIcon("edit"); |
||
| 249 | $link->addClass("_lnk"); |
||
| 250 | $link->setProperty("data-type", $instance->getType()); |
||
| 251 | $link->setProperty("data-ajax", $instance->getFile()); |
||
| 252 | return $link; |
||
| 253 | }); |
||
| 254 | $dt->onPreCompile(function($dt){ |
||
| 255 | $dt->getHtmlComponent()->mergeIdentiqualValues(0); |
||
| 256 | }); |
||
| 257 | $this->jquery->postOnClick("._lnk", $this->controller->_getAdminFiles()->getAdminBaseRoute()."/_showFileContent","{type:$(this).attr('data-type'),filename:$(this).attr('data-ajax')}","#modal"); |
||
| 258 | $this->jquery->postFormOnClick("._delete", $this->controller->_getAdminFiles()->getAdminBaseRoute()."/deleteCacheFile", "frmCache","#dtCacheFiles tbody",["jqueryDone"=>"replaceWith","params"=>"{toDelete:$(this).attr('data-ajax')}"]); |
||
| 259 | $this->jquery->postFormOnClick("._delete-all", $this->controller->_getAdminFiles()->getAdminBaseRoute()."/deleteAllCacheFiles", "frmCache","#dtCacheFiles tbody",["jqueryDone"=>"replaceWith","params"=>"{type:$(this).attr('data-ajax')}"]); |
||
| 260 | $this->jquery->postFormOnClick("._init", $this->controller->_getAdminFiles()->getAdminBaseRoute()."/initCacheType", "frmCache","#dtCacheFiles tbody",["jqueryDone"=>"replaceWith","params"=>"{type:$(this).attr('data-ajax')}"]); |
||
| 261 | return $dt; |
||
| 262 | } |
||
| 263 | |||
| 264 | public function getModelsStructureDataTable($datas){ |
||
| 265 | $de=$this->jquery->semantic()->dataElement("dtStructure", $datas); |
||
| 266 | $fields=\array_keys($datas); |
||
| 267 | $de->setFields($fields); |
||
| 268 | $de->setCaptions($fields); |
||
| 269 | foreach ($fields as $key){ |
||
| 270 | $de->setValueFunction($key, function($value) use ($key){ |
||
| 271 | if($value instanceof \stdClass){ |
||
| 272 | $value=(array) $value; |
||
| 273 | } |
||
| 274 | return \print_r($value,true); |
||
| 275 | }); |
||
| 276 | } |
||
| 277 | return $de; |
||
| 278 | } |
||
| 279 | |||
| 280 | public function getConfigDataElement($config){ |
||
| 281 | $de=$this->jquery->semantic()->dataElement("deConfig", $config); |
||
| 282 | $fields=\array_keys($config); |
||
| 283 | $de->setFields($fields); |
||
| 284 | $de->setCaptions($fields); |
||
| 285 | $de->setValueFunction("database", function($v,$instance,$index){ |
||
| 286 | $dbDe=new DataElement("",$v); |
||
| 287 | $dbDe->setFields(["dbName","serverName","port","user","password","cache"]); |
||
| 288 | $dbDe->setCaptions(["dbName","serverName","port","user","password","cache"]); |
||
| 289 | return $dbDe; |
||
| 290 | }); |
||
| 291 | $de->setValueFunction("templateEngineOptions", function($v,$instance,$index){ |
||
| 292 | $teoDe=new DataElement("",$v); |
||
| 293 | $teoDe->setFields(["cache"]); |
||
| 294 | $teoDe->setCaptions(["cache"]); |
||
| 295 | $teoDe->fieldAsCheckbox("cache",["class"=>"ui checkbox slider"]); |
||
| 296 | return $teoDe; |
||
| 297 | }); |
||
| 298 | $de->setValueFunction("mvcNS", function($v,$instance,$index){ |
||
| 299 | $mvcDe=new DataElement("",$v); |
||
| 300 | $mvcDe->setFields(["models","controllers"]); |
||
| 301 | $mvcDe->setCaptions(["Models","Controllers"]); |
||
| 302 | return $mvcDe; |
||
| 303 | }); |
||
| 304 | $de->setValueFunction("di", function($v,$instance,$index) use($config){ |
||
| 305 | $diDe=new DataElement("",$v); |
||
| 306 | $keys=\array_keys($config["di"]); |
||
| 307 | $diDe->setFields($keys); |
||
| 308 | foreach ($keys as $key){ |
||
| 309 | $diDe->setValueFunction($key, function($value) use ($config,$key){ |
||
| 310 | $r =$config['di'][$key]; |
||
| 311 | return \nl2br(self::closure_dump($r)); |
||
| 312 | |||
| 313 | }); |
||
| 314 | } |
||
| 315 | return $diDe; |
||
| 316 | }); |
||
| 317 | $de->fieldAsCheckbox("test",["class"=>"ui checkbox slider"]); |
||
| 318 | $de->fieldAsCheckbox("debug",["class"=>"ui checkbox slider"]); |
||
| 319 | return $de; |
||
| 320 | } |
||
| 321 | |||
| 322 | private static function closure_dump(\Closure $c) { |
||
| 323 | $str = 'function ('; |
||
| 324 | $r = new \ReflectionFunction($c); |
||
| 325 | $params = array(); |
||
| 326 | foreach($r->getParameters() as $p) { |
||
| 327 | $s = ''; |
||
| 328 | if($p->isArray()) { |
||
| 329 | $s .= 'array '; |
||
| 330 | } else if($p->getClass()) { |
||
| 331 | $s .= $p->getClass()->name . ' '; |
||
| 332 | } |
||
| 333 | if($p->isPassedByReference()){ |
||
| 334 | $s .= '&'; |
||
| 335 | } |
||
| 336 | $s .= '$' . $p->name; |
||
| 337 | if($p->isOptional()) { |
||
| 338 | $s .= ' = ' . \var_export($p->getDefaultValue(), TRUE); |
||
| 339 | } |
||
| 340 | $params []= $s; |
||
| 341 | } |
||
| 342 | $str .= \implode(', ', $params); |
||
| 343 | $str .= '){' . PHP_EOL; |
||
| 344 | $lines = file($r->getFileName()); |
||
| 345 | for($l = $r->getStartLine(); $l < $r->getEndLine(); $l++) { |
||
| 346 | $str .= $lines[$l]; |
||
| 347 | } |
||
| 348 | return $str; |
||
| 349 | } |
||
| 350 | |||
| 351 | private static function formatBytes($size, $precision = 2){ |
||
| 352 | $base = log($size, 1024); |
||
| 353 | $suffixes = array('o', 'Ko', 'Mo', 'Go', 'To'); |
||
| 354 | return round(pow(1024, $base - floor($base)), $precision) .' '. $suffixes[floor($base)]; |
||
| 355 | } |
||
| 356 | |||
| 357 | protected function relationMembersInForm($form,$instance,$className){ |
||
| 358 | $relations = OrmUtils::getFieldsInRelations($className); |
||
| 359 | foreach ($relations as $member){ |
||
| 360 | if($this->controller->_getAdminData()->getUpdateManyToOneInForm() && OrmUtils::getAnnotationInfoMember($className, "#manyToOne",$member)!==false){ |
||
| 361 | $this->manyToOneFormField($form, $member, $className, $instance); |
||
| 362 | }elseif($this->controller->_getAdminData()->getUpdateOneToManyInForm() && ($annot=OrmUtils::getAnnotationInfoMember($className, "#oneToMany",$member))!==false){ |
||
| 363 | $this->oneToManyFormField($form, $member, $instance,$annot); |
||
| 364 | }elseif($this->controller->_getAdminData()->getUpdateManyToManyInForm() && ($annot=OrmUtils::getAnnotationInfoMember($className, "#manyToMany",$member))!==false){ |
||
| 365 | $this->manyToManyFormField($form, $member, $instance,$annot); |
||
| 366 | } |
||
| 367 | } |
||
| 368 | } |
||
| 369 | |||
| 370 | protected function manyToOneFormField(DataForm $form,$member,$className,$instance){ |
||
| 371 | $joinColumn=OrmUtils::getAnnotationInfoMember($className, "#joinColumn", $member); |
||
| 372 | if($joinColumn){ |
||
| 373 | $fkObject=Reflexion::getMemberValue($instance, $member); |
||
| 374 | $fkClass=$joinColumn["className"]; |
||
| 375 | if($fkObject===null){ |
||
| 376 | $fkObject=new $fkClass(); |
||
| 377 | } |
||
| 378 | $fkId=OrmUtils::getFirstKey($fkClass); |
||
| 379 | $fkIdGetter="get".\ucfirst($fkId); |
||
| 380 | if(\method_exists($fkObject, "__toString") && \method_exists($fkObject, $fkIdGetter)){ |
||
| 381 | $fkField=$joinColumn["name"]; |
||
| 382 | $fkValue=OrmUtils::getFirstKeyValue($fkObject); |
||
| 383 | if(!Reflexion::setMemberValue($instance, $fkField, $fkValue)){ |
||
| 384 | $instance->{$fkField}=OrmUtils::getFirstKeyValue($fkObject); |
||
| 385 | $form->addField($fkField); |
||
| 386 | } |
||
| 387 | $form->fieldAsDropDown($fkField,JArray::modelArray(DAO::getAll($fkClass),$fkIdGetter,"__toString")); |
||
| 418 |