Maybe due to lack of sleep I have some problems with my logic.
In CodeIgniter, I need to get the last 10 posts from the DB and display them on the page.
My Controller is as follows:
public function index() {
// $data is an array containing some useless stuff like title, etc.
// Never mind.
// Skipped header & navbar view loaders;
$this->load->view('blog_page', $data);
$this->get_latest_posts(1);
}
Now the model goes:
public function get_latest_posts($i) {
// $i is a number of posts to be displayed. Now it is 10
$query = $this->db->get('posts', $i);
return $query->result();
// Any clue what to do here, cause I need an array that contains arrays of post rows;
}
I've been trying to do a foreach cycle, but it only works when using one row;
I would appreciate your help, guys!
Every answer will have a positive impact on the life of every kitten on a planet.
P.S. I removed any unnecessary stuff from my code, like loading header&footer views.
controller function
public function index() {
// $data is an array containing some useless stuff like title, etc.
// Never mind.
// Skipped header & navbar view loaders;
$this->load->view('blog_page', $data);
$data['get_latest_post'] = $this->your model name->get_latest_posts(1);
$this->load->view('your view file name',$data);
}
model function
public function get_latest_posts($i) {
$query = $this->db->get('posts', $i);
$this->db->limit(10);
}
is this fine..
Related
I'm currently learning the ropes of the MVC pattern and came across a problem I can't seem
to fix in a way I want and is in line with the MVC pattern.
I have set up the router, controllers and views up successfully.
The only thing I don't really get is the use of the Model. I know it's supposed to
serve the Data to the view, and here it is I have a problem.
I want to pass a function thru my view method, but it executes before it should be.
is there a way
I will try to be as specific as possible about the situation so sorry for the long post.
The controller class is this:
class Controller{
private $tpl_name = 'default';
public function model($model){
require('../admin/model/'.$model.'.model.php');
return new $model();
}
public function view($page_title,$file_paths,$params,$data = []) {
// takes an array with the file paths
$this->content = $file_paths;
$tpl_name = $this->tpl_name;
require_once('templates/'.$tpl_name.'/header.php');
require_once('templates/'.$tpl_name.'/nav.php');
require_once('templates/'.$tpl_name.'/content-top.php');
foreach ($file_paths as $content){
require_once('view/'.$content);
}
require_once('templates/'.$tpl_name.'/content-bottom.php');
require_once('templates/'.$tpl_name.'/footer.php');
}
}
The view renders the template I want, takes parameters from the router and, the data that
needs to be handled in the desired view. So far so good.
I want to serve my posts in my admin panel that displays a table of all the posts in the DB.
I have written a method that fetches the data, and a method that writes the data.
class Post{
......
//other functions above
public function displayPosts(){
// get's all the posts form the data base, returns an object array
$posts = Post::fetchContent('posts',0);
// array get's passes to the write function which will write out the data.
$writer = Post::write($posts);
}
static public function write(Array $posts){
foreach($posts as $single){
// for each object in the array, assign the vars so the view can handle them
// to create a single row in the table for each object:
$trashed = $single->getTrashed();
$id = $single->getID();
$title = $single->getTitle();
$category = $single->getCategory();
$content = $single->getContent();
$author = $single->getAuthor();
$date = $single->getDate();
$approved = $single->getApproved();
$dbt = $single->getDbt();
// This is a template which represents a table row with the post data I need.
require('view/content_table.php');
}
//controller file (needs to moved to other file later): handles approve/remove/edit/delete actions.
require('view/manage_content.php');
}
}
Now we have arrived at the problem:
When I call the model in my controller and render the view, it will execute immediatly
before the rest of my view loads, resulting in errors, although it displays the data,
it is not in my template, but above it, just in plain text.
errors:
Notice: Undefined variable: _SESSION in /Volumes/HDD Mac/Websites/server/admin/view/content_table.php on line 8
Warning: session_start(): Cannot send session cache limiter - headers already sent (output started at ...)
class Dashboard extends Controller {
public function index($params = null){
$model = $this->model('Post');
$posts = $model->displayPosts();
// view takes: page_title,[array of view files],params from the router,array of data from model
$this->view('Dashboard',['admin.php'],$params,[ 'posts' => $posts]);
}
}
Before I was trying to use MVC I just outputted this in my view:
And it worked just fine.
Non relevant HTML above
$posts = Post::fetchContent('posts',0);
// array get's passes to the write function which will write out the data.
$writer = Post::write($posts);
Non relevant HTML below
But now when I pass the display post function, I just want to do this in my view:
echo $data['posts'];
which doesn't work because it already executed my Write function.
The only way I could work around like this was by adding the content of my write function to the view,
and only pass the fetchContent method to my view method (this will output an array of objects).
But since I need this info in two place I dont want to repeat this code, I would prefer echoing
all out.
Non relevant HTML above
$posts = $data['posts'];
foreach($posts as $single){
// for each object in the array, assign the vars so the view can handle them
// to create a single row in the table for each object:
$trashed = $single->getTrashed();
$id = $single->getID();
$title = $single->getTitle();
$category = $single->getCategory();
$content = $single->getContent();
$author = $single->getAuthor();
$date = $single->getDate();
$approved = $single->getApproved();
$dbt = $single->getDbt();
// This is a template which represents a table row with the post data I need.
require('view/content_table.php');
}
//controller file (needs to moved to other file later): handles approve/remove/edit/delete actions.
require('view/manage_content.php');
Non relevant HTML below
Is it bad practise to just skip the use of the Model here and do it like this:
Non relevant HTML above
$posts = Post::fetchContent('posts',0);
// array get's passes to the write function which will write out the data.
$writer = Post::write($posts);
Non relevant HTML below
Or is there a way to rewrite my Post::Write function? Or just use the foreach loop in the view?
Thank you all for taking the time!
If you need more info, just ask:-)
Output is wrong , i Got three the same comment instead only one.
Can anyone help to make only one comment.
Here my view php code:
$modelid = "5" ;
$query = $this->db->query('SELECT message FROM message WHERE modelid = '.$modelid.' ');
$row = $query->row();
echo $row->message;
and my Table:
http://dev.interactive-creation-works.net/Stack/table.png
The controller:
class Comment extends CI_Controller
{
function index()
{
$data['result'] = $this->db->get('message')->result();
$this->load->view('commentView',$data);
}
function insert()
{
$this->load->model('commentjquery');
echo $this->commentjquery->inserttodb();
}
}
This view should only show one message, even if you do - for whatever reason, correctly or not - get three messages from the query. You may be loading the view three times in your controller. Try adding some static text to the top and bottom of your view, and see if it shows up multiple times.
Edit:
Now that you've posted the controller, I can see that's not the issue. But I notice you're trying to grab the comments from the database twice - once in the controller and once in the model. The one in the controller is actually the wrong way to do it unless you've implemented an ActiveRecord model for your comments, and you're not using the results anyway.
Another thing I noticed just now is that you're calling $query->row() instead of running a foreach over $query->result(). Try this:
foreach($query->result() as $row) {
echo $row->message;
}
Edit 2: Or maybe it is the problem, if the code snippet you posted isn't the entire view, which seems to be the case.
It's really weird that you aren't getting error. It gets weirder when you get three outputs.
Change your query to this.
$query = $this->db->query('SELECT message FROM message WHERE modelid = "'.$modelid.'"');
I'm using codeigniter to code my site and I'm running into a roadblock. I know how to do this in regular, non "MVC", not OOP PHP, but am struggling on it in Codeigniter.
I have blog_model.php, which has a function to retrieve the datetime from my database, explode it into an array so that I can work with it outside the model and feed it into CSS where I have individual calendar icons for it. Each calendar icon is loaded by the month number in the view (<div class='calendar-icon-$stats['date']). This function also pulls the amount of comments from that individual post and outputs it into an array so that I can show it in the view.
public function get_stats($id) {
$this->db->select('id,datetime')->from('blog_posts')->where('id',$id);
$dquery = $this->db->get();
$dquery = $dquery->row_array();
$date = date('Y-m-d', strtotime($dquery['datetime']));
$stats = explode("-", $date); // This makes $stats[0] the year, $stats[1] the month and $stats[2] the day.
$stats['time'] = date('H:i', strtotime($dquery['datetime']));
$stats['comcount'] = $this->db->get_where('blog_comments', array('blogid' => $id));
$stats['comcount'] = $stats['comcount']->num_rows();
return $stats;
}
There is also a function to retrieve the three most recent entries:
public function get_blog_last() {
$query = $this->db->order_by('id desc')->get('blog_posts',3);
return $query->result_array();
}
This code is then loaded into my controller and sent to the view to be displayed:
$data['blog'] = $this->blog_model->get_blog_last();
$data['stats'] = $this->blog_model->get_stats($data['blog']);
$this->load->view('index',$data);
The problem I face is how to get the get_stats() function to run for every entry I have on the index page, where the last three entries are displayed. So far I can only get it to run for one of them, therefore all three of the entries on my front page have the same date, the same time and the same amount of comments. I figured putting the code in a model would save myself from repeating myself when I had to load it for the archives page (where I display all the posts from the month) and the main entry page where I just display that entry and its comments.
So, the ultimate question here is:
How do I run get_stats for every entry I have on a given page?
I'm also having a bit of issue figuring out the correct value to pass to my get_stats() function.
Any guidance would be appreciated. Thank you in advance.
If I'm understanding correctly, you need to call get_stats for each of the three entries that you receive in get_blog_last. If that is the case, just change get_blog_last to this:
public function get_blog_last() {
$query = $this->db->order_by('id desc')->get('blog_posts',3);
$entries = $query->result_array(); // get the latest entries array
foreach ($entries as $index => $entry) { // loop through those entries
$stats = $this->get_stats($entry['id']); // call this model's `get_stats` method
$entries[$index]['stats'] = $stats; // add a `stats` key to the entry array
}
return $entries;
}
Why don't you put
$this->blog_model->get_stats($data['blog']);
inside loop ? ( i'd rather use normal loop )
example :
$stat_list = array();
for($i=0;$i<count($data['blog']);$i++){
$stat_list[] = $this->blog_model->get_stats($data['blog'][$i]);
}
$data['stats'] = $stat_list;
and in your view, you should try the same to print out each $stat_list
i need differents results from a model but i don't understand if it is correct make a single call and leave to model all the work or make more calls and collect the result to pass to the view when tables aren't joined or when i need fetch one row from a table and differents rows from others.
First example (more calls, collect and send to view):
CONTROLLER
// call functions of model
$modelName = new Application_Model_DbTable_ModelName();
$rs1 = $modelName->getTest($var);
$rs2 = $modelName->getTest2($var2);
// collect data
$pippo = $rs1->pippo;
if ($rs2->pluto == 'test') {
$pluto = 'ok';
} else {
$pluto = 'ko';
}
// send to view
$this->view->pippo = $pippo;
$this->view->pluto = $pluto;
MODEL
public function getTest($var) {
...
select from db...
return $result;
...
}
public function getTest2($var) {
...
select from db...
return $result;
...
}
Second example (one call, model collect all data, return to controller and send to view):
CONTROLLER
// call one function of model
$modelName = new Application_Model_DbTable_ModelName();
$rs = $modelName->getTest($var);
MODEL
public function getTest($var) {
...
select from db...
if ($result > 0) {
call other function
call other function
collect data
return $result;
...
}
Thanks
There's no one correct answer to this question, but in general, you should endeavor to keep your business logic in one place. Think of it as, "thin controller, thick model." I.e., keep the controllers as small and simple as possible and put all the business logic in the models.
There seems to be a few questions here:
But if i don't need to interact with db and i need only a simply
function is better put that function in model? For example:
CONTROLLER:
public function printAction() {
$data = $this->getRequest()->getPost();
$label = "blablabla";
$this->view->label = $label;
}
first, in the context of Zend Framework this particular example doesn't make much sense. The whole point of the controller is to populate the view template. However, I do get the idea. I would point you to Action Helpers and View helpers as a means to address your concerns. You can always add a utility class to your library for those pieces of code that don't seem to fit anywhere else.
Action Helpers typically are employed to encapsulate controller code that may be repetitive or reusable. They can be as simple or as complex as required, here is a simple example:
class Controller_Action_Helper_Login extends Zend_Controller_Action_Helper_Abstract
{
/**
* #return \Application_Form_Login
*/
public function direct()
{
$form = new Application_Form_Login();
$form->setAction('/index/login');
return $form;
}
}
//add the helper path to the stack in the application.ini
resources.frontController.actionhelperpaths.Controller_Action_Helper = APPLICATION_PATH "/../library/Controller/Action/Helper"
//the helper is called in the controller
$this->_helper->login();
a View helper does the same thing for the view templates:
class Zend_View_Helper_PadId extends Zend_View_Helper_Abstract
{
/**
* add leading zeros to value
* #param type $id
* #return string
*/
public function padId($id)
{
return str_pad($id, 5, 0, STR_PAD_LEFT);
}
}
//in this example the helper path is added to the stack from the boostrap.php
protected function _initView()
{
//Initialize view
$view = new Zend_View();
//add custom view helper path
$view->addHelperPath('/../library/View/Helper');
//truncated for brevity
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer');
$viewRenderer->setView($view);
//Return it, so that it can be stored by the bootstrap
return $view;
}
//and to use the helper in the view template
//any.phtml
<?php echo $this->padId($this->id) ?>
i need differents results from a model but i don't understand if it is
correct make a single call and leave to model all the work or make
more calls and collect the result to pass to the view when tables
aren't joined or when i need fetch one row from a table and differents
rows from others.
This question is more about structure then about correctness.
You can interact with your database table models in Action and View helpers for simple/repetitive queries if you need to, however most developers might frown on this approach as being difficult to maintain or just ugly.
Many people seem to favor Doctrine or Propel to help them manage their database needs.
At this point I like to roll my own and currently favor domain models and data mappers, not an end all be all pattern, but seems to be appropriate to your question.
This is not a simple suggestion to implement for the first time, however i found two articles helpful to get started:
http://phpmaster.com/building-a-domain-model/
http://phpmaster.com/integrating-the-data-mappers/
and if you really want to get into it try:
http://survivethedeepend.com/
I hope this answers at least a part of your questions.
I want to have a delete button underneath blogs entered just by the owner of the current profile, I have tried implementing a deleteMyBlog function but no joy so far. whats the best way to go about this?
Here is my view. I know I would need but some delete button here but I'm not sure how to fit around my current foreach loop:
<?foreach($blogs AS $viewData):
$delete = $viewData['id'];
{
$id = $viewData->id;
$title = $viewData->title;
$body = $viewData->body;
$username = $viewData->username;
$date = $viewData->date;
?>
<b> <?=$title?></b>
<p><?=$body?></p>
<p>posted by:<?=$username?></p>
<p>date: <?=$date?></p>
<?=anchor("blog/deleteMyBlog/$delete", 'delete')?>
<hr>
<?
}
?>
My model:
class Blogmodel extends CI_Model
{
public function __construct()
{
parent::__construct();
}
function deleteMyBlog($id)
{
$this->db->where(array('id' => $id));
$this->db->delete('blogs');
}
public function get_last_ten_entries()
{
$query = $this->db->get('blogs', 10);
return $query->result();
}
public function insert_entry()
{
$this->title = $this->input->post('title');
$this->body = $this->input->post('text');
$this->username = $this->session->userdata('username');
$this->date = date("Y-m-d");
$this->db->insert('blogs', $this);
}
}
Controller:
class Blog extends CI_Controller {
public function _construct()
{
parent::__construct();
$this->load->model('Blogmodel','Blog');
$this->load->model("profiles");
}
function deleteMyBlog($id) {
$this->blogs->deleteBlog($id);
redirect('blog');
}
public function index()
{
$username = $this->session->userdata('username');
$viewData['username'] = $username;
$this->load->model('Blogmodel');
if($this->input->post('act') =='create_post')
{
$this->Blogmodel->insert_entry();
}
$viewData['blogs'] = $this->Blogmodel->get_last_ten_entries();
$this->load->view('shared/header');
$this->load->view('blog/blogtitle', $viewData);
$this->load->view('shared/nav');
$this->load->helper('form');// Load the form helper.
// Lets set the stuff that will be getting pushed forward...
$data = array();
$data['form_open']=form_open();
$data['form_title'] = form_input(array('name' => 'title'));
$data['form_text'] = form_textarea(array('name' => 'text'));
$data['form_hidden'] = form_hidden('act','create_post');
$data['form_submit'] = form_submit('submit','Make Post');
$this->load->view('blog/blogview');
$this->load->view('blog/post', $data);
$this->load->view('shared/footer');
}
}
Thanks again guys
Simplest way is with assigning username to a variable, then with the SQL statement.
Delete from tbl where colname='$username'
That's the way I would do it, other people might have different methods. So all respect to those who would use somethin different
You're getting the error because of this bit:
<?foreach($blogs AS $viewData):
$delete = $viewData['id'];
It should be this:
$delete = $viewData->id;
You're using the exact same data a line later correctly, why are you trying to use $viewData which is an object as an array here, but an object 2 lines later. Other than that the rest of what you're doing there should work fine but it is rather dangerous in practice. If I go to your site and type the url to that controller function with a blogId at the end that blog goes away, at no point are you checking that the user actually should be allowed to delete that blog. Obscurity != Security. Meaning that just because you think people won't find the link doesn't mean they won't.
Personally I save the userId of a logged in user to the session and save the session to the database. Then when I do anything to user records I do a check to ensure the user making the change has the authorization to make that change.
So your delete function would be something like this:
function deleteMyBlog($id)
{
$this->db->where('username',$this->session->userdata('username');
$this->db->where('id',$id);
$this->db->delete('blogs');
}
Also you should be using userId's not usernames for saving to other tables, the indexes work better on numerical ID's as far as I know and it's less overall data in the tables. Saving userId 342 to your blogs table takes up less space than saving username bobsyouruncle3421.
For the record, I know this isn't part of the question but actually deleting things from the database has downsides. Not the least of which is screwing up the indexing and slowing down queries in the long run. A far better solution is adding a status or active column to any tables you may want to delete from and giving them a value of 1 for active and 0 for deleted. Then instead of actually deleting the item you change it's active column to 0. When displaying items you add a check for active = 1 to the display query.
This serves two purposes, first you don't mess up the indexing, the record is never removed just modified so the indexes remain intact. Second and nearly important is you never have the possibility of accidentally deleting something you didn't mean to delete, it is never really gone. So you could "undelete" anything at any time.