• 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Models, where, when and why?

#11
(10-29-2014, 08:50 PM)jlp Wrote: I tend to agree with Hobbes. Part of what I try to get through to my students is proper separation of concerns. Controllers are for handling usecases, models encapsulate data sources, and views present stuff for human consumption. Taken to an extreme, we end up with a Model-View-Adapter pattern, where the view has no idea where data comes from - that is up to the controller.

I give my students a small set of "golden rules" at the beginning of my course...

If a model generates HTML, you are "fired".
If a view is aware of the origin of data (eg RDB), you are "fired".

In this context, "fired" means 20% off your grade for a lab or assignment, no questions and no mercy. You also get "fired" if your webapp somehow triggers a PHP error visible to the user.

This is a simplisitic view, and doesn't work or apply in absolutely every case, granted, but it sure drives home the point of MVC Big Grin

Man, there are a lot of 'programmers' out there that would be fired. Good thing for enforcing the rules. You should make sure they get fired if they don't escape SQL queries too ^^
Reply

#12
Controller > Input
Model > Processing
View > Output

Black Box
Input > Black Box > Output

Once the Processing is debugged you never need to know whats going on in there.
What did you Try? What did you Get? What did you Expect?

Joined CodeIgniter Community 2009.  ( Skype: insitfx )
Reply

#13
(10-30-2014, 04:28 PM)InsiteFX Wrote: Controller > Input
Model > Processing
View > Output

Black Box
Input > Black Box > Output

Once the Processing is debugged you never need to know whats going on in there.

That's exactly right!
Reply

#14
Okay, that goes back to complicating things.

Libraries should not have access to database. Fair enough, that means what I use as library should be a model. Then, back to the situation with the blog post. I have a post with various details. One of them being a category, the other an author. But, as these categories and authors have various details too, they're their own objects.

So when I want to display the post-content, I just do echo $post->content; and be done with it. When I want to display the posts authors name, this would be echo $post->author->name;.

Now I do this by using libraries. But, as said, I'm probably using libraries where I should be using models. My setup would then be something like this?


Code:
class Post_model extends CI_Model {
    public $id;
    public $title;
    public $content;
    public $category;
    public $author;

    public function get_data($postID) {

        // Get the required Post
        $result = $this->db->where('id', $postID)->get('posts');

        // If result of query is 1 (should be only one post with that ID)
        if($result->num_rows == 1) {

            // Store the resulting row as... Object?
            $post = $result->row(0, 'Post_model');

            // And then comes the fun part. Create an empty Author,
            // Call its get_data function and store the resulting object as
            // this posts author
            $author = new Author;
            $post->author = $author->get_data($post->author);

            // And the same goes for the category
            $category = new Category;
            $post->category=  $category->get_data($post->category);

            // All done? Then return this model
            return $post;

        }
    }
}
Code:
class Category_model extends CI_Mode {
    public $id;
    public $name;
    public $parent;

    public function get_data($categoryID) {

        // Get the required Category
        $result = $this->db->where('id', $categoryID)->get('categories');

        // If result of query is 1 (should be only one category with that ID)
        if($result->num_rows == 1) {    

            // return the category as an object
            return $result->row(0, 'Category_model');
        }    
    }
}

// and the same for the user


So once I've done that, I can call my controller like http://domain.com/blog/post/15, so my controller has the following method::

Code:
public function post($postID) {

    // Create a new Post object
    $post = new Post;

    // Fill the object with data from id $postID (method-call)
    $data['post'] = $post->get_data($postID);

    // And send it to the view
    $this->load->view('blogpost', $data);
}


Lean controller, fat model. Right?

I'm thinking I've been doing something terribly wrong all along...
Reply

#15
Thyrosis, think about it this way. We'll start off with the router. So someone arrives at your URL looking for a blog post. The router nudges the blog controller and says, "Oi someone want a blog post. You can find the post ID here." The controller grabs the ID then goes to the blog model, and says, "Hello blog model, can you see if you can find this blog post for me please?". The blog model takes the post ID from the controller, runs it under the tap to clean it off (just in case someone stuck a bogie on it), then it looks for the post in the database. The blog model finds the post data, grabs it, grabs all the info associated with it, then asks the user model to hand over the info for the author of the blog post. Then it sees it needs to get the user's avatar image too, so then the blog model goes to the image library and asks it to check if the user's avatar file exists, and to create a URL to it. The image library is only too happy to oblige. The blog model now has everything the controller expects, and so it packages it up into an array, and says, "Here you go blog controller. This is all the blog info." The blog controller grabs it, says "Thanks for that mate!" and shakes the blog post view and says, "Oi view wake up! You need to show this to a visitor. Take this array and render your stuff!". The view takes the array and renders it's stuff, and they all live happily ever after.

So you can see the proper relational use between MVC here and also the involvement of other libraries you may have created to do specific tasks that could be re-used in other applications.

While that is the intended way of doing things, CI is very flexible, and doesn't force you to do anything. If you wanted to, you could have done everything, including displaying the HTML output with just the controller, but there wouldn't be any point in using CI at all if you were to do that.
Reply

#16
IMHO, if we don't follow the basic doctrine of MVC design pattern which is the "separation of concern", then I don't see any reason for using an MVC framework like CI. There are another type of design pattern called Front Controller pattern + Dispatcher + router which can be use for an application too small to be built on a full blown MVC Framework.

It is always safe to have model for every controller, unless the controller have a reasonable number of methods. Otherwise, several models can serve to a single controller. A good example of this is a controller responsible for a user control panel. Since this type of controller is responsible for handling the user's account settings, message inbox, profile, file uploads, article (creation, edit and deletion), etc.
Reply

#17
I think what most people would do is use SQL joins to do what you are doing and select that information in one query.

Your post has an author and a category, but there's no reason the author and category have to come from their own models.  Sure, you may have author and category models in place for other parts of your application, for when you read, create and update those entities.  But you don't have to necessarily use them here.

I would think your post model could look something like this, depending on your table structure.


PHP Code:
public function get_data($postID
{
    $this->db->select(post.id as post_idpost.title as titlepost.content as content);
    $this->db->select(users.id as user_idusers.username as author);
    $this->db->select(category.id as category_idcategory.name as category);

    $this->db->join('users','users.id = post.author');
    $this->db->join('category','category.id = post.category');

    $this->db->where('id'$postID);

    $query $this->db->get('post');

    if ($query->num_rows() > 0)
    {
        return 
$query->result_array()[0]
    


Just doing that off the top of my head, so I'm sure it's missing a semicolon and/or a quote here and there, but you probably get the idea.

The way I look at it is that "post" is a view of an entity with attributes.  Some of those attributes may come from other tables, but that's ok.  Your models don't have to be exact representations of database tables.  You can write a function that returns the view of a post that you need without overthinking it and embedding models within models.
Reply

#18
(11-16-2014, 11:55 AM)alroker Wrote: Thyrosis, think about it this way. We'll start off with the router. So someone arrives at your URL looking for a blog post. The router nudges the blog controller and says, "Oi someone want a blog post. You can find the post ID here." The controller grabs the ID then goes to the blog model, and says, "Hello blog model, can you see if you can find this blog post for me please?". The blog model takes the post ID from the controller, runs it under the tap to clean it off (just in case someone stuck a bogie on it), then it looks for the post in the database. The blog model finds the post data, grabs it, grabs all the info associated with it, then asks the user model to hand over the info for the author of the blog post. Then it sees it needs to get the user's avatar image too, so then the blog model goes to the image library and asks it to check if the user's avatar file exists, and to create a URL to it. The image library is only too happy to oblige. The blog model now has everything the controller expects, and so it packages it up into an array, and says, "Here you go blog controller. This is all the blog info." The blog controller grabs it, says "Thanks for that mate!" and shakes the blog post view and says, "Oi view wake up! You need to show this to a visitor. Take this array and render your stuff!". The view takes the array and renders it's stuff, and they all live happily ever after.

Brilliant explanation there! That's exactly the way I'm doing things now, but then with libraries. It also answered my question how different models interact, so thank you for that Smile

bclinton Wrote:I think what most people would do is use SQL joins to do what you are doing and select that information in one query.

That is one way of doing it too. Joins could get complicated pretty quickly in my experience, so in my personal opinion I find it a lot easier to write simple queries in the form of
PHP Code:
$page $this->db->where('page_id'1)->get('pages')->row();
$page->author $this->db->where('user_id'$page->author)->get('users')->row(); 
And achieve thesame result.
Reply

#19
(11-18-2014, 05:51 AM)Thyrosis Wrote: Joins could get complicated pretty quickly in my experience, so in my personal opinion I find it a lot easier to write simple queries

Yes, I used to do that too, but I very cordially advise you to take the plunge and learn proper SQL. At some point, you're bound to run into performance problems, plus it puts you in a poor light professionally.

I have a book (well, in PDF format) called "Simply SQL" by Rudy Limeback. I find it very useful. Rudy has more than 20 years experience working with SQL and he actually hangs out at the Sitepoint Forums, where he is very helpful.
Reply


Digg   Delicious   Reddit   Facebook   Twitter   StumbleUpon  


  Theme © 2014 iAndrew  
Powered By MyBB, © 2002-2020 MyBB Group.