Hi I've recently started yet another project and my boss is insisting that we use the MVC model, the problem being that due to many articles showing different ways to do this we havent managed to agree on what a proper MVC model should look like.
So here's my problem for this project (whether this is the correct way to do it or not) I am using the following baseline rules
Controller classes manage both getting the data from the model classes and passing the data to the view classes and retrieving the view and displaying it
Model classes managhe all database actions and return the data using mysql_fetch_assoc
View classes create the views using the data etc.
So my issue is with processing the information from mysql_fetch_assoc normally you would do something like this (assuming we have already run a query)
while ($row = mysql_fetch_assoc($result)) {
echo $row["username"];
}
but as I'm processing the results in the view class rather than the model how do I cycle through all of the results when I have already passed the assoc array to the view, currently I'm getting a problem where it keeps looping through the results until it hits a memory size error so for some reason it isn't able to figure out how many results it needs to cycle through
My current code snippets are below sorry for the bad explainations.
Controller
require_once 'admin_model.php';
require_once 'admin_view.php';
class admin_controller {
public $model;
public $view;
public function __construct() {
$this->model = new admin_model;
$this->view = new admin_view;
}
public function get_group_view() {
$in_model = $this->model->get_group_view();
$in_view = $this->view->get_group_view ($in_model);
echo $in_view;
}
Model
class admin_model {
public function get_group_view() {
$query = mysql_query("
SELECT
group_id,
group_name
FROM
user_groups
");
return mysql_fetch_assoc($query);
}
}
View
class admin_view {
public function get_group_view($group_data) {
while($group_data) {
$output .= $group_data['group_id'] . '###' . $group_data['group_name'] . '<hr />';
}
return $output;
}
}
Which currently returns the error:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 133693393 bytes)
So can someone please advise me on the best way to go through the results without moving 'mysql_fetch_assoc' function from the model class?
PS I know I'm probably doing MVC completely wrong but it works for us and we don't want to have to research and change our code yet again thanks.
You should not return the MySQL Result - you should do:
$return = array();
$query = mysql_query("SELECT group_id, group_name FROM user_groups");
while($row = mysql_fetch_assoc($query)) {
$return[] = $row;
}
mysql_free_result($row);
return $return;
And you should fix the $group_data bug per #Roman_S . The correct use, along with the above code is
public function get_group_view($group_data) {
$output = '';
foreach($group_data as $group) {
$output .= $group['group_id'] . '###' . $group['group_name'] . '<hr />';
}
return $output;
}
Finally you should migrate to MySQLi or PDO if possible.
You have en error here
while($group_data) {
$output .= $group_data['group_id'] . '###' . $group_data['group_name'] . '<hr />';
}
If $group_data is not empty - your loop will never end.
To give a suggestion on how to handle database control.
When using PDO for instance
$pdoInst = new PDO( .. );
and we have a method somewhere that validates every statement the $pdoInst produces
abstract class .. {
public static function validateStmt($stmt) {
if($stmt !== false) { .. }
// everything else you like, even error handling, log files, etc.
}
}
}
a prepared statement like the get_group_view method will look like the following
public function get_group_view {
$stmt = $pdoInst->prepare(" .. QUERY .. ");
// the return can be wrapped in a method to handle errors, etc, which can be done
// here or else where.
$stmt->execute() // returns true or false
return $stmt;
}
now for iteration
public function get_group_view($group_data) {
$output = "";
// validate the statement, can be done here or else where as said before
if($pdoInst::validateStmt($group_data)) {
// many ways how to iterate, foreach is just one.
foreach($group_data as $index => $group) {
$output .= $group['group_id'] . '###' . $group['group_name'] . '<hr />';
}
}
return $output;
}
The nicest thing about PDO is that you can extend the classes with custom ones. You can add functionality that adds more value to your Model.
Related
I'm trying to cover all my bases in the event my MYSQL database returns any errors (no rows, no connection, no table, etc...) when I'm making a query using CodeIgniter 3.
I have a helper function that returns the latitude and longitude based on a zip code provided. It will always only return a single row (granted the record exits). Here's my helper function as of now:
if (!function_exists('get_coordinates_from_zipcode')) {
//gets latitude and longitude coordinates from supplied zipcode. Returns array
function get_coordinates_from_zipcode($zipcode) {
$ci =& get_instance();
$ci->load->database();
$query = $ci->db->get_where('Geo', array('zip =' => $zipcode))->row_array();
if (!$query) {
return FALSE;
} else {
return $query;
}
}
//* Fields returned from geolocation database *//
/* -zip
-lat
-lng
// Returns false on error or no records
*/
}
And here is my View I'm using (passing $data['array'] array to it from my Controller):
<?php if ($array == FALSE || !$array) : ?>
<?php echo "No data returned"; ?>
<?php else : ?>
<?php echo $array['zip'] . ' is located at ' . $array['lat'] . ' and ' . $array['lng']; ?>
<?php endif; ?>
This works well if there are no rows, but I want to handle any other issues, such as more than one row (highly unlikely to happen), or if there's a problem connecting to the database or table.
I've tried this in my Helper
if ($ci->db->error()) {
return $ci->db->error(); //
} else {
return $query;
}
When I do this, and purposely use an invalid zip code to pass the error to the view, $ci->db->error() always returns array(2) { ["code"]=> int(0) ["message"]=> string(0) "" } and is empty. And of course I get errors that Undefined index: lat and Undefined index: lng
Should I be passing the $ci-db->error() array to the view and acting on it there?
I just want to make sure all my bases are covered. In my mind I should be handling errors in the Helper function but the error() always seems to be empty even when there's an error (such as no rows, or no db connectivity, or no table by that name.
I feel like
if (!$query) {
return FALSE;
} else {
return $query;
}
inside my helper function won't cover all problems I could potentially have connecting to the database.
Why don't you just do the following:
if (!function_exists('get_coordinates_from_zipcode')) {
//gets latitude and longitude coordinates from supplied zipcode. Returns array
function get_coordinates_from_zipcode($zipcode) {
$ci =& get_instance();
$ci->load->database();
if ($ci->db->conn_id === false) {
return false; // connection couldn't be established
}
$query = $ci->db->get_where('Geo', array('zip =' => $zipcode));
if ($query && $query->num_rows() == 1) {
return $query->row_array();
}
return false;
}
//* Fields returned from geolocation database *//
/* -zip
-lat
-lng
// Returns false on error or no records
*/
}
This way:
You test that query didn't return a FALSE result
You test that you are only getting 1 row
You make sure you have established a connection to the db (seems a bit overkill)
Please note: you should always check the value of num_rows() before attempting to access the result array/object. If there are no rows, then you will get undefined indexes when attempting to access the array.
i don't understand the purpose of your helper here - If you dont use a model and if you bypass the controller here why do you even use Codeigniter at first ?
Now your question
if its possible i would create a model where you handle all the errors and try to throw them via Exceptions
a possible approach
Model
class Geo_model extends CI_Model
{
public function __construct()
{
$this->load->database();
}
public function get_coordinates_from_zipcode($zipcode = false)
{
if (!$zipcode) throw new InvalidArgumentException('Zipcode should be set');
$query = $this->db
->select('*')
->from('Geo')
->where('zip', $zipcode)
->get();
$arrError = $this->db->error();
if (isset($arrError['message']) && !empty($arrError['message'])) throw new RuntimeException($arrError['message']);
if ($query->num_rows() != 1) throw new RuntimeException('Query - Number of rows should be 1');
return $query->row_array();
}
}
controller
class Geo extends CI_Controller
{
public function coordinatesfromzipcode($zipcode)
{
$this->load->model('Geo_model');
try
{
$row = $this->Geo_model->get_coordinates_from_zipcode($zipcode);
//load your coordinates view
}
catch (Excepetion $e)
{
//load an error view or something like that...
echo $e->getMessage();
}
}
}
So here is my case:
I have three functions performing some chemical reactions(synthesis1(), synthesis2() & synthesis3() ).
All of these functions will give an answer or a fail in the results.
They were originally separate scripts but are now in a class.
NB: the functions work fine by themselves, even in the class.
Below is my script to instantiate the class and start the functions.
My problem is that since i am running a reaction which fires all the functions;
i get one 1 correct answer and two fails or three fail at once.
What is the best way to handle the situation.
I want one correct answer and suppress the two fails or just show one fail in case of three fails(all fails). I don't expect three right answers.
P.s. All answers are strings.
<?php
// create an object for class name
$aaa = new synthesis();
$abc = new synthesis();
$abcd = new synthesis();
// call the functions in the class
$synthesis1 = $aaa->synthesis1();
$synthesis2 = $abc->synthesis2();
$synthesis3 = $abcd->synthesis3();
// call the if functions
$searches = array($synthesis1, $synthesis2, $synthesis3);
foreach($searches as $search) {
if ($aaa->synthesis1($search)){
echo 'Match found: ' . $search;
break;
}
elseif ($abc->synthesis2($search)){
echo 'Match found: ' . $search;
break;
}
elseif ($abcd->synthesis3($search)){
echo 'Match found: ' . $search;
break;
}
else{ echo"Please try again or try another reaction";}
}
?>
I don't know why you need to instantiate three different objects if you have three individually named methods.
I would think you might want to add a method to your class to simply run all synthesis methods all at once and return the result. So something like:
class synthesis {
protected $synthesis_methods = array(
'synthesis1',
'synthesis2',
'synthesis3',
// add more methods here if needed
}
public function synthesis1() {
// your method logic here
}
public function synthesis2() {
// your method logic here
}
public function synthesis2() {
// your method logic here
}
public function synthesize_all() {
$result = false;
$i = 0;
while(false === $result && $i < count($this->synthesis_methods)) {
$result = call_user_func(array($this, $this->synthesis_methods[$i]));
$i++;
}
return $result;
}
}
You would then only instantiate a single object. Usage would be:
$synth_obj = new synthesis();
var_dump($synth_obj->synthesize_all());
An easy way to handle this is to use OR logic:
if($aaa->synthesis1($search) or $abc->synthesis2($search) or $abcd->synthesis3($search))
{
echo "Match Found: $search";
break;
}
else
{
echo "Please try again or try another reaction.";
}
I am trying to retrieve a list of items from a mySQL db and insert them as a list in a select object on a webpage. The following is the bit of code that isnt working.
In the first line, I am trying to retrieve a JSON object from a public function called getBrands() in a singleton object I have created called DatabaseInterface.
The second line is then attempting to turn that JSON object into a php array.
Finally, I am running a loop which can option each item in between tags for the webpage.
Where am I going wrong?
<?php
var $brandArrayJSON = DatabaseInterface::getBrands();
$brandArray = JSON_decode($brandArrayJSON);
for ($loop=0; $loop < sizeof($brandArray); $loop++) {
echo "<option>$brandArray[$loop]</option>";
}
?>
EDIT: In case it helps, here is my DatabaseInterface singleton. I have included this file at the top of my php file
class databaseInterface {
private static $_instance;
// Private constructor prevents instantiation
private function __construct() {
}
public static function getInstance() {
if (!self::$_instance) {
self::$_instance = mysqli_connect(self::databaseHost, self::databaseUsername, self::databasePassword, self::databaseName);
if (mysqli_connect_errno(self::$_instance)) {
throw new Exception("Failed to connect to MySQL:" . mysqli_connect_error());
}
}
return self::$_instance;
}
public function getBrands() {
try {
$con = DatabaseInterface::getInstance();
} catch (Exception $e) {
// Handle exception
echo $e->getMessage();
}
$query = "SELECT psBrandName from brands";
$result = mysqli_query($con, $query) or die ("Couldn't execute query. ".mysqli_error($con));
$resultArray[] = array();
while ($row = mysqli_fetch_assoc($result)) {
extract($row);
$resultArray[] = $psBrandName;
}
return json_Encode($resultArray);
}
There is nothing "wrong" with the code, in that it should work (provided nothing is broken on the query-side). However, there are several things that should be improved.
First, basically what the getBrands() method is doing is equivalent to this:
$brandArray = json_encode(array('test','test2','test3'));
echo $brandArray; // returns ["test","test2","test3"]
Now, when you decode that you get the same thing you originally put in (an array):
$brandArray = json_decode('["test","test2","test3"]');
var_dump($brandArray); // Will dump an array
Since this is an array (not a PHP object), you can just use a foreach.
foreach($brandArray as $option) {
echo '<option>', $option, '</option>';
}
If you're worried about it being an object in some instances (maybe you had a non-array JS object which would be mostly the equivalent to a PHP associative array), you could cast the json_decode result into an array.
$brandArray = (array)$brandArray;
Now, in your getBrands() method, I would highly recommend just using $row['psBrandName'] instead of cluttering things up with extract, unless you have a really good reason to do this.
i am looking for a function to fetch wordpress category hierarchy from wordpress tables (wp_terms, wp_term_relationships, wp_term_taxonomy) WITHOUT using wordPress functions / templates.
i am basically looking to fetch categories (parent/child) relationship and then I want to insert them into my own CMS table. For this i must know "What Category Comes under What? (with all the sub-sub (multiple) directories)"
So far I made this:
$sql="SELECT a.term_id,a.description,a.parent,a.count,b.name,b.slug
FROM wp_term_taxonomy a INNER JOIN wp_terms b WHERE a.term_id=b.term_id
AND a.taxonomy='category';
";
$result = mysql_query($sql);
while ($row = mysql_fetch_assoc($result)) {
echo($row['name'].'<br>');
}
exit;
This function displays all the categories but NOT the hierarchy and i am NOT able to get the Child Parent thing..
Can anyone help me with this please?
Regards
It's not that hard. First I'd wrap that recursive stuff by letting it behave like a RecursiveIterator:
class RecursiveCategoryIterator implements RecursiveIterator {
const ID_FIELD = 'term_id';
const PARENT_FIELD = 'parent';
private $_data;
private $_root;
private $_position = 0;
public function __construct(array $data, $root_id = 0) {
$this->_data = $data;
$this->_root = $root_id;
}
public function valid() {
return isset($this->_data[$this->_root][$this->_position]);
}
public function hasChildren() {
$subid = $this->_data[$this->_root][$this->_position][self::ID_FIELD];
return isset($this->_data[$subid])
&& is_array($this->_data[$subid]);
}
public function next() {
$this->_position++;
}
public function current() {
return $this->_data[$this->_root][$this->_position];
}
public function getChildren() {
return new self($this->_data,
$this->_data[$this->_root][$this->_position][self::ID_FIELD]);
}
public function rewind() {
$this->_position = 0;
}
public function key() {
return $this->_position;
}
public static function createFromResult($result) {
$menu_array = array();
while($row = mysql_fetch_assoc($result)) {
$menu_array[$row[self::PARENT_FIELD]][] = $row;
}
return new self($menu_array);
}
}
Now why would I do that? First, because you can re-use id for displaying the tree, or do other stuff with it like import it in your own table. Second, if you have to test your code, you can just put in some other RecursiveIterator as a mock (for example a RecursiveArrayIterator).
Now the second part, the actual import of the word-press data:
// your original query
$sql="SELECT a.term_id,a.description,a.parent,a.count,b.name,b.slug
FROM wp_term_taxonomy a INNER JOIN wp_terms b WHERE a.term_id=b.term_id
AND a.taxonomy='category';
";
$result = mysql_query($sql, $dbh);
// always test for failure
if($result === false) {
die("query failed: ". mysql_error());
}
// create the iterator from the result set
$wpterms = RecursiveCategoryIterator::createFromResult($result);
// and import it.
insert_it($wpterms, 0);
// the function which does all the dirty work.
function insert_it($iterator, $parent_id = 0) {
foreach($iterator as $row) {
// insert the row, just edit the query, and don't forget
// to escape the values. if you have an insert function,
// use it by all means
$qry = 'INSERT INTO my_table (myparent, myname, ...)'
. ' VALUES (\'' . mysql_real_escape_string($parent_id)
. '\', \'' . mysql_real_escape_string($row['name']) . '\', ....)';
$status = mysql_query($qry);
if($status === false) {
// insert failed - rollback and abort
die("hard: " . mysql_error());
}
// you need to pass the id of the new row
// so the "child rows" have their respective parent
$cid = mysql_insert_id();
// insert the children too
if($iterator->hasChildren()) {
insert_it($iterator->getChildren(), $cid);
}
}
}
If you want to migrate your data to a different (and even custom-built) CMS, you should use Export, which will generate a WXR (XML) file that is very easy to parse and import into third-party systems. It includes all post types, taxonomies, meta data, attachments and everything else.
Working directly with the database is a pain, but if you will, it'll be easier to work on the WordPress side write the data into your other tables, rather than trying to read the data from somewhere else.
I have users' table users, where I store information like post_count and so on. I want to have ~50 badges and it is going to be even more than that in future.
So, I want to have a page where member of website could go and take the badge, not automatically give him it like in SO. And after he clicks a button called smth like "Take 'Made 10 posts' badge" the system checks if he has posted 10 posts and doesn't have this badge already, and if it's ok, give him the badge and insert into the new table the badge's id and user_id that member couldn't take it twice.
But I have so many badges, so do I really need to put so many if's to check for all badges? What would be your suggestion on this? How can I make it more optimal if it's even possible?
Thank you.
optimal would be IMHO the the following:
have an object for the user with functions that return user specific attributes/metrics that you initialise with the proper user id (you probably wanna make this a singleton/static for some elements...):
<?
class User {
public function initUser($id) {
/* initialise the user. maby load all metrics now, or if they
are intensive on demand when the functions are called.
you can cache them in a class variable*/
}
public function getPostCount() {
// return number of posts
}
public function getRegisterDate() {
// return register date
}
public function getNumberOfLogins() {
// return the number of logins the user has made over time
}
}
?>
have a badge object that is initialised with an id/key and loads dependencies from your database:
<?
class Badge {
protected $dependencies = array();
public function initBadge($id) {
$this->loadDependencies($id);
}
protected function loadDependencies() {
// load data from mysql and store it into dependencies like so:
$dependencies = array(array(
'value' => 300,
'type' => 'PostCount',
'compare => 'greater',
),...);
$this->dependencies = $dependencies;
}
public function getDependencies() {
return $this->dependencies;
}
}
?>
then you could have a class that controls the awarding of batches (you can also do it inside user...)
and checks dependencies and prints failed dependencies etc...
<?
class BadgeAwarder {
protected $badge = null;
protected $user = null;
public function awardBadge($userid,$badge) {
if(is_null($this->badge)) {
$this->badge = new Badge; // or something else for strange freaky badges, passed by $badge
}
$this->badge->initBadge($badge);
if(is_null($this->user)) {
$this->user = new User;
$this->user->initUser($userid);
}
$allowed = $this->checkDependencies();
if($allowed === true) {
// grant badge, print congratulations
} else if(is_array($failed)) {
// sorry, you failed tu full fill thef ollowing dependencies: print_r($failed);
} else {
echo "error?";
}
}
protected function checkDependencies() {
$failed = array();
foreach($this->badge->getDependencies() as $depdency) {
$value = call_user_func(array($this->badge, 'get'.$depdency['type']));
if(!$this->compare($value,$depdency['value'],$dependency['compare'])) {
$failed[] = $dependency;
}
}
if(count($failed) > 0) {
return $failed;
} else {
return true;
}
}
protected function compare($val1,$val2,$operator) {
if($operator == 'greater') {
return ($val1 > $val2);
}
}
}
?>
you can extend to this class if you have very custom batches that require weird calculations.
hope i brought you on the right track.
untested andp robably full of syntax errors.
welcome to the world of object oriented programming. still wanna do this?
Maybe throw the information into a table and check against that? If it's based on the number of posts, have fields for badge_name and post_count and check that way?