I would really appreciate any feedback on the back-end of my mobile application. I tried to keep an MVC pattern, where I considered the app to be my view. Perhaps an REST framework would have been good to use, but for now I am not attempting cross domain requests.
Controller
The Group Controller, that receives JSON requests and mediates all activities involved in managing a group.
A couple notes:
I have not yet added a validation step to incoming data. I plan on creating a validation class to help me with that.
I was planing on putting said validation in the filter method.
I still need to add some methods for
UPDATE
andDELETE
ing groups, but this should be enough to get a feel for it.
<?php
require_once("../model/group_model.php");
require_once("../helper_validation.php");
header('Content-type: application/json');
//controller
session_start();
$group_array = array('msg' => 'Oops, we messed up. Try again soon.',
'success' => 0,
'group_action' => -1,
'session_logged in' => $_SESSION['logged_in']);
if (isset($_POST['group_action']) && ($_SESSION['logged_in'] === 1) && isset($_SESSION['user_id'])){
capture();
} else{
echo die(json_encode($group_array));
}
function capture() {
$group_array['group_action'] = (int)$_POST['group_action'];
direct($group_array['group_action']);
}
//directs action depending on registration state
function direct($i) {
switch ($i) {
case 0:
$new_group = new create_group;
$new_group->create_new_group();
break;
case 1:
$search = new search_group;
$search->initial_search_group();
break;
case 2:
$join = new join_group;
$join->start_join_group($join->join_array);
break;
case 3:
$search = new search_group;
$search->my_search_group();
break;
case 4:
$join = new join_group;
$join->start_unjoin_group($join->join_array);
break;
case 5:
$search = new search_group;
$search->show_one();
break;
default:
echo die(json_encode($group_array));
}
}
class create_group {
public $create_group_array = array();
public function __construct() {
$this->create_group_array['group_name'] = $this->filter($_POST['group_name']);
$this->create_group_array['description'] = $this->filter($_POST['description']);
$this->create_group_array['location_name'] = $this->filter($_POST['location']);
$this->create_group_array['privacy'] = (int)$this->filter($_POST['privacy']);
$this->create_group_array['lat'] = $this->filter($_POST['lat']);
$this->create_group_array['lng'] = $this->filter($_POST['lng']);
$this->create_group_array['state'] = $this->filter($_POST['state']);
$this->create_group_array['user_id'] = (int)$this->filter($_SESSION['user_id']);
$this->create_group_array['created_by_name'] = $this->filter($_SESSION['username']);
if($_POST['password']!==""){
$this->create_group_array['password'] = hash('sha256', $this->filter($_POST['password'].SALT));
}
}
public function create_new_group() {
//need to add validation with regex
$this->create_group_array['action'] = 0;
$create = new create_group_model($this->create_group_array);
$data = $create->insert_new_group($create->input);
///after group is created, join it
if($data['success']===1) {
$join = new join_group;
$join->start_join_group(array(
'user_id' => $this->create_group_array['user_id'],
'group_id'=> (int)$data['results'][0],
'privacy' => $this->create_group_array['privacy'],
'password'=> @$this->create_group_array['password'],
));
}
echo die(json_encode($data));
}
public function filter($var) {
$var = trim($var);
return $var;
}
}
class search_group{
public $search_group_array = array();
public function __construct() {
//$this->search_group_array['keyword'] = $this->filter($_POST['keyword']);
@$this->search_group_array['lat'] = $this->filter($_POST['lat']);
@$this->search_group_array['lng'] = $this->filter($_POST['lng']);
$this->search_group_array['user_id'] = (int)$this->filter($_SESSION['user_id']);
@$this->search_group_array['group_id'] = (int)$this->filter($_POST['group_id']);
}
public function filter($var) {
$var = trim($var);
return $var;
}
//show local groups that i am not a part of
public function initial_search_group() {
$this->search_group_array['action'] = 1;
$search_c = new search_group_model($this->search_group_array);
$data = $search_c->default_list_groups();
echo json_encode($data);
}
///show my groups
public function my_search_group() {
$this->search_group_array['action'] = 3;
$search_c = new search_group_model($this->search_group_array);
$data = $search_c->my_list_groups($search_c->input['user_id']);
echo json_encode($data);
}
//show one group profile
public function show_one() {
$this->search_group_array['action'] = 5;
$this->search_group_array['privacy'] = (int)$this->filter($_POST['privacy']);
$show = new search_group_model($this->search_group_array);
$data = $show->show_one($show->input['user_id'],$show->input['group_id'],(int)$show->input['privacy']);
echo json_encode($data);
}
}
class join_group {
public $join_array = array();
public function __construct() {
$this->join_array['user_id'] = (int)$this->filter($_SESSION['user_id' ]);
@$this->join_array['group_id'] = (int)$this->filter($_POST['group_id']);
$this->join_array['action'] = (int)$_POST['group_action'];
@$this->join_array['privacy'] = (int)$this->filter($_POST['privacy']);
if((int)$this->join_array['privacy'] !==0 ){
$this->join_array['password'] = hash('sha256', $this->filter($_POST['password'].SALT));
}
}
public function filter($var) {
$var = trim($var);
return $var;
}
//join group, only send info to client if they requested it with corresponding action 2/4
public function start_join_group($group) {
$join_c = new join_group_model($group);
$data = $join_c->join_group();
if(@$group['action']===2) {
echo json_encode($data);
}
}
public function start_unjoin_group($group) {
$join_c = new join_group_model($group);
$data = $join_c->unjoin_group();
if(@$group['action']===4){
echo json_encode($data);
}
}
}
?>
Model
The group Model:
<?php
require_once("database.php");
class model {
///data is returned to the controller
public $data = array(
'success'=>0,
'msg' =>'There was a small problem, try again soon.',
'data_type' => 'group',
'action' => '',
'results'=> array(),
);
//anything that will be put into the DB will be held in input
public $input = array();
public $exclude =array();
public function __construct($a) {
Global $db;
$db->escape($a);
$this->input = $db->escape($a);
@$this->data['action'] = $a['action'];
}
//move insert and data return up here
}
class create_group_model extends model {
public function insert_new_group($a) {
Global $db;
$b = $db->insert_array('group', $a, 'action');
if ($b['mysql_affected_rows']===1) {
$this->data['success'] = 1;
$this->data['msg'] = 'Congrats, you created a new group.';
array_push($this->data['results'], $b['mysql_insert_id']);
return $this->data;
} else {
$this->data['success'] = 0;
$this->data['msg'] = 'No group created, try again soon.';
return $this->data;
}
}
}
class search_group_model extends model {
public function default_list_groups() {
$this->list_groups($this->input['lat'], $this->input['lng'], 'group', 10, 30, $this->input['user_id'], 'all' );
$this->data['msg'] = 'Updated';
return $this->data;
}
public function custom_list_groups() {//add custom settings
$this->list_groups($this->input['lat'], $this->input['lng'], 'group', 10, 10);
}
public function my_list_groups($id){
Global $db;
//fix distance 10000 here, so it doesnt take into account distance
$b = $db->def_g_list($this->input['lat'], $this->input['lng'], 'group', 10000, 30, (int)$id, 'mine');
if ($b !== 0) {
$this->data['success'] = 1;
while ($row = mysql_fetch_array($b, MYSQL_ASSOC)) {
array_push($this->data['results'], $row);
}
$this->data['msg'] = 0;
return ($this->data);
} else {
$this->data['msg'] = 'There was a small problem, try again soon.';
$this->data['success'] = 0;
return ($this->data);
}
}
public function show_one($user_id, $group_id, $privacy) {
Global $db;
(bool)$confirm = FALSE;
if($privacy === 0){
$confirm = TRUE;
}else {
$confirm = FALSE;
$privacy = 1;
}
if(!$confirm){
$s = 'group_id';
$f = 'user_group_rel';
$w = sprintf("user_id =%d AND group_id=%d",$user_id, $group_id);
$b = $db->exists($s,$f,$w);
if(mysql_num_rows($b) ===1 && !is_num($b)) {
$confirm=true;
}
}
if($confirm){
$s = 'group_id,group_name,location_name,description,
user_id,lat,lng,created_by_name,state';
$f = 'group';
$w = sprintf("group_id=%d AND privacy=%d",(int)$group_id, (int)$privacy);
$b = $db->exists($s,$f,$w);
if(mysql_num_rows($b) ===1 && !is_int($b)) {
$this->data['success'] = 1;
$this->data['msg'] = 0;
while ($row = mysql_fetch_array($b, MYSQL_ASSOC)) {
array_push($this->data['results'], $row);
}
$this->data['results'][0]['people']= $this->group_mems($user_id, $group_id);
$this->data['results'][0]['total_people']= $this->group_mems_count($group_id);
return ($this->data);
}
}
$this->data['msg'] = 'There was a small problem, try again soon.';
$this->data['success'] = 0;
echo 'still going';
return ($this->data);
}
public function group_mems($me, $group_id) {
Global $db;
$result = array();
$q = sprintf("
SELECT
t1.group_id, t2.user_id, t2.username, t2.first_name, t2.last_name, t2.user_id = %d as me
FROM user_group_rel t1
LEFT JOIN users t2 ON t1.user_id = t2.user_id
WHERE group_id = %d and status = 1
ORDER BY me desc",
(int)$me, (int)$group_id);
$b = $db->query($q);
if ($b !== 0 and is_resource($b)) {
while ($row = mysql_fetch_array($b, MYSQL_ASSOC)) {
array_push($result, $row);
}
return $result;
} else {
return 'err';
}
}
public function group_mems_count($group_id) {
Global $db;
$result = array();
$q = sprintf("
SELECT
count(t1.user_id) as people
FROM user_group_rel t1
WHERE group_id = %d and t1.status = 1",
(int)$group_id);
$b = $db->query($q);
if ($b !== 0 and is_resource($b)) {
while ($row = mysql_fetch_array($b, MYSQL_ASSOC)) {
array_push($result, $row);
}
return $result[0]['people'];
} else {
return 'err';
}
}
private function list_groups($lat, $lng, $table, $distance, $limit, $id, $whos) {
Global $db;
$b = $db->def_g_list($lat, $lng, $table, $distance, $limit, (int)$id, $whos);
if ($b !== 0) {
$this->data['success'] = 1;
while ($row = mysql_fetch_array($b, MYSQL_ASSOC)) {
array_push($this->data['results'], $row);
}
return ($this->data);
} else {
$this->data['msg'] = 'There was a small problem, try again soon.';
$this->data['success'] = 0;
return ($this->data);
}
}
}
class join_group_model extends model {
public function join_group() {
Global $db;
$pass = 0;
if(array_key_exists('password', $this->input) && strlen($this->input['password'])>20) {
$pass = $db->pass_check('group', $this->input['group_id'],$this->input['password'] );
}
else {
$pass = $db->pass_check('group', $this->input['group_id'],'NULL' );
}
if($pass !==0) {
array_push($this->exclude, 'password', 'action');
$b = $db->insert_array('user_group_rel',$this->input, $this->exclude);
//echo print_r($b);
if ($b !== 0) {
$this->data['success'] = 1;
$this->data['results'] = $this->input['group_id'];
$this->data['msg'] = ' you joined a new group. ';
return ($this->data);
} else {
$this->data['msg'] = 'There was a small problem, try again soon.';
$this->data['success'] = 0;
return ($this->data);
}
}
}
public function unjoin_group() {
Global $db;
$b = $db->delete('user_group_rel', (int)$this->input['user_id'], (int)$this->input['group_id']);
if ($b !== 0) {
$this->data['success'] = 1;
$this->data['results'] = $this->input['group_id'];
$this->data['msg'] = ' you left that group. ';
return ($this->data);
} else {
$this->data['msg'] = 'There was a small problem, try again soon.';
$this->data['success'] = 0;
return ($this->data);
}
}
}
Database
Application wide database methods. Probably the most glaring mistake here is not using PDO statements.
<?php
require_once('../config/config.php');
class MySQLDatabase {
private $connection;
public $last_query;
private $magic_quotes_active;
private $real_escape_string_exists;
function __construct() {
$this->open_connection();
$this->magic_quotes_active = get_magic_quotes_gpc();
$this->real_escape_string_exists = function_exists( "mysql_real_escape_string" );
}
public function open_connection() {
$this->connection = mysql_connect(DB_SERVER, DB_USER, DB_PASS);
if (!$this->connection) {
die("Database connection failed: " . mysql_error());
} else {
$db_select = mysql_select_db(DB_NAME, $this->connection);
if (!$db_select) {
die("Database selection failed: " . mysql_error());
}
}
}
public function close_connection() {
if(isset($this->connection)) {
mysql_close($this->connection);
unset($this->connection);
}
}
public function query($sql) {
$this->last_query = $sql;
$result = mysql_query($sql, $this->connection);
$this->confirm_query($result);
return $result;
}
public function escape($q) {
if(is_array($q))
foreach($q as $k => $v)
$q[$k] = $this->escape($v); //recursive
else if(is_string($q)) {
$q = mysql_real_escape_string($q);
}
return $q;
}
private function confirm_query($result) {
if (!$result) {
$output = "Database query failed: " . mysql_error() . "<br /><br />";
die( $output );
}
}
public function exists($s,$f, $w) {
//rewrite bottom 2 functions rid of this function
$q = sprintf("SELECT %s FROM %s WHERE %s LIMIT 1",$s,$f, $w);
//echo $q;
$result = $this->query($q);
if (mysql_num_rows($result)===0) {
return 0;
} else if (mysql_num_rows($result)===1) {
return $result;
} else{
return 0;
}
}
public function pass_check($t, $id, $p) {
if ($p==='NULL'){
$q = sprintf("SELECT * FROM %s WHERE %s_id = %s AND password is NULL", $t,$t,$id);
}
else{
$q = sprintf("SELECT * FROM %s WHERE %s_id = %s AND password = '%s'", $t,$t,$id,$p);
}
$result = $this->query($q);
if (mysql_num_rows($result)===0) {
return (int)0;
} else if (mysql_num_rows($result)>0) {
return $result;
}
}
public function insert_array($table, $data, $exclude = array()) {
$fields = $values = array();
if( !is_array($exclude) ) $exclude = array($exclude);
foreach( array_keys($data) as $key ) {
if( !in_array($key, $exclude) ) {
$fields[] = "`$key`";
$values[] = "'" .$data[$key] . "'";
}
}
$fields = implode(",", $fields);
$values = implode(",", $values);
if( mysql_query("INSERT INTO `$table` ($fields) VALUES ($values)") ) {
return array( "mysql_error" => false,
"mysql_insert_id" => mysql_insert_id(),
"mysql_affected_rows" => mysql_affected_rows(),
"mysql_info" => mysql_info()
);
} else {
echo print_r(array( "mysql_error" => mysql_error() ));
return 0;
}
}
public function def_g_list($lat, $lng, $table, $dist, $limit, $id, $whos='all') {
//either refactor to make this function more flexible or get rid of table variable
$where = '';
if(is_int($id) && $id>0 && $id < 100000 && $whos == 'all'){
//subquery used to display only groups the user is NOT IN- probably a better way to do this
$where = sprintf("
t2.group_id NOT IN (
SELECT user_group_rel.group_id
FROM user_group_rel
WHERE user_group_rel.user_id =%d)",
(int)$id);
} else if (is_int($id) && $id>0 && $id < 100000 && $whos == 'mine') {
$where = 't3.user_id = ' . (int)$id;
} else {
echo 'fuckin fail';
return 0;
}
$d_formula = $this->distance_formula($lat, $lng, 't1');
//sorry for this query
$q = sprintf("
SELECT
t1.group_id, t1.group_name, t1.location_name, t1.description, t1.lat, t1.lng, t1.privacy,t2.people, %s
FROM %s AS t1
JOIN (SELECT group_id, count(group_id) as people
FROM user_group_rel
GROUP BY group_id) t2
ON t1.group_id = t2.group_id
JOIN (SELECT user_group_rel.group_id, user_group_rel.user_id
FROM user_group_rel ) t3
ON t1.group_id = t3.group_id
WHERE %s
GROUP BY t1.group_id
HAVING distance < %s
ORDER BY distance
LIMIT %s",
$d_formula, $table, $where,$dist, $limit );
$result = $this->query($q);
if (mysql_num_rows($result)===0) {
return 0;
} else if (mysql_num_rows($result)>0) {
return $result;
}
}
function delete($table,$uid,$cid) {
$q = sprintf('
DELETE FROM `disruptly`.`%s`
WHERE `%s`.`user_id` = %d
AND `%s`.`group_id` = %d
',$table, $table, $uid,$table, $cid
);
//echo $q;
$result = $this->query($q);
if ($result===0) {
return 0;
} else if ($result===1) {
return $result;
}
}
public function distance_formula($lat, $lng, $table) {
//get rid of the round after'SELECT *,' for more accurate results
$q = sprintf("round(3956 * 2 * ASIN(SQRT( POWER(SIN((%s -abs(%s.lat)) * pi()/180 / 2),2) + COS(%s * pi()/180 ) * COS(abs(%s.lat) * pi()/180) * POWER(SIN((%s - %s.lng) * pi()/180 / 2), 2) )),2) as distance ", $lat, $table, $lat, $table, $lng, $table);
return $q;
}
}
$database = new MySQLDatabase();
$db =& $database;