2009-02-10 14 views
2

J'ai une fonction dans mon contrôleur qui a pris plus de temps que je ne le souhaiterais et que je voudrais refactoriser pour appeler quelques fonctions discrètes pour le rendre plus facile à gérer. Comment puis-je mieux organiser une longue fonction dans un contrôleur Codeigniter?Comment refactoriser une fonction de contrôleur Codeigniter trop longue?

Ce que j'ai essayé:

Je sais que vous pouvez créer des fonctions privées dans un contrôleur en les nommant avec un underscore (_myfunc), mais les variables de la fonction sont hors de portée pour la appeler la fonction du contrôleur. Donc, vous devez retourner toutes les données nécessaires à partir de la fonction qui est un problème.

Est-ce la meilleure option pour gérer une fonction de contrôleur complexe? Existe-t-il un moyen plus simple où les variables peuvent toutes être globales à la classe de contrôleur comme une variable de membre de classe standard?

Suggestions? Merci d'avance!

EDIT: Quelqu'un a demandé le code alors j'ai ajouté le code pour le contrôleur géant ci-dessous. Une possibilité d'amélioration consiste à déplacer la logique dans les instructions de commutation vers des fonctions distinctes (suppression, prévisualisation, ordre, etc.). Mais j'essaie de décider de la prochaine étape après cela. Déplacer le gros code de configuration de validation dans sa propre fonction prendrait vraiment un peu de poids, mais où devrais-je le déplacer?

function categories() { 
    $this->load->library('upload'); 
    $this->load->model('categories_m'); 
    $this->load->model('products_m'); 
    $this->load->model('pages_m'); 
    $this->load->model('backoffice/backofficecategories_m'); 
    $data['body'] = $this->load->view('backoffice/categories/navigation_v', '', TRUE); 
    $data['cat_tree'] = $this->categories_m->getCategoryTree(); 
    $data['page_list'] = $this->pages_m->getPageList(); 
    $data['category_dropdown'] = $this->load->view('backoffice/categories/category_dropdown_v',$data,TRUE); 

    switch ($this->uri->segment(3)) { //display views based on parameter in URL. 
    case 'delete':   
     $categoryTreeID = $this->sitewide_m->checkURLParam($this->uri->segment(4),'CategoryTree'); //if parameter is in URL, show 404 if invalid parameter is passed. Otherwise, set variable known to be safe. 
     if (isset($_POST['delete'])) { 
      $this->backofficecategories_m->deleteCategory($categoryTreeID); 
      $data['body'] .= '<span class="error">Category Deleted.</span>'; 
     } else { 
      $data['cat_details'] = $this->categories_m->getCategoryDetails('',$categoryTreeID); 
      $data['parent_category'] = $this->categories_m->getParentCategory($categoryTreeID); 
      $data['products_to_reassign'] = $this->products_m->getProductsInCategory('',$categoryTreeID); 
      $data['body'] .= $this->load->view('backoffice/categories/delete_v',$data,TRUE); //pull fresh category tree data since tree was just updated. 
     } 
     break; 
    case 'preview': 
     if ($this->uri->segment(4)) $data['categoryTreeID'] = $this->sitewide_m->checkURLParam($this->uri->segment(4),'CategoryTree'); //if parameter is in URL, show 404 if invalid parameter is passed. Otherwise, set variable known to be safe. 
     $data['cat_details'] = $this->categories_m->getCategoryDetails(NULL,$data['categoryTreeID']); //get category ID being edited from the URL and store it. Returns false if category ID isn't found. 
     foreach ($data['cat_details']->result() as $detail) { 
      $data['categoryName'] = $detail->Name; 
      $data['categoryID'] = $detail->ID; 
     } 
     $data['body'] .= $this->load->view('backoffice/categories/preview_v', $data, TRUE); 
     break; 

    ...cases continue... 
    default: 
     $this->load->library('table'); 
     $data['body'] .= $this->load->view('backoffice/categories/categories_v', $data, TRUE); 
     break; 
    } 
    $this->load->view('backoffice/template_v',$data);  
} 
+0

Pourriez-vous poster la fonction du contrôleur pour nous de regarder? – robsymonds

+1

Pourquoi ne pas laisser CI faire le routage du troisième segment au lieu d'utiliser une instruction switch? –

+0

C'est un bon point - la raison pour laquelle je n'ai pas été le code avant que l'instruction switch ne s'applique à toutes les actions que l'instruction switch vérifie. Comment puis-je rendre la section avant le commutateur réutilisable si je divise l'instruction switch en fonctions séparées? Une fonction séparée serait hors de portée, n'est-ce pas? –

Répondre

4

En regardant votre code, vous utilisez une méthode pour plusieurs actions. Je ferais de chaque action sa propre méthode. Les ressources communes peuvent être des membres de classe et chargées dans le constructeur. Donc au lieu d'une URL comme "your_controller/categories/add" vous pouvez changer votre URL en "category_controller/add" et avoir une méthode pour chaque action. Si vous ne voulez pas changer vos urls, utilisez un itinéraire:

$route['your_controller/categories/(.*)'] = 'your_controller/$1'; 
0

Quelle version de PHP utilisez-vous? PHP 5 a le soutien pour de vrai OO, afin que vous puissiez déclarer la fonction privée qui sera traitée comme telle par l'interprète:

private function foo(){ 
... 
} 

Si vous voulez des classes qui étendent votre classe (classes d'enfants) pour avoir accès à la fonction, remplacer private avec protected.

Je n'ai jamais utilisé CodeIgnniter alors j'ai peur de ne pas pouvoir vous aider avec votre domaine de problème spécifique. Cependant, la refactorisation d'une fonction qui est devenue trop longue est un problème très commun, avec des solutions communes. Martin Fowler est un gars intelligent qui a écrit quelques livres sur le sujet qui sont bien considérés, donc vous pourriez voir si vous pouvez trouver l'un des his books. Il existe également des tutoriels en ligne pour vous aider à commencer le refactoring.

+0

Merci pour la réponse Jeremy. Je suis familier avec les principes généraux de refactoring, essayant juste de comprendre la meilleure approche en travaillant dans les contraintes de Codeigniter. –

5

Utilisez-vous des modèles? L'allumeur de code n'applique pas cela, mais l'utilisation de modèles en plus des contrôleurs et des vues est un bon moyen d'avoir une fonction de contrôleur plus courte. Vous pouvez également placer certaines fonctions dans votre propre assistant, puis l'importer.

Et si vous souhaitez définir des valeurs par défaut pour l'ensemble du constructeur, vous pouvez utiliser le constructeur de classe. Ceci est décrit ici:

http://codeigniter.com/user_guide/general/controllers.html#constructors

+0

Merci Stuart. Ouais, en utilisant des modèles, bien que je n'aime pas la vue retstrictive de Codeigniter sur les modèles. La documentation de Codeigniter indique essentiellement que vous ne mettez que des requêtes DB dans les modèles. J'aimerais déplacer beaucoup de ma logique vers mon modèle mais cela semble aller à l'encontre de l'idée de codeigniter que model = db. D'accord? –

+0

Oui, les modèles ne sont qu'une couche supplémentaire pour traiter les données, comme d'habitude dans l'approche MVC. Si vous voulez vous en tenir aux "règles" de l'allumeur de code, le mieux est d'utiliser les fonctions privées. Ou si elles vont être des fonctions réutilisables, écrivez-les dans une aide. –

2

une couche de service aiderait.

+0

Je crois que je comprends l'essentiel d'une couche de service, mais pourriez-vous clarifier un peu avec un exemple? –

+0

Si je me souviens bien, http://tudu.sourceforge.net/ avait une implémentation décente de la couche Service. Jetez un coup d'oeil à l'intérieur des sources. Voici une autre explication assez simple: http://is.gd/jgl6. Aussi, consultez ce morceau de code http://is.gd/jgnQ. Bonne chance! –

2

Si vous souhaitez conserver votre logique dans le même contrôleur, vous pouvez simuler une méthode privée en plaçant un trait de soulignement avant le nom de la fonction, par exemple: _myMethod(). Comme l'indique le link, un trait de soulignement avant le nom de la fonction empêche CI de l'appeler depuis l'URL. Vous pouvez, par exemple, créer des méthodes _delete(), _preview(), _order() etc. dans le contrôleur Categories. Si, toutefois, vous utilisez la même logique pour supprimer, prévisualiser, commander etc. d'autres choses, vous devriez peut-être déplacer ces méthodes dans un modèle ou un assistant.

1

Personnellement, je pense que vous faites trop avec une seule méthode de commande. Une des premières choses que je ferais est de séparer vos fonctions CRUD (Créer une mise à jour de suppression) en tant que méthodes séparées. Par exemple, votre exemple est pour travailler avec des "catégories", pourquoi ne pas avoir un contrôleur "catégories" séparé?

class Categories extends Controller 
{ 
    function __construct() 
    { 
    parent::Controller(); 
    } 

    function index() 
    { 
    //display logic/code here 
    } 

    function edit() 
    { 
    //get the category to update from the post or url for editing 
    //do the editing, etc 
    } 

    function delete() 
    { 
    //delete the category 
    } 

    function add() 
    { 
    //create the new category 
    } 
} 

Vos URL renverrait le contrôleur catégories:

http://www.example.com/categories/edit http://www.example.com/categories/delete etc.

La deuxième chose que je suggère est la mise à niveau CodeIgniter 1.7.1 - la bibliothèque form_validation mise à jour rend facile de déplacer toutes vos règles de validation dans un fichier de configuration séparé.

+0

Merci pour l'article JayTee. Je suis d'accord que les deux idées sont un pas dans la bonne direction. Ce fut le premier contrôleur que j'ai construit en codeigniter et j'apprenais toujours les meilleures pratiques dans ses contraintes. Il est lentement devenu trop grand. –

1

Essayez d'examiner la fonction _remap() du contrôleur dans codeigniter. En l'utilisant, vous pouvez conserver le code commun dans la fonction _remap, puis appeler n'importe quelle autre fonction depuis _remap pour supprimer, mettre à jour etc (basé sur le uri_segment (3)).

0

Vous pouvez mettre des fonctions communes dans une bibliothèque et l'appeler.

Questions connexes