i am looking at building my first real class, i've played around with bits and bobs but its time to try it for real :)
what i am trying to do is have a form class which handles all my form submissions, checks the data entered and returns with either an error message or success message.
so here one of my forms, (i have 5 of these on 1 page)
<form action="include/mform.php" method="post" name="business_listing">
<input name="biz_name" type="text" value="Business or Place Name" />
<select name="biz_department">
<option value="">Business Sector</option>
<?php
$query = $user->database->query("SELECT * FROM tbl_sectors");
while($row=$user->database->fetchArray($query))
{
$id = $row['sectorID'];
$dept = $row['sectorName'];
echo "<option value='$id'>$dept</option>";
}?>
</select>
<input name="biz_address1" type="text" value="Street Name" />
<select name="job_location">
<option value="">Location</option>
<?php
$query = $user->database->query("SELECT * FROM tbl_places");
while($row=$user->database->fetchArray($query))
{
$id = $row['placeID'];
$dept = $row['placeName'];
echo "<option value='$id'>$dept</option>";
}?>
</select>
<input name="biz_phone" type="text" value="Contact Number" />
<input name="businessSubmit" type="submit" value="Submit" />
</form>
</div>
each of the form's action is set to include/mform.php which contains my class. within the class one of the first things it does is check to see which form was submitted and the idea is then to check the data that has been submitted and do whats necessary with it
my problem is that once my class knows which form was submitted what would be the best way to check the submitted data? should i create varibles within the function to get all the post data and take it from there or should i pass those in the actual function as parameters? , or does it matter?
here is my current class file which is a little bare atm
class Mform
{
private $values = array(); //Holds submitted form field values
private $errors = array(); //Holds submitted form error messages
private $num_errors; //The number of errors in submitted form
public function __construct()
{
if(isset($_POST['businessSubmit']))
{
$this->chkBusiness();
}
if(isset($_POST['jobSubmit']))
{
$this->chkJob();
}
if(isset($_POST['accommodationSubmit']))
{
$this->chkAccommodation();
}
if(isset($_POST['tradeSubmit']))
{
$this->chkTrade();
}
if(isset($_POST['eventSubmit']))
{
$this->chkEvent();
}
}
public function chkBusiness()
{
$field = "business";
}
public function chkJob()
{
return "job";
}
public function chkAccommodation()
{
return "accommodation";
}
public function chkTrade()
{
return "trade";
}
public function chkEvent()
{
return "event";
}
/**
* setValue - Records the value typed into the given
* form field by the user.
*/
public function setValue($field, $value)
{
$this->values[$field] = $value;
}
/**
* setError - Records new form error given the form
* field name and the error message attached to it.
*/
public function setError($field, $errmsg)
{
$this->errors[$field] = $errmsg;
$this->num_errors = count($this->errors);
}
/**
* value - Returns the value attached to the given
* field, if none exists, the empty string is returned.
*/
public function value($field)
{
if(array_key_exists($field,$this->values))
{
return htmlspecialchars(stripslashes($this->values[$field]));
}
else
{
return "";
}
}
/**
* error - Returns the error message attached to the
* given field, if none exists, the empty string is returned.
*/
public function error($field)
{
if(array_key_exists($field,$this->errors))
{
return "<font size=\"2\" color=\"#ff0000\">".$this->errors[$field]."</font>";
}
else
{
return "";
}
}
/* getErrorArray - Returns the array of error messages */
public function getErrorArray()
{
return $this->errors;
}
}
/* Initialize mform */
$mform = new Mform();
most of the individual functions just have return "word" as placeholder so i dont forget to do that function at a later date.
this is what i was thinking of doing for each of the individual form functions
public function chkBusiness()
{
$field = "business";
$name = $_POST['biz_name'];// all need to be sanitized!!
$dept = $_POST['biz_dept'];
$address = $_POST['biz_address'];
$location = $_POST['biz_location'];
$phone = $_POST['biz_phone'];
//start checking the input
if(!$name || strlen($name = trim($name)) == 0)
{
$this->mform->setError($field, "* Name not entered");
}
...
...
}
any help would be appreciated
Luke
I generate a class for every table in my database; life is too short to actually write that functionality for every database table!
Each field has it's own object containing it's value, type, maxlength etc. and the fields are also added to an array so I can do really cool things with them.
Each of the table classes extend a much bigger class that allows me to insert, update, delete and display as tables and forms... I can override any functions that are non-standard although that is very rare.
The performance overhead is about 0.001ms for iterating through about 25000 good sized records.
As an example, my code to produce a datatable of document records looks like this:
//Create object
$objDocument = new cls__CMS_Document();
//Set the fields you want to display in the datatable
$objDocument->objID->Active(true);
$objDocument->objDocument_Name->Active(true);
$objDocument->objAuthorID->Active(true);
$objDocument->objVersion->Active(true);
$objDocument->objDate_Created->Active(true);
$objDocument->objDate_Last_Edited->Active(true);
//Include a hyperlink from the ID field
$objDocument->objID->HyperLink('/drilldown.php');
$objDocument->objID->Post(true);
//Pass a field through a formatting function
$objDocument->objAuthorID->OutputFunction('getAuthorFromID');
$result .= $objDocument->displayTable($sqlConditions);
unset ($objDocument);
The best bit: every step of the way, the auto-complete works:) So you type $objDocument-> and all the methods and properties pop up including all the field objects so you never misspell them.
If I get enough interest (votes), I'll make the whole thing available on the net.
That should be food for thought though when making your own.
Related
i am beginner php programmer, iv been trying to create a small program that takes input from a forum and then after submission i want it to be printed on the screen. simple and easy i thought, iv been trying and suspiciously it seems to work fine for 1 text field, when i added the remaining 2 text fields called [fam][user] my code stops returning the content to the screen. also i started to recieve an error of an unindex array, therefore i had to use isset to counter this problem, and also, why does my code call the destructor although i never implicitly set my destructor. i dont know how to ask these questions because the errors arent consistent.
code doesnt print my [name][fam][user]
code prints [name] when everything about [fam][user] are ommited from the code.
-code sometimes called the destructor
-code doesnt clear html previous input(e.g, when working with the one text field, lets say i input the [name] john, and click submit it
displays submit, then,i refresh the page, and the name john is still
displayed, why doesnt the destructor clear the memory of name from my
submission.
<form class="nameform" action="book.php" method="post">
<input type="text" name="Name" value="1">
<input type="text" name="Fam" value="2">
<input type="text" name="User" value="3">
<input type="button" name="submit" value="Submit">
</form>
private $name; private $familyName; private $userName;
function __construct($names,$familyNames,$userNames)
{
$this->name = $names;
$this->familyName = $familyNames;
$this->userName = $userNames;
}
function getName()
{
return $this->name;
}
function getFamilyName()
{
return $this->familyName;
}
function getUserName()
{
return $this->userName;
}
public function __destruct()
{
echo "destroyed again";
$this->name;
$this->familyName;
$this->userName;
}
}
if(!isset( $_POST["Name"])||!isset($_POST["Fam"])||!isset($_POST["User"]))
{
echo "Please fill in the data";
} else {
$p1 = new Person($_POST["Name"],$_POST["Fam"],$_POST["User"]);
print $p1->getName();
print $p1->getFamilyName();
print $p1->getUserName();
print_r($_POST);
}
// $n = $_POST["Name"];
// $f = $_POST["Fam"];
// $u = $_POST["User"];
// $p1 = new Person($_POST["Name"],$_POST["Fam"],$_POST["User"]);
?>
code doesnt print my [name][fam][user]
You never echo them out of the destuctor
public function __destruct()
{
echo "destroyed again";
$this->name; //<---- does nothing
$this->familyName;
$this->userName;
}
So I am not sure what this is supposed to do. You have them down at the bottom
print $p1->getName();
print $p1->getFamilyName();
print $p1->getUserName();
But the only thing you'll get from the destruct method is
"destroyed again"
And you will only see that if everything in the form is set. Which it always is when the form is submitted, because type text is always submitted with its form.
Which brings me to this, you should be checking empty instead of isset there
if ('POST' === $_SERVER['REQUEST_METHOD']) { //check if POST
if(empty($_POST["Name"])||empty($_POST["Fam"])||empty($_POST["User"])){
echo "Please fill in the data";
} else {
$p1 = new Person($_POST["Name"],$_POST["Fam"],$_POST["User"]);
print $p1->getName();
print $p1->getFamilyName();
print $p1->getUserName();
print_r($_POST);
}
}
Note that anything falsy will be empty, false, [], '', 0, '0', null etc.
I don't know if this solves all of you problems, but these things could produce some of the behaviour you are experiencing.
Another more advance way to check these is like this:
if ('POST' === $_SERVER['REQUEST_METHOD']) { //check if POST
$post = array_filter( $_POST, function($item){
return strlen($item); //any thing of a length of 0 is removed
});
if(count($post) != count($_POST)){
foreach(array_diff_key( $_POST, $post) as $missing=>$empty) {
echo "Please fill in $missing\n";
}
}else{
$p1 = new Person($_POST["Name"],$_POST["Fam"],$_POST["User"]);
print $p1->getName();
print $p1->getFamilyName();
print $p1->getUserName();
print_r($_POST);
}
}
Output
Please fill in Name
Please fill in Fam
You can test it online Here
Cheers!
I'm getting a number of errors, related to profile_address not being defined. Most of this current code came from a working update for 1 value, I've since tried to add in updating a second value and I would like to expand it to update another 10 values.
I think the issue is the variables are not being parsed correctly in my application.
I initially tried to add it to an array as seen in the model however this hasn't worked.
I think this is a fairly trivial problem and I'm sure it's a simple solution that I haven't thought of yet.
View:
<form action="<?php echo Config::get('URL'); ?>login/editUserProfile_action" method="post">
<label for="comment">Name</label>
<textarea class="form-control" rows="3" name="profile_name"></textarea>
<br>
<label for="comment">Address</label>
<textarea class="form-control" rows="3" name="profile_address"></textarea>
</form>
Controller:
public function editUserProfile()
{
Auth::checkAuthentication();
$this->View->render('login/editUserProfile');
}
/**
* Edit user profile (perform the real action after form has been submitted)
* Auth::checkAuthentication() makes sure that only logged in users can use this action and see this page
*/
// make this POST
public function editUserProfile_action()
{
Auth::checkAuthentication();
UserModel::editUserProfile(Request::post('profile_name', 'profile_address'));
Redirect::to('login/editUserProfile');
}
Model:
public static function editUserProfile($profile_name, $profile_address)
{
// write to database, if successful ...
if (UserModel::saveUserProfile(Session::get('user_id'), $profile_name, $profile_address)) {
Session::set(array('profile_name', $profile_name, 'profile_address', $profile_address));
Session::add('feedback_positive', Text::get('FEEDBACK_EMAIL_CHANGE_SUCCESSFUL'));
return true;
}
Session::add('feedback_negative', Text::get('FEEDBACK_UNKNOWN_ERROR'));
return false;
}
/**
* Writes new data to database
*
* #param $user_id int user id
*
* #return bool
*/
public static function saveUserProfile($user_id, $profile_name, $profile_address)
{
$database = DatabaseFactory::getFactory()->getConnection();
$query = $database->prepare("UPDATE users SET profile_name = :profile_name, profile_address = :profile_address WHERE user_id = :user_id LIMIT 1");
$query->execute(array(':profile_name' => $profile_name, ':profile_address' => $profile_address, ':user_id' => $user_id));
$count = $query->rowCount();
if ($count == 1) {
return true;
}
return false;
}
The problem is in the arrays, I have fixed the issue by doing the following:
public function editUserProfile_action()
{
Auth::checkAuthentication();
UserModel::editUserProfile(Request::post('profile_name'),
Request::post('profile_address'));
Redirect::to('login/editUserProfile');
}
This was fixed by doing the same thing in the model.
Just started with CI last week and got this issue. What to put inside the matches function if I'm passing the form data as an array?
I use array in the html form to locate all input fields inside single array in case I want to pass user generated input such as multiple phone numbers or emails. So everything is placed in array such as this:
<div>
<label for="password">Password</label>
<input type="password" name="input[password]" id="password" value="<?php echo set_value("input[password]")?>"/>
</div>
<div>
<label for="password">Confirm Password</label>
<input type="password" name="input[conf_password]" id="conf_password" value="<?php echo set_value("input[conf_password]")?>"/>
</div>
Notice the *name="input[password]"*
The validation works like a charm for all except when I use the function matches:
$this->form_validation->set_rules("input[password]", "Password", 'required|matches[input[conf_password]]');
$this->form_validation->set_rules("input[conf_password]", "Confirm Password", 'required');
matches[input[conf_password]]
This will not work because after I checked the Form_Validation.php I found out that matches will take whatever string I put between the square brackets of matches and tries to fetch the value from $_POST directly.
CI codes:
/**
* Match one field to another
*
* #access public
* #param string
* #param field
* #return bool
*/
public function matches($str, $field)
{
if ( ! isset($_POST[$field]))
{
return FALSE;
}
$field = $_POST[$field];
return ($str !== $field) ? FALSE : TRUE;
}
So by right there would be no such thing as $_POST[input[conf_password]].
I'm aware that I can solve this by using
custom validation function
compare directly $_POST["input"]["password"] === $_POST["input"]["conf_password"]
I'm not sure what I'm missing since everything in CI related to forms is working nicely with arrays, why wouldn't this function?
Yes, i have a similar problem and there is no way CI core input can solve that, I solved mine not by creating a custom callback function it clutters the controller often, but by extending the Form_validation class MY_Form_validation
then i created a function which i called matches_array then used as
matches_array[inputkeyname---inputkeyvalue]
os you would write yours as
$this>form_validation>set_rules("input[password]","Password",'required|matches_array[input---conf_password]');
Here is the function as i remember it.
public function matches_array($str, $field)
{
$field = explode('---',$field);
if ( ! isset($theField = $_POST [$field[0] ][ $field[1] ]))
{
return FALSE;
}
return ($str !== $theField) ? FALSE : TRUE;
}
EDIT
Put it on your app/libraries and name it MY_Form_validation, MY_ is what you defined in your config. anything you put in here will be automatically added to the rules.
class MY_Form_validation extends CI_Form_validation
{
public function __construct($rules = array())
{
parent::__construct($rules);
$this->CI->lang->load('MY_form_validation');
}
//your custom functions
}
You can Edit MY_Form_validation
public function matches($str, $field)
{
return isset($this->_field_data[$field], $this->_field_data[$field]['postdata'])
? ($str === $this->_field_data[$field]['postdata'])
: FALSE;
}
I know this sounds very much like "Is there a PHP framework that does my entire job for me?" But bear with me.
Say I want to create a signup form for a camp, and I have a simple data structure in mind --
Person
First name
Last name
Address
Address
Line 1
Line 2
Suburb
Town
etc
Things I have seen that do part but not all of what I want:
PHP form libraries like jFormer and ValidForm (not to mention all the big frameworks): these things let you define the form you want to make using a little bit of PHP -- so you'd say "add text field, add textarea", etc -- but they don't let the user edit the form data structure, nor do they automatically save into a data structure. They're more useful for developers.
Front-end form creators like foxyform, jotform: they let the user edit the form but the backend needs to be done in some other way, and it's not linked up.
Then there's Wordpress Pods CMS, which is almost exactly what I want -- but without the wordpress part.
Ideally, I would like one of two things:
1) A microframework where you define your data schema in some reasonably simple way, like say Json or Yaml -- your basic
Person
First name: Text
Last name: Text
Address: has_one Address
Address
... and so on
And it would take that and create the form you needed and maybe even create the database schema and so forth. You could then get hold of the data objects to iterate over in your code elsewhere (I'm not crazy enough to try and automate that as well... Or maybe I am, but certainly it feels outside the scope of this particular encapsulation).
OR
2) The above, plus a little editor for editing the data schema.
Create data type:
Name: [Person]
Fields:
First name: [Text field]
[+ Add field]
I have had a good look around and haven't found anything that's small and standalone and does just this. Pods CMS is almost exactly what I want, but smaller and cleaner and not tied to Wordpress.
Does such a thing exist? If not -- and I'm straying onto opinion here but I'll take a chance -- does it seem like such a thing should exist? Wouldn't it be nice to just be able to drop such a thing into any application, and either write the schema yourself or allow the user to edit it? It doesn't seem so very difficult, and it would be usable in so many contexts.
I am not quite sure such a detail of personalization exists but let's give it a try:
First you should create your classes:
class Person{
private $first_name;
private $last_name;
private $adress;
public function __construct($data=array(), adress $adress=null){
foreach ($data as $cle => $valeur)
$this->$cle=$valeur;
$this->adress=$adress;
}
public function __get($property){
return $this->$property;
}
public function __set($property,$value)
{
$this->$property=$value;
}
public function all_object_properties() {
return get_object_vars($this);
}
public function all_class_properties(){
return get_class_vars(__CLASS__);
}
}
class Adress{
private $id;
private $line_1;
private $line_2;
private $suburb;
public function __construct($data=array()){
foreach ($data as $cle => $valeur)
$this->$cle=$valeur;
}
public function __get($property){
return $this->$property;
}
public function __set($property,$value)
{
$this->$property=$value;
}
public function all_object_properties() {
return get_object_vars($this);
}
public function all_class_properties(){
return get_class_vars(__CLASS__);
}
public function fill($id){
$connexion=db_connect();
$req=$connexion->prepare("SELECT * FROM adresses WHERE id=:id");
$req->execute(array('id'=>$id));
while ($row = $req->fetch(PDO::FETCH_ASSOC)){
foreach ($row as $cle => $valeur)
$this->$cle=$valeur;
}
return $this;
}
public function save(){
$connexion=db_connect();
$sql1="INSERT INTO adresses (";
$sql2=" VALUES (";
$vars=$this->all_properties();
foreach($vars as $var=>$value){
$sql1.=$var.", ";
$sql2.=":".$var.", ";
}
$sql1=substr($sql1,0,-2).")";
$sql2=substr($sql2,0,-2).")";
$sql=$sql1.$sql2;
$query=$connexion->prepare($sql);
$query->execute($vars);
if($query){
$this->id=$connexion->lastInsertId();
return true;
}else{
return false;
}
}
public function update(){
$connexion=db_connect();
$sql1="UPDATE adresses SET ";
$vars=$this->all_properties();
foreach($vars as $var=>$value){
$sql1.=$var."=".":".$var.", ";
}
$sql1=substr($sql1,0,-2)." WHERE id=:id";
$query=$connexion->prepare($sql1);
$query->execute($vars);
if($query){
return true;
}else{
return false;
}
}
}
Then you want the ability to create a form based on a file like JSON or even XML:
<forms>
<form>
<host>name_of_my_page_where_my_form_is_displayed.php</host>
<method>post</method>
<target>#</method>
<input>
<type>text</type>
<name>first_name</type>
<class>Person</class>
</input>
<input>
<type>text</type>
<name>last_name</type>
<class>Person</class>
</input>
<input>
<type>text</type>
<name>line_1</type>
<class>Adress</class>
</input>
<input>
<type>text</type>
<name>suburb</type>
<class>Adress</class>
</input>
</form>
</forms>
Here you need a function to parse and create the form:
function display_form($xml_file,$page_to_display){
$content=null;
$values=array();
$dom = new DOMDocument;
$dom->load($xml_file);
$forms=$dom->getElementsByTagName("form");
foreach($forms as $form){
$urls = $form->getElementsByTagName( "host" );
$url = $urls->item(0)->nodeValue;
if($url==htmlspecialchars($page_to_display)){
$tp_method = $form->getElementsByTagName( "method" );
$method = $tp_method->item(0)->nodeValue;
$tp_target = $form->getElementsByTagName( "target" );
$target = $tp_target->item(0)->nodeValue;
$content="<form action=".$target." method=".$method."><br/>";
$inputs=$dom->getElementsByTagName("input");
foreach($inputs as $input){
$tp = $form->getElementsByTagName( "type" );
$type = $tp->item(0)->nodeValue;
$tp = $form->getElementsByTagName( "name" );
$name = $tp->item(0)->nodeValue;
$tp = $form->getElementsByTagName( "class" );
$class = $tp->item(0)->nodeValue;
$content.="<input type=".$type." name=".$name."/><br/>";
$values[]=array("class"=>$class,"name"=>$name);
}
$content.="<input type='submit' value='Submit'/>";
}
}
return array("content"=>$content,"values"=>$values);
}
Finally in your page where the form is displayed:
<?php
$array=display_form("forms.xml",$_SERVER['REQUEST_URI']);
$content=$array['content'];
$values=$array['values'];
$classes=array();
$names=array();
foreach($values as $tab){
$class=$tab['class'];
$name=$tab['name'];
$classes[]=(!in_array($class,$classes)) ? $class : null;
$names[]=$name;
${$class}[]=$name;
}
$form_submitted=true;
foreach($names as $name){
if(!isset($_POST[$name]))
$form_submitted=false;
}
if($form_submitted){
foreach($classes as $name_class){
$var= new $name_class;
foreach(${$name_class} as $value){
$var->__set($value,$_POST[$value]);
}
$var->save();
}
}
echo $content;
In addition, you may want to create a function to fill your xml.
In each classes you can use functions fill() to retrieve data for an object in the database based on their ID, save() to create a new insert in the database, or update() to update an already existent row based on the ID.
There is a lot of improvement which can be done, but the general idea is here.
Feel free to improve it and make yours!
I'm finishing up a small contact form and had a question about providing default values for $_POST. The reason I'm asking about default values is because within my form I have fields like this:
<input type="text" name="fullname" value="<?php echo $_POST['fullname']; ?>" />
Clearly I would like to retain the submitted value if I do not permit the data to clear. This raises errors when the page is first loaded, since there is no value for $_POST['fullname'].
To my question: is there anything I should be concerend about providing default values to the $_POST array like I'm doing in the next code-sample:
$_POST += array(
'fullname' = '',
);
If $_POST['fullname'] already exists, it will be retained - if it doesn't, it will be created within the array. This way, upon loading the form, blank values will be presented in the input fields.
Don't worry, all
I sanitize my data
Thank you for the help
Even if you are doing so, put that data in your container, do not modify superglobals. Create class that'll contain your data - then you'll have the interface do sanitize, manipulate and get it te proper way. Import data from $_POST and then validate, if all necessary values are in.
As for code:
<?php
class PostData
{
private $data;
public function __construct(array $data)
{
$this->data = is_array($data)
? $data
: array();
}
public function set($key, $value)
{
$this->data[$key] = $value;
}
public function get($key, $default, $escaping)
{
if(isset($this->data[$key]))
{
switch($escaping)
{
case 'htmlspecialchars':
{
return htmlspecialchars($this->data[$key]);
break;
}
case 'mysql_real_escape_string':
{
return mysql_real_escape_string($this->data[$key]);
break;
}
// and so on, your invention goes here
default:
{
return $this->data[$key];
}
}
}
else
{
return $default;
}
}
}
$postData = new PostData($_POST);
Create function:
function displayValue($field) {
if(isset($_POST[$field])) {
echo 'value="' . htmlentities($_POST[$field]) . '"';
}
}
And then use like:
<input type="text" name="fullname" <?php displayValue('fullname'); ?> />
You can also do it like this:
<?php echo empty($_POST['fullname']) ? null : $_POST['fullname']; ?>