I wrote a posts.php page which shows a certain post and its comments, giving the user the ability to add a new comment to this post dynamically.
I want to implement the *"submit_comment"* using Ajax, but I don't really know how to do it in MVC.
This is my Posts.php:
<script type="application/javascript" src="Ajax/addComment.js"> </script>
src="../views/Ajax/addComment.js"> </script> <title> Posts
(View)</title> </head>
<body> <div id="main"> <div class="container"> <?=$data['header'];?>
<div id="content">
<!-- Main Post -->
<div class="content-background">
<h2> <?=$data['title'];?></h2>
<h4> <?=$data['date'];?> </h4>
<p> <?=$data['content'];?></p>
</div>
<div id="form-content">
<form name="commentForm" method="post">
Enter your name: <input type="text" name="username" id="username"> <br />
Enter your comment: </br> <textarea name="comment" id="comment" cols="10" rows="10"> </textarea> <br />
<input value='Submit' type='button' onclick='JavaScript:commentRequest2("<?=$data['id']?>")'
name='submit'>
</form>
</div>
<div id="commentArea">
<?=include_once("comments_area.php");?>
</div><!-- end commentArea -->
</div> </div> </div>
</body> </html>
This is My Posts_Controller.php:
<?php
/**
* This file handles the retrieval and serving of posts posts
*/
class Posts_Controller
{
/**
* This template variable will hold the 'view' portion of our MVC for this
* controller
*/
public $template = 'posts';
/**
* This is the default function that will be called by router.php
*
* #param array $getVars the GET variables posted to index.php
*/
public function main(array $getVars)
{
$postsModel = new Posts_Model;
}
else{
//get the post
$post = $postsModel->get_main_post("`id`='", $getVars['id'], "LIMIT", "1");
//get the comments
$comments = $postsModel->get_comments("`postID`='", $getVars['id']);
//create a new view and pass it our template
$header = new View_Model('header_template');
$view = new View_Model($this->template);
//assign post data to view
$view->assign('header', $header->render(FALSE));
$view->assign('title' , $post['title']);
$view->assign('content' , $post['content']);
$view->assign('date' , $post['date']);
$view->assign('by' , $post['added_by']);
$view->assign('id' , $post['id']);
$view->assign('commentsArr' , $comments);
$view->render();
}
}
}
And This is My Posts_Model.php:
<?php
/**
* The Posts Model does the back-end heavy lifting for the Posts Controller
*/
class Posts_Model
{
/**
* Holds instance of database connection
*/
private $db;
public function __construct()
{
$this->db = new MysqlImproved_Driver;
}
/**
* Fetches article based on supplied name
*
* #param string $author
*
* #return array $article
*/
public function get_main_post($cond1, $var1, $cond2 ="", $var2 = "")
{
//connect to database
$this->db->connect();
//sanitize data
$var1 = $this->db->escape($var1);
$var2 = $this->db->escape($var2);
$cond = $cond1.$var1."'";
$cond.= " ".$cond2." ".$var2;
//prepare query
$this->db->prepare
(
"
SELECT * FROM `posts`
WHERE $cond
;
"
);
//execute query
$this->db->query();
$article = $this->db->fetch('array');
return $article;
}
public function get_comments($cond1, $var1, $cond2 ="", $var2 = "")
{
//connect to database
$this->db->connect();
//sanitize data
$var1 = $this->db->escape($var1);
$var2 = $this->db->escape($var2);
$cond = $cond1.$var1."'";
$cond.= " ".$cond2." ".$var2;
//prepare query
$this->db->prepare
(
"
SELECT * FROM `comments`
WHERE $cond
;
"
);
//execute query
$this->db->query();
$resultArr[0] = 0;
$i = 1;
while( $result = $this->db->fetch('array')){
$resultArr[$i] = $result;
$i++;
}
$resultArr[0] = $i;
return $resultArr;
}
}
?>
Where Should I add the addComment.js? (V, M or C?)
And what should the addComment do? what URL/ function to call?
Any suggestions? Examples?
In your controller create a function that will allow you to add javascript files to your views. Then call it in your controller. It will add the file path to an array, and just before the view is displayed, assign it to the template.
$view->addJavascript('/Ajax/addComment.js');
Or you can just use
$js_files[] = '/Ajax/addComment.js';
$view->assign('js_files', $js_files);
then in your view you can use
<?
foreach($data['js_files'] as $file)
{
echo '<script type="application/javascript" src="'.$file.'"> </script>'
}
?>
Not sure how your routing is structured in your application, but you would create a method similar to
main for the ajax functionality. if you mention more about how URLs route to Controller then I might bbe able to make a more educated suggestion.
Before you even try to learn how to use MVC, you should learn how to write semantic HTML and unobtrusive JavaScript! Judging by posts.php files, you really have no clue what you are doing.
I would recommend reading everything from this page. It will contains a lot of very good materials about HTML. Then watch this lecture and then watch everything on http://yuiblog.com/crockford/. Also i would advise you read Eloquent Javascript book.
Are you using a home-made framework or is it some 3rd part product?
Anyway. You should start by making a non-AJAX method for adding comments. In your current setup it would most likely happen by adding post_comment() method in Posts_Model. And then creating a separate method in Posts_Controller, for dealing with POST requests.
When that is done, you can start working on using AJAX to post new comments for the article. Your '/views/Ajax/addComment.js' file should be added in posts.php files (what you call "view", but it isn't). You should use absolute path for including that JS file.
The AJAX request should be executed through event handlers which is attached to onsubmit event for the <form name="commentForm">. You gather data from form's elements and create XHR request. To learn how to do it, read this article.
That said, your code is vulnerable to SQL injections. Just because you use PDO/MySQLi it will not magically protect you. You actually need to know how to use it too. I would recommend reading this article (yes, it is for PDO, but the theory does not change). Especially this section. It should explain you how to properly use prepared statements and why it is important. For MySQLi equivalents you will have to read the MySQLi::bind_param() manual entry.
Related
So I am trying to pass data from my controller to my model which will than query the database and return the results back to the controller, then the controller will send them to the view so that they can be displayed however I keep getting a blank page.
This is what my Controller code looks like:
class WelcomePageController extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('guestsearchmodel');
$this->load->helper('url');
}
public function index()
{
$this->load->view('WelcomePageView');
}
public function SearchQuestion()
{
$SearchTerm = $this->input->post('GSearch');
/* Checks to see if a field has been left blank if so then it
* will show and error
*/
if ($SearchTerm == null||$SearchTerm == '')
{
$this->load->view('WelcomePageView');
/* This is a little script that displays an alert
* box on screen when a field has been left blank.
*/
echo '<script type="text/javascript">
window.onload = function () {
alert("No Search Terms Have Been Entered Please Try Again.");
}
</script>';
return false;
}
/* This will call the AddNewUser function from the Model*/
$Term = $this->guestsearchmodel->GuestSearch($SearchTerm);
$data['term'] = $Term;
$this->load->view('GuestSearchResultsView',$data);
}
And this is the code from my model:
class guestsearchmodel extends CI_Model {
function __construct()
{
parent::__construct();
$this->load->database();
}
function GuestSearch($Term)
{
$results = "SELECT * FROM Questions WHERE Question Title LIKE '%".$this->db->escape_like_str($Term)."%'";
$data = $results->row_array();
$results->free_result();
return $data;
}
And for reference this is code from my view but none of it displays, it's just a blank screen:
<html>
<head>
<title> My Website </title>
<link rel="stylesheet" href="<?php echo base_url();?>Assets/CSS/SampleCSS.css" type="text/css" />
</head>
<body>
<header>
<ul>
<li>Login</li>
<li>Register</li>
<li>Home</li>
</ul>
</header>
<!--This is my Welcome Page View which was loaded by the WelcomePage Controller -->
<h1> Welcome to my Advanced Web Coursework! </h1>
<hr>
<p>The Contents of my Guest Search Result are yet to be decided.</p>
<!--<?php echo $term['Question Title']?>-->
<footer>
Details about my Website
</footer>
P.S: Sorry for any other mistakes or inefficient code, as this is just very early stages of development. Also I am not the best of programmers unfortunately.
EDIT: After Some testing I have discovered that when I add This Code:
$results = "SELECT * FROM Questions WHERE 'Question Title' LIKE '%".$this->db->escape_like_str($Term)."%'";
$data = $results->row_array();
$results->free_result();
return $data;
The page just loads as a blank page, therefore something in this part of the code must be breaking it.
You don't appear to be actually running the query.
Try something like the following
$sql = "SELECT * FROM Questions WHERE 'Question Title' LIKE '%".$this->db->escape_like_str($Term)."%'";
$results = $this->db->query($sql);
$data = $results->row_array();
$results->free_result();
return $data;
I believe there may be an error in your SQL query.
"SELECT * FROM Questions WHERE Question Title LIKE '%foo%'"
Here, your field appears to have a space in its name;
... WHERE Question Title LIKE ...
^--- space
In this case, the correct syntax would be:
... WHERE `Question Title` LIKE ...
EDIT: For MySQL, at least.
I'm totally new to smarty... and it's creeping me out :)
I got the following class in /inc/class/search.php:
Class search
{
function __construct($request) {
global $dbconn;
$request = htmlspecialchars($request);
$sql = "SELECT * FROM table WHERE id LIKE '%{$request}%'";
$res = $dbconn->Query($sql);
$entity = $res->fetchArray();
return $entity;
}
}
I have this in php_head.php:
if (isset($_REQUEST['search']) && !empty($_REQUEST['search'])) {
$is = new search($_REQUEST['search']);
$smarty->assign("searchValues", $is);
}
This code in php_head is designed to be called by ajax later on. But when I run index.php?search=string I get the whole smarty template. Please help.
What you need to do is displaying only some part of output when search is in URL.
So you could modify your code this way:
if (isset($_REQUEST['search']) && !empty($_REQUEST['search'])) {
$is = new search($_REQUEST['search']);
$smarty->assign("searchValues", $is);
$smarty->display('searchvalues.tpl'); // custom base template
exit; // stop execution of later code
}
And you should create searchvalues.tpl template and display here only this part that you want to display and not the whole base template.
You need to clear template that you need for ajax, and if you want to include it in some other template
{include file="path_to_template.tpl"}
And when you need only the result from this template use
echo $smarty->fetch('path_to_template.tpl');
For example you have :
$smarty->display('index.tpl');// this will return index.tpl
And in index.tpl :
<div id="result_ajax">
{include file="ajax_template.tpl"}
</div>
And in ajax.php :
//Do some stuff
echo $smarty->fetch('ajax_template.tpl');
I'm trying to write a simple mvc application with php and mysql. I'm very new to mvc and relativly new to php aswell. I'm letting the user choose from different movies and then add the ones they want to their own list. But I can't figure out how to get the correct form action to insert the choosen movie into the db.
This is how my two model class methods looks like:
public function checkMovie() {
// Check if movie exist in db.
$stmt = $this->dbh->prepare("SELECT * FROM watchlist WHERE my_title='{$_POST['my_title']}'");
$stmt->bindParam(':my_title', $_POST['my_title']);
$stmt->execute();
$rows = $stmt->fetchALL();
$this->n = count($rows);
}
public function addMovie() {
// Add choosen movie to db.
$sql = $this->dbh->prepare("INSERT INTO watchlist(my_title, my_des, my_link)
VALUES ('{$_POST['my_title']}', '{$_POST['my_des']}', '{$_POST['my_link']}')");
$sql->bindParam(':my_title', $_POST['my_title'], PDO::PARAM_STR);
$sql->bindParam(':my_des', $_POST['my_des'], PDO::PARAM_STR);
$sql->bindParam(':my_link', $_POST['my_link'], PDO::PARAM_STR);
$sql->execute(array(':my_title' => $_POST['my_title'],':my_des' => $_POST['my_des'],':my_link' => $_POST['my_link']));
}
As you can see I have the basic sql-code in here and then I call the methods from a method in my controller:
public function getAddMovie() {
$this->addModel = new AddMovieModel();
if (isset($_POST['submit'])) {
// Call checkmovie from addmoviemodel and check if movie allready is taken.
$checkmovie = $this->addModel->checkMovie();
if($this->n > 0) { // Should this logic perhaps be in my model?
// Shows javascript-popup eg. 'movie allready added'.
include 'view/viewscripterror.php';
}
else { // Call addMovie from addmoviemodel to insert movie to db.
$addmovie = $this->addModel->addMovie();
// Shows javascript-popup eg. 'movie is now added'.
include 'view/viewscriptsuccess.php';
}
}
}
I'm not sure if the if($this->n > 0) perhaps should be in my model aswell?
And here's the form, I can't figure out what to pass as form action? This problem has been driving me crazy for a while now and that's why I'm turning here in hope for some help.
echo '<form action="??" method="post">',
'<input type="hidden" name="my_title" value="'.$title.'">',
'<input type="hidden" name="my_des" value="'.$description.'">',
'<input type="hidden" name="my_link" value="'.$link.'">',
'<input type="submit" name="submit" value="Peppa!">',
'</form></div>';
Try like
echo '<form action="http://site_url/getAddMovie" method="post">',
You need to pass the url of the function getAddMovie into the action,then after submitting it,it will post/get the params into that function.
And try to load the model like
$this->load->model('AddMovieModel');
And try to call it like
$checkmovie = $this->AddMovieModel->checkMovie();
Or even you can try like
$addModel = new AddMovieModel();
and call it like
$checkmovie = $addModel->checkMovie();
I have a table name "Category" which contains (cat_id, name, description). I can insert, retrieve, and delete without any problems. But when I update my Category, no data inserted in my database. I check my table and the result is nothing.
The POST model "Category_Model extends CI_Model":
public function custom_query($data)
{
$q = $this->db->query($data);
return $q;
}
The POST controller "Category extends CI_Controller":
public function edit_category()
{
$data['title'] = "Edit Category Page";
$this->load->view('edit_category', $data);
}
public function update_category()
{
$id = $this->input->post('cat_id'); // I try $id = $this->uri->segment(3); but no result
$name = $this->input->post('name');
$desc = $this->input->post('description');
$this->post_model->custom_query("update category set cat_name='".$name."', description='".$desc."' where cat_id='".$id."'"); // when I delete 'where cat_id='".$id."'' clause, all my records were changing/updating
// I change to $this->db->where('cat_id', $id); $this->db->update('category'), but no result.
redirect ('category/view_categories');
}
Here is my EDIT CATEGORY view:
<form action="<?php echo base_url(); ?>category/update_category" method="POST">
<fieldset>
<legend>Edit Category</legend>
<label for="cat">Name :</label>
<input type="text" name="name"/>
<label for="desc">Descriptions :</label>
<textarea name="description" cols="40" rows="2"></textarea>
<input type="submit" value="Update">
</fieldset>
</form>
Please anyone tell me what was wrong with my code? Thank in advance
best regards.
*note: I put 'database' in autoload config.
First of all, are you sure you writing table name correctly?
..."update kategori..."
If this is ok, try to output your query before sending it to database, like this:
$query = "update kategori set cat_name='".$name."', description='".$desc."' where cat_id='".$id."'";
error_log('My query: ' . print_r($query, true));
$this->post_model->custom_query($query);
Then, if you won't see any problems in that query, give it to us.
It looks like your query might not be getting the cat_id as I don't see it anywhere in the passing view. Try a hidden field in the HTML which contains the cat_id. This might also be easier than trying to get it via URI segments.
You could be learn about CI models, it will simplify your life.
I believe with, for some reason, the redirect could be close your connection before the commit... It doesn't occur if you use the model object.
A little sample for models...
Create a class on application/models, like this
file "category_model.php"... attention for this name, because the CI is very restrictive with model name. Must by equal class name, but all lowercase.
class Category_model extends CI_Model {
// your fields
var $id = null;
var $name = null;
// call parent constructor... it's essential
function __construct() {
parent::__construct();
}
// create a set function, for fill all fields directly from get or post
function set($data) {
$this->id = isset($data['id']) ? $data['id'] : null;
$this->name = isset($data['name']) ? $data['name'] : null;
}
// execute update on database
function update($id) {
$this->db->update('category', $this, array('id' => $this->id));
}
}
on the controller, instance and invoke the model
$this->load->model('Category_Model', null, true);
$this->Category_Model->set($this->post());
$this->Category_Model->update();
after this, proceed you normal code.
Following this tutorial http://johnsquibb.com/tutorials/mvc-framework-in-1-hour-part-one Im trying to write my first MVC Blog.
I understood how it works, router.php Calls the suitable page controller. This controller calls the model, Then calls the View page with the returned value.
My question is, What if I want to add to the same news.php page a header\footer. Normally I would write "include("header.php"). But now when using MVC, how could I implement that?
These are my files:
router.php:
<?php
/**
* This controller routes all incoming requests to the appropriate controller
*/
//Automatically includes files containing classes that are called
//fetch the passed request
$pageURL = $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
$path_parts = pathinfo($pageURL);
$page_name = $path_parts['filename'];
$parsed = explode('?' , $page_name);
//the page is the first element
$page = array_shift($parsed);
// If there is any variables, GET them.
if(!empty($parsed))
{
$parsed = explode('&' , $parsed[0]);
$getVars = array();
foreach ($parsed as $argument)
{
//explode GET vars along '=' symbol to separate variable, values
list($variable , $value) = explode('=' , $argument);
$getVars[$variable] = urldecode($value);
}
}
//compute the path to the suitable file
$target ='controllers/' . $page . '_controller.php';
//get target controller
if (file_exists($target))
{
include_once($target);
//modify page to fit naming convention
$class = ucfirst($page) . '_Controller';
//instantiate the appropriate class
if (class_exists($class))
{
$controller = new $class;
}
else
{
//did we name our class correctly?
die('class does not exist!');
}
}
else
{
//can't find the file in 'controllers'!
die('page does not exist!');
}
//once we have the controller instantiated, execute the default function
//pass any GET varaibles to the main method
$controller->main($getVars);
// AutoLoad
function __autoload($className)
{
// Parse out filename where class should be located
// This supports names like 'Example_Model' as well as 'Example_Two_Model'
list($suffix, $filename) = preg_split('/_/', strrev($className), 2);
$filename = strrev($filename);
$suffix = strrev($suffix);
//select the folder where class should be located based on suffix
switch (strtolower($suffix))
{
case 'model':
$folder = '/models/';
$filename = ($className);
break;
case 'library':
$folder = '/libraries/';
break;
case 'driver':
$folder = '/libraries/drivers/';
break;
}
//compose file name
$file = SERVER_ROOT . $folder . strtolower($filename) . '.php';
//fetch file
if (file_exists($file))
{
//get file
include_once($file);
}
else
{
//file does not exist!
die("File '$filename' containing class '$className' not found in
'$folder'.");
}
}
?>
post_controller.php
<?php
/**
* This file handles the retrieval and serving of posts posts
*/
class Posts_Controller
{
/**
* This template variable will hold the 'view' portion of our MVC for this
* controller
*/
public $template = 'posts';
/**
* This is the default function that will be called by router.php
*
* #param array $getVars the GET variables posted to index.php
*/
public function main(array $getVars)
{
//$b_controller =new Bottom_Bar_Controller;
//$b_controller->main($getVars);
$postsModel = new Posts_Model;
//get an post
$post = $postsModel->get_post($getVars['id']);
//create a new view and pass it our template
$header = new View_Model('header_template');
$bottom_bar = new View_Model('bottom_bar');
$view = new View_Model($this->template);
//assign post data to view
$view->assign('header', $header->render(FALSE));
$view->assign('bottom', $bottom_bar->render(FALSE));
$view->assign('title' , $post['title']);
$view->assign('content' , $post['content']);
$view->assign('date' , $post['date']);
$view->assign('by' , $post['added_by']);
$view->render();
}
}
posts_model.php
<?php
/**
* The Posts Model does the back-end heavy lifting for the Posts Controller
*/
class Posts_Model
{
/**
* Holds instance of database connection
*/
private $db;
public function __construct()
{
$this->db = new MysqlImproved_Driver;
}
/**
* Fetches article based on supplied name
*
* #param string $author
*
* #return array $article
*/
public function get_post($id)
{
//connect to database
$this->db->connect();
//sanitize data
$author = $this->db->escape($id);
//prepare query
$this->db->prepare
(
"
SELECT * FROM `posts`
WHERE
`id` = '$id'
LIMIT 1
;
"
);
//execute query
$this->db->query();
$article = $this->db->fetch('array');
return $article;
}
}
?>
view_model.php
<?php
/**
* Handles the view functionality of our MVC framework
*/
class View_Model
{
/**
* Holds variables assigned to template
*/
private $data = array();
/**
* Holds render status of view.
*/
private $render = FALSE;
/**
* Accept a template to load
*/
public function __construct($template)
{
//compose file name
$file = SERVER_ROOT . '/views/' . strtolower($template) . '.php';
if (file_exists($file))
{
/**
* trigger render to include file when this model is destroyed
* if we render it now, we wouldn't be able to assign variables
* to the view!
*/
$this->render = $file;
}
}
/**
* Receives assignments from controller and stores in local data array
*
* #param $variable
* #param $value
*/
public function assign($variable , $value)
{
$this->data[$variable] = $value;
}
/**
* Render the output directly to the page, or optionally, return the
* generated output to caller.
*
* #param $direct_output Set to any non-TRUE value to have the
* output returned rather than displayed directly.
*/
public function render($direct_output = TRUE)
{
// Turn output buffering on, capturing all output
if ($direct_output !== TRUE)
{
ob_start();
}
// Parse data variables into local variables
$data = $this->data;
// Get template
include($this->render);
// Get the contents of the buffer and return it
if ($direct_output !== TRUE)
{
return ob_get_clean();
}
}
public function __destruct()
{
}
}
posts.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link href="../style/style.css" rel="stylesheet" type="text/css" media="screen" />
<title> Posts (View)</title>
</head>
<body>
<div id="main">
<div class="container">
<?=$data['header'];?>
<div id="content">
<div class="content-background">
<h2> <?=$data['title'];?></h2>
<h4> <?=$data['date'];?> </h4>
<p><?=$data['content'];?></p>
</div>
</div>
</div>
</div>
</body>
</html>
Part of the problem is, that your tutorial has a pretty primitive interpretation of MVC-inspired design pattern (you actually cannot implement classical MVC in PHP, but there are patterns, that are based on it).
View is not just a template. Views are supposed to be class instances, which contain all presentation logic and deal with multiple templates. What you actually have there is a layout template which contains posts template.
// class \Application\View\Posts
public function render()
{
$layout = new Template( $this->defaultTemplateDirectory . 'layout.html');
$content = new Template( $this->defaultTemplateDirectory . 'posts.html' );
$layout->assign( 'content' , $content->render() );
return $layout->render();
}
Also, one of the things, that a view instance should do, is requesting information from the model layer.
Few materials that you might find useful:
Model-View-Confusion part 1: Why the model is accessed by the view in MVC
Simple PHP Template Engine
How should a model be structured in MVC?
And if you want to expand you knowledge in OOP, this post contains a list of recommended lectures and books.
There is no good solution in the MVC structure for that. You can find a solution in every framework. In CakePHP for example you can use an AppController, a controller which is always called for every requests. Sort of base controller.
But no, not very nice to do.
Most simple one is to ask the data directly from the view. That sounds weird but it is accepted in the MVC structure. So in your view you call $News->getLatestItems(10); and work with them.
I don't like it but it works.
If you have lots of widgets and blocks MVC alone might just not be the right structure. Then you could take a look at things like: http://techportal.inviqa.com/2010/02/22/scaling-web-applications-with-hmvc/ which is a derivate of MVC.
Another solution which you see more and more: Request it via AJAX calls. So just load them after the page has loaded. That way you also solve the issue since one MVC request then becomes multiple MVC requests.