I was woundering what is the best coding practice when dealing with the Model-View-Controller setup when using database queries. Should I pass the returned query to the controller, like so ...
Controller
$query = $this->db->get();
$this->template->write_view('content', 'work/index', array('work_query' => $query));
$this->template->render();
View
<?php if ($work_query->num_rows() == 0): ?>
<p>There are no works</p>
<?php else: ?>
<?php foreach($work_query->result() as $work): ?>
//Do something
<?php endforeach; ?>
<?php endif; ?>
Or should I call some function from the query class (for example result_array()) and pass that to the view.
Controller
$query = $this->db->get()->result_array();
$this->template->write_view('content', 'work/index', array('works' => $query));
$this->template->render();
View
<?php if (empty($works)): ?>
<p>There are no works</p>
<?php else: ?>
<?php foreach($works as $work): ?>
//Do something
<?php endforeach; ?>
<?php endif; ?>
Is one considered a better coding standard? Is one more efficient than the other?
As pinadelsai already said, logic tells to put your query inside the Model. That servers 2 purposes: 1) mantain a stricter and better coding practice by separating the three logics bheind MVC; 2) keep your code organized and easier to mantain the day you want to make some changes in your query.
It looks you're calling the query just in a single rendred view, but what if you call it in more than once, and you decide one day to change it? You'll need to check all the views and make changes in each of them; by keeping the logic inside the model, you just change one method and you serve the corrected results to all the views at once.
A small addition and a piece of advice: you needn't necessarily call an array-returning function to have results in array form. Doing something like (in your Model):
$query = $this->db->get('mytable'); // this is your fetching;
$data = array();
foreach($query->result() as $row)
{
$data[] = $row;
}
return $data;
You will have a properties which is always in array form ($data), no matter if you choose to use result() and later change it to result_array(); your view will keep the same foreach logic, you will just need to change how you call the values (using array notation or object notation).
UPDATE:
as per your comment, you can either use a different view for when you have no results (but that strongly depends on your design. More commonly, you let the model pass with its data the information on the amount for results. For example:
$query = $this->db->get('mytable'); // this is your fetching;
$data = array();
if($query->num_rows() > 0)
{
foreach($query->result() as $row)
{
$data[] = $row;
}
}
return $data;
In this case, $data starts as an empty array. If rows are returned, the array is populated, otherwise is returned empty as it is. And then, you decide in your view (or in our controller, if you need to load a whole different view in case) how to differentiate this condition by doing like you already do in your code.
Apart from all of this, there's no enforcing law you need to compel to. You can place whatever logic you want inside your view and CI will work nonetheless. Doing a strict separation of Business logic, Data manipulation and Data display just ensures your app will be much more managable in the future; even if you'll be the only one mantaining your code, I'm pretty sure six month from now, when you go back to those views containing a query, you'll curse yourself from not having done the "right" MVC way.
Better not to pass query in your view. Idle practice and core concept of MVC architecture is to separate Controller(Logic) - Model(Code Behind/ Data Manipulation) - View(Templates).
To pass your complete result set in array to the view is recommended. You can pass your query to view BUT in Rare/Specific cases. [e.g you need some data in footer for this case call your model query in view so that you don't have to render model query in each function of controller and pass it to the view.]
I hope, here i made ma point clear.
Thanks.
Related
Im trying to imprive my skills and move away from procedural style coding, however the switch is not coming easy and I am very much new to the OOP concept so please keep this in mind when reading / answering.
I have the following method inside a class called Jobs{}
The task of the method is pretty simple query the database for all the different job categories in a table and return it.
The following is an extract from the code inside my class
function dispCategories(){
$sql = "SELECT DISTINCT category FROM jobs";
$stmnt = $db->prepare($sql);
$stmnt->execute();
return $stmnt->fetchAll();
}
Now inside my view or html page I do the following
$obj = new Jobs();
$categories = $obj->dispCategories()
?>
<!--DISPLAY CATEGORIES -->
<form id="getJobByCategory" method="post" action="">
<select name="selectedCategory">
<?php
foreach ($categories as $category){
?>
<option value="<?php echo $category['category'] ?>">
<?php echo $category['category'] ?>
</option>
<?php
}
?>
My Question
When you look at my code where I initiate the object Jobs{} and call the method categories $categories = $obj->dispCategories()
I use a foreach loop combined with some html & php. Is this an acceptable way of doing things in the OOP realm?
Should I rather create a seperate method like getCategories() where I loop over the results returned by the dispCategories() method?
Alternatively should I just process the data returned by the dispCategories() method inside the actual dispCategories() method...
Hope this make sense. Just looking for some best practices and guidance here, however please keep in mind im a rookie.
Should I rather create a seperate method like getCategories() where I loop over the results returned by the dispCategories() method?
You're definitely on the right track with what you have now. Since your dispCategories() method only returns one column, it makes sense that it would return a one-dimensional array, a simple list of the values rather than a 2D structure that retains characteristics of the SQL query that generated it. I would probably modify the method to fetch all rows and then reduce them to a single array, which you then return. This makes it unnecessary to create another method just for looping over the categories returned by dispCategories().
Assuming PHP 5.5+, you can easily do this with array_column():
function dispCategories(){
$sql = "SELECT DISTINCT category FROM jobs";
$stmnt = $db->prepare($sql);
$stmnt->execute();
// Return a simple 1D array of the category only
return array_column('category', $stmnt->fetchAll());
}
It's then a simple foreach to list them out without needing to access array keys.
foreach ($categories as $category){
?>
<option value="<?php echo $category; ?>">
<?php echo $category; ?>
</option>
<?php
}
Remember that you may need to call htmlspecialchars() on the $category value if it may contain characters requiring HTML entity encoding.
Alternatively should I just process the data returned by the dispCategories() method inside the actual dispCategories() method...
No, I don't think you ought to do this. As you learn OOP, read up on the concept of separation of concerns and on the MVC (model-view-controller) pattern. If you were to create a method in your Jobs class that created this HTML block, it would then be performing work that is more appropriately done by your view code. It should be the responsibility of the Jobs class to retrieve items from the database, provide information about them, and prepare them to be passed to a view or template, but the view or template HTML should retain responsibility for organizing a list of categories from the Jobs class into markup appropriate for however you happen to be displaying it.
How to merge two controllers in one view.
I have two controllers:
1. PostController
2. CommentController
Post controller will show all posts from database, and that posts can have comments. For comments i use another controller CommentController to avoid DRY. In html post list while looping am trying to attach comments if exist for all post bassed on their ID.
In my PostController > indexAction() am fetch all posts
// controllers/PostController.php
/**
* List all posts
*
*/
public function index()
{
$data = array(
'posts' => $this->post->findAll(),
);
$this->load->view('post/index', $data);
}
Here is method from comment controller for listing comments assigning post_id:
// controllers/CommentController.php
/**
* List all comments assigning post id
*
* #param int $post_id
*/
public function index($post_id)
{
$data = array(
'comments' => $this->comment->findAllByPostId($post_id), // <-- SELECT * FROM comments WHERE post_id = {post_id}
);
$this->load->view('comment/index', $data);
}
So now in post/index am fetch all posts:
<?php if($posts): ?>
<?php foreach ($posts as $post): ?>
<h1> <?= $post->title; ?> </h1>
<div> <?= $post->text; ?> </div>
<div class="comment-list">
<!-- How to call here comment controller and index($post->post_id) -->
<!-- i can use load->view('comment/index') but with this i do nothin i need to assigning post id
<!-- Need somthing $commentObject->index($post->post_id) but this in MVC is not good idea -->
</div>
<?php endforeach() ;?>
<?php endif; ?>
Any other solution ?
My solution to slove this is to put all in one controller Post. But i think that is bad practice bcs i will DRY latter. I need that comment controller for other ex(PictureController can have also comments i dont want DRY)
Maybe my process or organization is bad ?
ps. i to search SO for this but that results not helpful for me
the controller gets the data from a model. so in general any database interaction is going to happen in the model, and then that controller asks "did you get my information? if yes, show it, if no, do something else" When everything is sorted out the data is sent to the view.
one controller can call from many different models, and can send more then one data structure to the view.
public function index()
{
// assuming you have a model called 'post'
// check if any posts came back from the search
if( ! $posts = $this->post->findAll() )
{
$this->_showNoPostsReturned() ;
}
// now assuming you have a model called comment
// in your model you will have to foreach through posts etc
// did any comments come back?
elseif( ! $comments = $this->comment->returnFor($posts) )
{
$this->_showOnlyThe($posts) ;
}
// else we have posts and comments
else{ $this->_showBoth($posts,$comments) ; }
}
private function _showBoth($posts,$comments){
// this is how you pass more then one data structure
// array, object, text, etc etc
// with $data['name']
$data['posts'] = $posts ;
$data['comments'] = $comments ;
// and call more then one view if necessary
$this->load->view('post/index', $data);
$this->load->view('comment/index', $data);
}
so this index method is only asking for data from models, and then depending on what if any data it gets back - it calls a separate private method and that method can call the appropriate views. In other words now you don't need to do this in your view
<?php if($posts): ?>
thats what you want to avoid, because then the view is making decisions about what to show. obviously some logic is going to happen in views, but as much as possible all decisions should happen in the controller.
Meta
First of all, I think you do actually want to DRY, as DRY means "Don't Repeat Yourself". I think you got the concept but reading that you "don't want to DRY" is kind of confusing ;)
Answer
Secondly: In a classical MVC approach (which CodeIgniter really much does), one does indeed let the controller handle the model that is then (or data from it) passed on to the view.
Now there are different concepts on how to retrieve all data that you want from the controller, e.g. really reading all of it out in a controller and then passing it on the the view, as compared to only passing the "post" models and let the view take out the posts comments in the view itself. I think both have valid reasons and you can decide which one to use (and there are others, too!), even though I prefer the latter one.
One alternative could be to use a "Decorator Pattern" (see Wikipedia), which seems to have an userland implementation in CodeIgniter only: https://github.com/ccschmitz/codeigniter-decorator
TL;DR
Your approach is imho fine, but you might look into decorator patterns (see above).
I'm new to OOP and MVC and currently building a website using CodeIgniter.
There seems to be a lot of contrasting information about whether loops should be in the view or the model.
On the one hand I'm trying to keep all my html markup inside the views, but on the other hand I want to keep my messy PHP logic outside of the view. Plus I also need to format the data inside my loops using functions located in my model.
What's the best way to go about organising this?
Here is a simplified version of my current implementation:
View
<section>
<ul>
<?php echo $albumTracklistHtml ?>
</ul>
</section>
Controller
$data = [
'$albumTracklistHtml' => $this->MyModel->getAlbumTracklistHtml()
];
$this->load->view('myPage', $data);
Model
public function getAlbumTracklistHtml()
{
//$this->tracklisting returned from db call in other function
foreach($this->tracklisting as $song) {
$mp3 = $this->convertToAmazonUrl($song['mp3']);
$art = $this->formatArtUrl($song['art']);
$name = $this->formatTrackName($song['name']);
$class = 'mp3';
$btn ='';
if(substr($name, 0, 1) == '*') {
$class = 'load mp3';
$btn = '<span class="playBtn"></span>';
}
<li class="'.$class.'" '.$mp3.'>'.$btn.$name.'</li>';
}
}
Very generally speaking, and keep in mind that this isn't a hard and fast rule and if you ask ten different people you'll get ten slightly different answers, but the job of the model view and controller are essentially:
The model provides a way for the controller & view to access data from another source (a database, for instance). It's basically an abstraction on whatever your data is stored in.
The views simply display data they are given.
Controllers connect the model's data with the view, so it can display the data.
I would argue that the example code you've posted is just fine, and fits these definitions. Your model retrieves the data (or processes it), the controller hands the resulting data to the view, and the view simply displays it.
However, I also think it's fine (and generally I prefer this) for the model to simply return a list of items, and then for the view to loop through them and display each one. Of course, the view "shouldn't" be doing a lot of processing, but outputting HTML for each item seems like exactly what it should be doing. The reason I prefer this is purely for separating concerns - your models should be fairly HTML-agnostic. As in, if you ever wrote a non-web-based application to interact with the same data, it could use the same models. Because of this, I would put any HTML-rendering code in my views. Even though it requires some looping logic.
At the end of the day, though, I don't think it matters that much in your case. If you strongly prefer putting the loop in the model, go with that. The most important thing is just to develop your own conventions, and then stick to them.
Here's how I would do the view:
<section>
<ul>
<?php foreach($album->getTracks() as $track): ?>
<li
class="<?php echo $track->isPlayable() ? 'load mp3' : '' ?>"
>
<span class="playBtn">
<?php echo $track->getName() ?>
</span>
</li>
<?php endforeach ?>
</ul>
</section>
This assumes that you've passed a variable called $album, and that a method offered therein returns an array of type Track.
You can return arrays if you like as well, however I prefer objects as you can convert complex conditions to simple, meaningful names. Thus, rather than your '*' test, the programmer calls $track->isPlayable(), which makes much more sense, and doesn't need commenting in the template.
In my experience it depends on what the loop is doing. One can have view specific loops. You can loop through html tags that display elements dynamically and that should go in the view.
for($controller_sent_array as $element)
{
echo "<h4>$element</h4>";
}
However things like looping through file input should go in the controller/model side. I make a point of being vague here because the choice of what should be in models verses controllers depends on framework optimizations. However what is important is that the model and controller are not sending html to be rendered. Rather, they should process data either from a database or user input or network connection, and package that data for the view to figure out. To this end you also need loops.
// This is not a safe way to do this in real life...
for($_POST as $post_input)
{
$this->your_database_library->save_input($post_input);
}
Consider your framework selection with respect to what loops are processing what where, but loops should be used when needed in the model, view, or controller.
I'm using Doctrine2 and CodeIgniter2, and am new to both, as well as to OOP/MVC, so please use simple explanations :)
For testing purposes, I have a Controller a Model and a View. I want to display data from a table that contains user information. First name, last name, ID number, and so forth.
My controller makes a call to the model- which retrieves data from the doctrine entity, and then the controller passes that data to the view.
(controller)
class Test_v_to_m extends CI_Controller {
public function index() {
$this->load->model('testing/test_v_to_m_model');
$data = $this->test_v_to_m_model->display_user_info();
$this->load->view('testing/test_v_to_m_view', $data );
}
}
(model)
class Test_v_to_m_model extends CI_Model{
public function display_user_name() {
$query = $this->doctrine->em->createQuery("select u from ORM\Dynasties2\Users u");
return $query->getResult();
(view)
//print_r($data);
First question is: How do I pass the object or array along to the view in a useful way? This works if I'm just dealing with a single variable:
(controller)
$user = $this->doctrine->em->find('Entities\User', $user_id);
$data['firstname'] = $user->getFirstName();
$this->load->view('testing/test_v_to_c_view_2',$data);
(view)
echo $firstname;
But I don't know how to do something similar when its an array, or a multidimensional array.
The second question is whether or not to let the view do any real work (php logic, loops, foreach, etc) or to do all of that in the controller and have the view only do formatting and display.
Yes, You can just pass multi-dimensional array to the view and then access it as required.
e.g.
$template_date['result_arr'] = array(
array('firstname' => 'abc', 'lastname' => 'xyz')
, array('firstname' => 'abc', 'lastname' => 'xyz')
);
in your view file -
foreach($result_arr as $key => $row) {
echo $row['firstname'].' <br />';
}
Re your 2nd question - As per my understanding - it's fine to use some foreach, for loops in the view but it's best if business logic is kept to controllers and models. Hope it makes sense to you.
As for your first question, I don't know the answer off the top of my head (sorry!). I would imagine, however, that an array can be passed as part of the data (as a single item), but you would need to iterate though it in the view (see below). Just a guess, however...
As for your second question, the principle of MVC is to have only display logic in the view - so all of the "real work" should be done in the controller.
Now, if you want to have a loop to display data in a table, that's "real work" being done in the view, but since it's part of formatting and display that would be acceptable.
Regarding your first question, it's actually quite simple:
$data = array(
'firstname' => 'string',
'array' => array(1, 2, 3),
'multidimensional_array' => array('ocean' => 'deep')
);
In the view, you can access these as:
$firstname;
$array;
$multidimensional_array;
They're just exported to the view, so you can treat each key in the $data array as a variable, and the values in the $data array as the variables' values.
Regarding the second question, it is generally best if you have the view only do formatting and display. In some cases, it might be useful to use ifs or loops, for example, if you want to display different messages based on a certain variable, or if you want to fill a table with a bunch of rows. However, I strongly recommend that you keep out as much logic as possible. The view is meant to receive all the data it needs and display it in a way that suits it.
There are plenty of reasons for this, namely maintainability (if your logic changes, you don't need to update the view), reusability (if you make views as general as possible, you can reuse them very easily) and even the ability to create new views or to replace that view with a different one, without worrying about the logic.
I hope this helps. :)
I just realized that i may not be following best practices in regards to the MVC pattern.
My issue is that my views "know" information about my database
Here's my situation in psuedo code...
My controller invokes a method from my model and passes it directly to the view
view.records = tableGateway.getRecords() // gets array of records
view.display()
in my view
each records as record
print record.name
print record.address
...
In my view i have record.name and record.address, info that's hard-coded to my database. Is this bad?
What other ways around it are there other than iterating over everything in the controller and basically rewriting the records collection. And that just seems silly.
Thanks
EDIT
Heres an actual view
<?php foreach( $categories as $category ): ?>
<tr>
<td><?php echo $category['name'] ?> </td>
<td><?php echo $category['fields'] ?> </td>
<td><?php echo $category['records'] ?></td>
<td>View</td>
</tr>
<?php endforeach; ?>
So a simple loop through the data won't work, i need to capture certain fields of the sql result in my view.
Is there a way around this? It makes me feel dirty.
I'd say it's not bad to have such info hardcoded if you need to have it quick and dirty.
But consider having generic class for views with method that takes your data from db and some array describing which columns to use. Then in the children classes (UserView, PostView, WhateverTableNameView) you could call this base method with array containing "Name", "Address" etc.
Pardon me if I am talking Python gibberish, I came to this question from PHP tag ;) More or less like this
class BaseView {
public function display(& $data, array $columnNames) {
foreach($data as $row) {
foreach($columnNames as $c) {
echo $row->$c; // or $row[$c] or whatever your data is, I'm assuming objects
}
echo "\n";
}
}
class UserView extends BaseView{
public function display(& $data) {
parent::display($data, array('Name', 'Address');
}
}
The nice things here:
Need one more column? Make sure you query for it, then modify 1 line in UserView.
Need to have text for HTML column labels (<th> stuff) - it's already here.
$data could be resource descriptor (think while($rs.nextRow())) and not neccessarily full array that might occupy a lot of memory and take time to pass around from one function to another.
if you go for nice looking HTML tables around these records, you have unified look & feel across application as there's only one place where you define them.
If for some reason this doesn't appeal to you, the truly generic solution is to have indexes instead of column names. $data[$i][0], $data[$i][1] and so on... Most database APIs offer possibility to query for columns as names, as numbers or both. For PHP + MySQL see examples on http://www.php.net/manual/en/function.mysql-fetch-array.php
But this will bite you in the a$$ sooner or later because you lose metadata info. Let's say you want later to wrap your "names" into links:
echo '',$record['name'],'';
Good luck doing this in reusable way without column names...
With getters/setters and a piece of code to map the record fields to them you can remove this, but you'll add some complexity.
The real question is: Do i need to rename field names at all?
With some planning/thinking/feedback it shouldn't be hard to find appropriate names for your fields that survive the applications lifetime. However, if the semantics of the field change you should add a new field. This has also the advantage that you can clearly document the deprecation of it and lead the programmer to the new one.