ExpressionEngine CMS
Open, Free, Amazing

Thread

This is an archived forum and the content is probably no longer relevant, but is provided here for posterity.

The active forums are here.

Don't Repeat Yourself in CI doesn't exist

February 24, 2009 3:46am

Subscribe [9]
  • #1 / Feb 24, 2009 3:46am

    gh0st

    130 posts

    CodeIgniter is advertised as one of the many frameworks out there that works best with the “so-called” DRY principle.

    I call it “so-called” because it doesn’t exist, well not in my experience of using CI.

    I’ve noticed not only am I repeating myself, but I’m repeating myself just as much as I was BEFORE I worked with MVC.

    For example, no CRUD library is installed for CI so you end up writing controllers to add/edit/delete for every table, so you are essentially repeating yourself for every table you want to append a CRUD capacity to.

    Any time you want to check whether the user is logged in, has the right priveleges, etc you enter in a if statement (probably at the constructor level at every constructor you want it in), but every time you copy/paste this code in you are repeating yourself.

    In controllers you load the libraries custom to each controller to save on load time, etc—but this again is repetition.

    In some controllers they’ll be pagination, but not likely in all controllers—but any time you put it in you’re repeating yourself.

    —-

    Another issue I’m frustrated with is this concept that because I’m using CI I no longer have to worry about changing fields everywhere to make an application work.

    But its still the same, I don’t see anything NEW or different.

    Lets say you want to list all cars, so you have a model to pull out all cars and feed this into an array, which is looped and presented in a view.

    But lets say you change, or add a field in the database—you have to add this field in every controller, every method, every view where you want/require it.

    For example;

    foreach ($query->result as $row):
    print $row->car_name;
    // any new fields have to be added to this loop, and everywhere else too where the change is require.
    endforeach;

    Where is the DRY in this?

    Where is this fancy concept of not changing fields everywhere?  It doesn’t exist!

    —-

    Finally, I always get this feeling that there is meant to be a correct way of doing MVC, DRY, etc, etc—but I never see any code or examples of how to do it, nor does CI come automatically bundled with libraries which are meant to make it easier.

    For example, if HMVC is so good, why isn’t it bundled automatically?  If ORM libraries/plugins are meant to be so good, why aren’t they bundled automatically? 

    If the concept is to make development easier—why do I have to keep going round in circles to get CI the way that its been advertised to work?

  • #2 / Feb 24, 2009 4:05am

    Crimp

    320 posts

    Good rant. A little repetitive, but still good.

  • #3 / Feb 24, 2009 4:46am

    davidbehler

    708 posts

    Ever tried to create a new controller class by extending the CI class? There you can add auth check to the constructor and don’t have to do it in every contoller.

    Auto-loading of libraries, models, configs, etc. is a pretty nice feature aswell! That saves quite some repetition.

    Imho one can reduce repetition to a mininum by outsourcing often used functions and usage of libraries, auto-loading and so on but you can’t get totally rid of it.

  • #4 / Feb 24, 2009 4:54am

    johnwbaxter

    651 posts

    @crimp - I see what you did there…

    @gh0st - Let me answer a couple of your questions. With regards to the auth check having to be in every controller, that is not quite true. You could do a couple of things here, first you could create a hook that ran before the controller runs that does your auth check - http://ellislab.com/codeigniter/user-guide/general/hooks.html

    That only needs to be written once. Secondly you could Extend the Core controller which you would put your auth code in and then all your other controllers would extend your customised parent controller - http://ellislab.com/codeigniter/user-guide/general/core_classes.html

    Your other questions are architectural really. You could write your own code and way of including libs relying on some logic or other. At the end of the day you can do what you want to do. Codeigniter does not stand in your way.

    Finally with regards to HMVC and ORM being bundled, i agree with you to a certain extent. Why can’t they be included as optional additions to CI, you only load them if you need them like other libs and stuff. I simply think that at the moment EE doesn’t use ORM so CI does not have ORM. However, there are some excellent user contributed HMVC offerings and also some great ORM offerings, i know they’re not supported by CI and that is an issue but they’re there so why not use them?!

    Top rant though.

  • #5 / Feb 24, 2009 5:11am

    Colin Williams

    2601 posts

    Everything you seem to see as hindrances, I (and others, I’m sure) see as flexibility.

    If you just don’t get MVC yet, don’t give up. It’s a pretty basic design pattern. I can’t figure out why so many people fuss over it.

    When you really understand CI from a high-level, I’m sure your frustrations will disappear, and you’ll see how to use CI in a DRY way.

    Also, have you looked at any user contributed code? Like, look at the blogging software people have released, and don’t miss Derek’s Bamboo Invoice. These give you an opportunity to pop the hood and take a look at how CI can be used effectively.

  • #6 / Feb 24, 2009 5:35am

    xwero

    4145 posts

    I just checked and CI doesn’t gets advertised as being a DRY framework on the site, simple and easy is the vibe from the frontpage i’m getting.

    DRY is something you have to do yourself. As mentioned before, the most basic example of DRY programming is the autoload config file. If you notice you use a helper,library,model,plugin in all your controllers just add it to autoload.php and it will be loaded without code in the controllers.

    DRY code is done by programming when a certain functionality needs to be available and you can do that by autoload or hooks or extending CI classes. Or by convenience, you can find and example of it here

    I think it’s a good thing most code is optional or third party. CI doesn’t have to be another framework with a massive amount of libraries, helpers and plugins. Everything is done already, it’s only the way you think it should work that makes you pick a certain piece of code or write it yourself.

    I get the feeling you blame CI for you not wanting to find the best solution for your applications.

  • #7 / Feb 24, 2009 7:29am

    Colin Williams

    2601 posts

    It’s particularly revealing that you say you had a hard time keeping DRY before you used CI. Maybe it’s just PHP’s fault!

  • #8 / Feb 24, 2009 7:45am

    I think Symfony may be more your kind of thing

  • #9 / Feb 24, 2009 8:52am

    Colin Williams

    2601 posts

    For example, no CRUD library is installed for CI so you end up writing controllers to add/edit/delete for every table, so you are essentially repeating yourself for every table you want to append a CRUD capacity to.

    Have you ever considered that each data object in your application might have unique needs with regard to CRUD-like operations? Get you some IgnitedRecord or DataMapper if you want even more abstraction.

    Lets say you want to list all cars, so you have a model to pull out all cars and feed this into an array, which is looped and presented in a view.

    But lets say you change, or add a field in the database—you have to add this field in every controller, every method, every view where you want/require it.

    Same thing. Just consider that this introduction to the automobile object model requires specific handling, by all components. The very fact that you have separated your app into MVC components makes it easy to handle this addition. If you wanted something more automated, you might have wanted to use something like Drupal, whose CCK module provides a GUI for object modeling.

    Any time you want to check whether the user is logged in, has the right priveleges, etc you enter in a if statement (probably at the constructor level at every constructor you want it in), but every time you copy/paste this code in you are repeating yourself.

    Fine-grained control is a good thing! Like, say I show an upload form for privileged users and nothing for unprivileged users. And putting access checks in a controller constructor gives you access control for the whole object. How much broader do you want it.

    Also, every time I create a new controller, I have to copy stuff like “class Example extends Controller” in every controller file and I’m repeating myself.

    I just think your concept of DRY is way off.

  • #10 / Feb 24, 2009 9:01am

    xwero

    4145 posts

    Colin i think you made your point 😊

  • #11 / Feb 24, 2009 9:30am

    Colin Williams

    2601 posts

    Just wanted to point a few specific things out.

  • #12 / Feb 24, 2009 9:41am

    obiron2

    199 posts

    @gh0st

    In your example of the car,  to produce this as DRY you would store a ‘model’ of the car properties and thier database locations in a model file.

    Pull the car ‘model’ and use the fields to carry out the select and data return in your DB query and again to populate the fields in the view.

    This way, when you change the properties of the car, you only have to change the ‘model’ and the DB query and view will change automatically.

    Of course, you would still need to add the relevant fields to the relevant tables.

    This technique is handy for building generic forms - I am working on one at the moment, if I ever get a chance to finish it, I will publlish it in the wiki

    DRY is possible with CI, but somtimes you need to make one more lateral thought step.


    obiron

  • #13 / Feb 24, 2009 11:29am

    got 2 doodle

    171 posts

    There has to be limits on DRY but you can save a lot of code by capturing those things that you always seem to be doing in each controller by creating an extension of the controller class and put stuff in there that is common.

    Here’s a snippet from one of my controllers.

    /*******************************************************************************/    
        function welcome()
    /*******************************************************************************/    
        
        {
    $this->load_views($this->get_pagedata('lmf_home'));
    
        }
    /*******************************************************************************/    
        function explain()
    /*******************************************************************************/    
        
        {
    $this->load_views($this->get_pagedata('lmf_explain'));
    
        }
    /*******************************************************************************/    
        function products()
    /*******************************************************************************/    
        
        {
    $this->load_views($this->get_pagedata('lmf_products'),'products');
    
        }

    Each webpage has a unique name which is passed to the controller, this works because the pages are all very similar.  The last example ‘lmf_products’ doesn’t use the default view so it passes ‘products’ to specify a view format.  So not wanting to bore you, I think I have definitely cut down on the repetion.

    Just as a matter of coherency here is the code in the parent controller.

    /*-----------------------------------------------------------------------*/ 
    /* get data  */      
    function get_pagedata($page_name,$menu = false) {
    /*-----------------------------------------------------------------------*/ 
    
    if($menu) {    $active = $menu;
                    } else {
                $active = $page_name;
                }
    /* default location is Limstone Mtn Farm */        
    $business='lmf';    
    $business=$this->uri->segment(1);    
        if($business == 'shf') { 
                $mode='right';
                    } else {
                $mode='left';
                }
            $data['pagemode']=$mode;                        
            $data['icon_pic']="icon_$mode.jpg";                            
            $this->load->model('images');
            $this->load->model('pagedata');
            $this->load->model('sitedata');
            $this->load->model('product_data');
            $data['page_name']= $page_name;
            $data['products']=$this->product_data->get_products_by_business($business);
            $data['page_data'] = $this->pagedata->get_pagedata_by_page_name($page_name);
            $data['keyphrase'] = $this->pagedata->get_keyphrase_by_page_name($page_name);
            $data['description'] = $this->pagedata->get_description_by_page_name($page_name);
            $data['business'] = $this->pagedata->get_business_by_page_name($active);
            $data['menu'] = $this->pagedata->get_menu_by_page_name($active);
            $data['image_data'] = $this->pagedata->get_images_by_page_name($page_name);
            $data['site_url'] = $this->config->item('site_url');
            $data['base_url'] = $this->config->item('base_url');
            $data['css'] = "site";
            
            // initialize some containers
        $cc_main = "";
        $cc_news = "";
        $cc_tag = "";
     if(isset($data['page_data'])) {   
            foreach ($data['page_data'] as $segment) {
                
                if ($segment ['location'] == 'cc_main'){
                        $cc_main .= $segment ['data'];
                    }
                if ($segment ['location'] == 'cc_news'){
                        $cc_news .= $segment ['data'];
                    }
                if ($segment ['location'] == 'cc_tag'){
                        $cc_tag .= $segment ['data'];
                    }    
            }
            }
            $data['cc_main']=$cc_main;
            $data['cc_news']=$cc_news;
            $data['cc_tag']=$cc_tag;
    $data = array_merge($data,$this->sitedata->get_all_site_keys());
    return $data;
      } 
    /*-----------------------------------------------------------------------*/ 
    /* get views  */      
    function load_views($data,$type = 'standard') {
    /*-----------------------------------------------------------------------*/
    
    switch ($type) {
    default:
    case 'standard':
    $output = $this->load->view('header',$data,true);
    $output .= $this->load->view('l_column',"",true); 
    $output .= $this->load->view('content',"",true); 
        if($this->test) $output .= $this->load->view('helper',"",true); 
    $output .= $this->load->view('footer',"",true);
    break;
    case 'products':
    $output = $this->load->view('header',$data,true);
    $output .= $this->load->view('l_column',"",true); 
    $output .= $this->load->view('content_products',"",true); 
        if($this->test) $output .= $this->load->view('helper',"",true); 
    $output .= $this->load->view('footer',"",true);
    break;
    case 'product':
    $output = $this->load->view('header',$data,true);
    $output .= $this->load->view('l_column',"",true); 
    $output .= $this->load->view('content_product',"",true); 
        if($this->test) $output .= $this->load->view('helper',"",true); 
    $output .= $this->load->view('footer',"",true);
    break;
    } // end switch
    /* Done loading views */        
            $this->output->set_output($output); 
      }   
    /*-----------------------------------------------------------------------*/
  • #14 / Feb 25, 2009 9:16am

    gh0st

    130 posts

    I think I learnt a lot with this thread.  Perhaps my thoughts of what DRY was/is, is probably off-base.

    Thanks

  • #15 / Feb 25, 2009 9:50am

    got 2 doodle

    171 posts

    Maybe you had it mixed up with DRYE

    (Dont Repeat Yourself Ever)

    :coolsmile:

    I have learned more than I could tell from this forum, play nice and these guys are great and very generous with their knowledge.

    doodle

.(JavaScript must be enabled to view this email address)

ExpressionEngine News!

#eecms, #events, #releases