Welcome Guest, Not a member yet? Register   Sign In
CI, MVC and m:n table-relationships
#1

[eluser]eff_kay[/eluser]
Hi guys!

I'm completely new to the MVC-pattern and frameworks in general. I'm working on a project and I figured I'd give it a go with Codeigniter. So far I've only done procedural programming, so the MVC pattern is a bit alien to me. Bear with me, please.

Okay, I'm going to use an example to try and explain:

I've got three tables:
tableA (id, name, description)
tableB (id, difficulty, situation)
tableAB (tableA_id, tableB_id)

.. and I've got a model which interacts with the tables. The model has a function for each table:
function a() gets everything from tableA
function b($id) uses $id to select everything from tableB where $id = tableAB.tableA_id and tableB_id = tableAB.tableB_id

Okay, so far everything is Hunky Dory. I've set up my controller with functions to interact with the model. One of these uses "function a" from the model to produce an array containing tableA's content. I then send this to a view and echo it as a list of links. These links point to another function in the controller like this:

Code:
anchor('controller/functionForTableB/' . $tableA_content->id, $tableA_name)

Here's how the code for functionForTableB would look like:
Code:
function functionForTableB()
{
  $id = $this->uri->segment(3);
  $data['tableB'] = $this->model->b($id);

  $this->load->view('viewname', $data);
}

Here's where I get stuck. I've got two more tables:
tableC (id, type)
tableBC (tableB_id, tableA_id)

So this connects tableB and tableC in the same way that tableA and tableB is connected. I want functionForTableB to be able to use data from tableC as well. I created a new function in the model ("function c"). This function does the same as "function b" does (only for tableB and tableC of course). I need to provide the correct id from tableB and send it to the SAME view.

Code:
function functionForTableB()
{
  $id = $this->uri->segment(3);
  $data['tableB'] = $this->model->b($id);
  $data['tableC'] = $this->model->c($howDoIGetThisIdFromTableB);

  $this->load->view('viewname', $data);
}

Hope this makes sense. Kinda difficult to explain exacly what I need to do.
-FK-
#2

[eluser]TheFuzzy0ne[/eluser]
Welcome to the CodeIgniter forums.

Unfortunately, it's too late at night for me to fire up my brain without waking the neighbours, so for the mean time, I was wondering if you've considered using [url="http://www.assembla.com/wiki/show/IgnitedRecord"]ORM[/url] at all. I'm not sure if it will make it any easier for you, but it sounds ideal.
#3

[eluser]slowgary[/eluser]
I read your post but found it very hard to follow and relate to the flow because of your "codenames". If you could provide the actual names it might be easier to relate the tables to eachother and figure out a valid solution.

I'll admit I'm tired right now as well, but all I can read is A = B & BC is B + B, CB talks to A who thinks C is a B. I really wanted to help, too.
#4

[eluser]eff_kay[/eluser]
Hi again.

@fuzzyone
I read about ORM in another post. I think I got it wrong, tho. I thought ORM was another programming pattern (like MVC), and I dismissed it out of hand. My mind is clay right now, but I'll take a look at it later. Thanks!

@slowgary
Thanks for trying! I realize that the post is hard to follow and that my sincere efforts NOT to obfuscate everything were futile in the end. Im afraid that the actual table names for this project arent much better than in the post above. Lots of acronyms. Ill get back to you tomorrow with the code and a diagram outlining the db (Im on my iPhone right now).
#5

[eluser]Thorpe Obazee[/eluser]
[quote author="eff_kay" date="1244533848"]
@slowgary
Thanks for trying! I realize that the post is hard to follow and that my sincere efforts NOT to obfuscate everything were futile in the end. Im afraid that the actual table names for this project arent much better than in the post above. Lots of acronyms. Ill get back to you tomorrow with the code and a diagram outlining the db (Im on my iPhone right now).[/quote]

Hehe. The same reason I didn't respond Tongue

You should probably show the actual code so that we see everything.
#6

[eluser]jedd[/eluser]
Btw, don't know if you're doing this for a particular reason, but your code:

Code:
function functionForTableB()
{
  $id = $this->uri->segment(3);
  $data['tableB'] = $this->model->b($id);
...

... can be written more conveniently as:
Code:
function functionForTableB ( $id = NULL)
{
   ...

Obviously you wrap some checks around the $id before you proceed (but I see you're not showing them on your uri-segment call anyway).

Oh, and use an id column in your third table.
#7

[eluser]eff_kay[/eluser]
Hi again!

The image attached to this post shows my tables and how they're connected. As you can see, there are three many-to-many relationships. Basically scenario has many ob -> ob has many oe -> oe has many tc.

As for the UI, the user will be presented with a list of available scenarios. When the user selects a scenario, I want to display everything belonging to this scenario.

Using CodeIgniter, I figured I could make either a model, or a function within a model, for each of the tables. This is the model I have thus far:
Code:
//scenario_model.php
class Scenario_model extends Model{

    function Scenario_model()
    {
        parent::Model();
    }
    
    function getScenarios()
    {
        $query = $this->db->get('scenario');

        return $query;
    }
    
    function getObs($scenarioId)
    {    
        $this->db->select('scenario.name, ob.id, ob.type, ob.description');
        $this->db->from('ob');
        $this->db->join('scenario_has_ob', 'scenario_has_ob.ob_id = ob.id');
        $this->db->join('scenario', 'scenario_has_ob.scenario_id = scenario.id');
        $this->db->where('scenario.id = ' . $scenarioId);
        
        $query = $this->db->get();

        return $query;
    }
    
    function getOes($obId)
    {
        $this->db->select('oe.id, oe.description, oe.situation, oe.difficulty');
        $this->db->from('oe');
        $this->db->join('ob_has_oe', 'ob_has_oe.oe_id = oe.id');
        $this->db->join('ob', 'ob_has_oe.ob_id = ob.id');
        $this->db->where('ob.id = ' . $obId);
        
        $query = $this->db->get();
        
        return $query;
    }

}

And my controller:
Code:
//scenario.php
class Scenario extends Controller {

    function Scenario()
    {
        parent::Controller();
        
        $this->load->helper('url');        
        $this->load->model('Scenario_model');
    }
    
    function index()
    {    
        $data['scenario_data'] = $this->Scenario_model->getScenarios();
    
        $this->load->view('scenario_view', $data);
    }
    
    function ob($scenarioId = null)
    {
        $data['ob_data'] = $this->Scenario_model->getObs($scenarioId);
        
        $this->load->view('ob_view', $data);
    }

}

And the view for scenarios:
Code:
//scenario_view.php
...
<h1>Please select a scenario from the list:</h1>
&lt;?php
echo "<ul>";

foreach ($scenario_data->result() as $row):
    
    echo "<li>" . anchor('scenario/ob/'.$row->id, $row->name) . "</li>";
    
endforeach;

echo "</ul>";
?&gt;
...

This gives me a link-list of the scenarios existing in table "scenario". So when a user clicks one of the links, the function "ob" is called and the scenarioid is passed to it in the link. It then uses this to look up the ob's belonging to the scenario. The functions in the model takes a parameter $id. This id is used to fetch the elements belonging to the different tables.

Thats fine and it works. But I want to use information from the other tables as well (oe, and tc). So I'm stuck in ob_view.php:
Code:
//ob_view.php
...
&lt;?php
foreach ($ob_data->result() as $ob):

    echo "<h2>" . $ob->type . " - " . $ob->description . "</h2>";
        
        // THIS is where i want data from the other tables (oe and tc)
    
    echo "<hr />";

endforeach;
?&gt;
...

Basically what I want function ob to do, is get ALL the data belonging to a scenario given only the scenario id using the models and send this to ob_view.

If it's still unclear, I could provide you guys with the procedural equivalent just for clarity. I'm going to leave it out for the time being, tho.

Thank you in advance! :c)

Edit:
Oh yeah, btw. Thanks for the tip, jedd. I modified the code now :c)
#8

[eluser]jedd[/eluser]
I might be being really obtuse here, but can you just create the SQL call you need in your model, as a new method, and call it from your controller's ob() method, and then send that on? You can start with ->query calls rather than the CI 'AR' calls - I find the native calls easier to deal with, but I'm old school.

Anyhoo, I'm not sure if you're having an architecture / design (CI) problem, or just an old-fashioned SQL one. Feel free to re-phrase the previous sentence to form a question Smile

I'd suggest you avoid composite keys, while on the subject of SQL, and stick with dedicated id's as the PK's in your three _has_ tables.
#9

[eluser]eff_kay[/eluser]
Aha, so you suggest I form a (monster)SQL-statement to get all I need instead of using methods for each of the tables? Ill try that, thanks.

Regarding architecture. As I've said earlier, I'm new to everything MVC, so I dont really know what to put where. For instance, should I create a dedicated model for each of the tables, or should I just have one model with several methods for the tables? Should I have one controller for "scenario", another for "ob", etc, or should I just have one? These are questions I have been asking myself since I got to know CI.

I guess my question is this: "Is it a matter of preference what I put where, or are there strict conventions I should adhere to?"

I've always used composite PK's for "x_has_y" tables, so I didn't think it would be a problem. Anyways, I'll change it.

I'll get my SQL-statements up and running (with query->), then. Thanks, Jedd :c)
#10

[eluser]jedd[/eluser]
[quote author="eff_kay" date="1244583407"]Aha, so you suggest I form a (monster)SQL-statement to get all I need instead of using methods for each of the tables? Ill try that, thanks. [/quote]

Well, in very general terms, a large single SQL query will out-perform several separate SQL queries.

I'd suggest in most cases, too (including this one) it'll be easier to follow the application's logic.

Quote:Regarding architecture. As I've said earlier, I'm new to everything MVC, so I dont really know what to put where. For instance, should I create a dedicated model for each of the tables, or should I just have one model with several methods for the tables? Should I have one controller for "scenario", another for "ob", etc, or should I just have one? These are questions I have been asking myself since I got to know CI.

Big questions, indeed.

Models are the interface to your data, so you're not limited to having one model per table. I have a model that attends to about 25 tables, for example, and this works just fine (for me).

A stricter AR approach (the [url="http://codeigniter.com/wiki/ActiveRecord_Class/"]Active Record Class[/url] for instance) would lead you to a 1:1 ratio of models to tables.

If you have a smaller number of disparate types of tables, this may work best for you. In my case (above) I have about 20 tables that are very similar - so it does not.

A thread a while back talked about the average (hoping to lead to a heuristic for determining the best) size for a model. It doesn't really make much sense, though, given that even a 2,000 line model is calling, and parsing, a trivial amount of PHP code compared to everything else that's going on.

Controllers - well, there are comparable considerations here. One controller per resource - is one mantra. If you have an admin component, the question there often becomes whether you have a controller/admin_function or an admin_controller/function design. Much to consider, to be sure.


Quote:I guess my question is this: "Is it a matter of preference what I put where, or are there strict conventions I should adhere to?"

Some things are clear. Database calls only go into models. HTML should only be generated in your view. Business logic should reside in your controller. Display logic should occur in your view. But there are some vague exceptions to even these - the CI table() class, for example, generates HTML and is usually found in the controller.

Quote:I've always used composite PK's for "x_has_y" tables, so I didn't think it would be a problem.

Why do you use composite keys for your joining tables normally? I mean, you aren't using natural or composite keys for your other tables here - just curious on the rationale.




Theme © iAndrew 2016 - Forum software by © MyBB