diff --git a/Router.php b/Router.php index f9318dc..dad1d9b 100644 --- a/Router.php +++ b/Router.php @@ -15,6 +15,10 @@ spl_autoload_register(function ($class_name) { elseif(file_exists(__DIR__ . "/models/" . $class_name . ".php")) { include_once __DIR__."/models/Model.php"; include_once __DIR__ . "/models/" . $class_name . ".php"; +// echo "Load class ".__DIR__."/models/".$class_name.'.php'."
\n"; + }elseif(file_exists(__DIR__ . "/models/Plan/" . $class_name . ".php")) { + include_once __DIR__."/models/Model.php"; + include_once __DIR__ . "/models/Plan/" . $class_name . ".php"; // echo "Load class ".__DIR__."/models/".$class_name.'.php'."
\n"; } }); diff --git a/application/Auth.php b/application/Auth.php index d9ff8f7..bf7f65b 100644 --- a/application/Auth.php +++ b/application/Auth.php @@ -227,6 +227,7 @@ class Auth { } public static function currentUserId(){ + $a = Auth::getInstance(); return $_SESSION['user_id']; } } diff --git a/application/WebPage.php b/application/WebPage.php index 2bf82aa..c158c35 100644 --- a/application/WebPage.php +++ b/application/WebPage.php @@ -14,6 +14,7 @@ class WebPage { public $pr = ""; // project root public $err = array(); public $msg = array(); + public $data = array(); public function __construct(){ diff --git a/models/Model.php b/models/Model.php index 589211c..5aa0748 100644 --- a/models/Model.php +++ b/models/Model.php @@ -9,8 +9,11 @@ abstract class Model { protected static $table; protected static $tableExtends = null; protected static $fields; + protected $isLoaded = false; + protected $initialValues = []; + protected $errors = []; - public function __construct(){ + public function __construct($id = 0){ foreach (static::$fields as $field => $type){ switch ($type){ case "INT": @@ -23,6 +26,29 @@ abstract class Model { break; } } + + + if($id > 0){ + try { + $firstField = array_keys(static::$fields)[0]; + $result = DB::query("SELECT `".implode("`, `", array_keys(static::$fields))."` FROM ".static::$database.".".static::$table . " t1 WHERE `$firstField` = ?", $id )->fetch_assoc(); + + $values = []; + foreach (static::$fields as $field => $type){ + $this->{ $field } = $result[$field]; + + // cache initial value + $values[$field] = $this->{$field}; + } + + $this->isLoaded = true; + + } catch (DatabaseException $e) { + $this->errors[] = $e; + } + } + + $this->initialValues = $this->asArray(); } /** @@ -95,6 +121,7 @@ abstract class Model { $result = array(); while($row = $q->fetch_assoc()){ $res = new static(); + $res->isLoaded = true; /* Alternative for non-joined queries: foreach (static::$fields as $key => $ignore){ @@ -118,9 +145,67 @@ abstract class Model { return false; } + public function isLoaded() : bool { + return $this->isLoaded; + } + // public function set($value, ) + /** + * @throws DatabaseException + */ public function save(){ + $newFields = []; + $newValues = []; + $allExceptFirstFields = []; + $allExceptFirstValues = []; + + $fieldCount = 0; + foreach(static::$fields as $field => $type){ + $currValue = $this->{ $field }; + if(!empty($this->initialValues) && $currValue != $this->initialValues[$field]){ + $newFields[] = $field; + $newValues[] = $currValue; + } + if($fieldCount > 0){ + $allExceptFirstFields[] = $field; + $allExceptFirstValues[] = $currValue; + } + + $fieldCount++; + } + + if(!empty($this->initialValues) && empty($newValues)){ + return false; + } + + if($this->isLoaded()){ + $updateSets = []; + foreach($newFields as $field){ + $updateSets[] = "`$field` = ?"; + } + + DB::query(sprintf('UPDATE %s.%s SET %s WHERE %s LIMIT 1', static::$database, static::$table, implode(', ', $updateSets), "`" . array_keys(static::$fields)[0] . "` = ?"), array_merge($newValues, [$this->{array_keys(static::$fields)[0]}])); + } + else { + $sql = sprintf('INSERT INTO %s.%s (%s) VALUE (%s)', static::$database, static::$table, implode(', ', $newFields), implode(', ', array_fill(0, count($newFields), '?'))); + + DB::query($sql, $newValues); + $insertid = DB::insert_id(); + $this->{ array_keys(static::$fields)[0] } = $insertid; + $this->isLoaded = true; + return $insertid; + } + + return true; + } + + public function asArray(): array { + $arr = []; + foreach ($this as $key => $val){ + $arr[$key] = $val; + } + return $arr; } } \ No newline at end of file diff --git a/models/Plan/PlanStore.php b/models/Plan/PlanStore.php new file mode 100644 index 0000000..f580d36 --- /dev/null +++ b/models/Plan/PlanStore.php @@ -0,0 +1,30 @@ + "INT", + "space_id" => "INT", + "name" => "VARCHAR", + "created" => "DATETIME", + "state" => [ 'planning', 'shopping', 'closed' ] + ]; + + public static function getUserSpaces(){ + $spaces = array_merge( + static::get([ 'owner_id' => Auth::currentUserId() ]), + PlanSpaceMember::get([ 'member_id' => Auth::currentUserId() ]) + ); + + foreach ($spaces as $s){ + if($s->space_name == ""){ + $spaceOwner = User::get(['user_id' => $s->owner_id])[0]; + $s->space_name = $spaceOwner->full_name != "" ? sprintf('%ss space', $spaceOwner->full_name ) : "A users space";; + } + } + + return $spaces; + } +} \ No newline at end of file diff --git a/models/Plan/PlanStoreItem.php b/models/Plan/PlanStoreItem.php new file mode 100644 index 0000000..c98e0fe --- /dev/null +++ b/models/Plan/PlanStoreItem.php @@ -0,0 +1,29 @@ + "INT", + "space_name" => "VARCHAR", + "owner_id" => "INT", + "space_type" => [ 'STORE', 'CHECK', 'CALORIES' ] + ]; + + public static function getUserSpaces(){ + $spaces = array_merge( + static::get([ 'owner_id' => Auth::currentUserId() ]), + PlanSpaceMember::get([ 'member_id' => Auth::currentUserId() ]) + ); + + foreach ($spaces as $s){ + if($s->space_name == ""){ + $spaceOwner = User::get(['user_id' => $s->owner_id])[0]; + $s->space_name = $spaceOwner->full_name != "" ? sprintf('%ss space', $spaceOwner->full_name ) : "A users space";; + } + } + + return $spaces; + } +} \ No newline at end of file diff --git a/models/PlanSpace.php b/models/PlanSpace.php index 2292fa1..c98e0fe 100644 --- a/models/PlanSpace.php +++ b/models/PlanSpace.php @@ -7,14 +7,23 @@ class PlanSpace extends Model { protected static $fields = [ "space_id" => "INT", "space_name" => "VARCHAR", - "owner_id" => "VARCHAR", + "owner_id" => "INT", "space_type" => [ 'STORE', 'CHECK', 'CALORIES' ] ]; public static function getUserSpaces(){ - return array_merge( + $spaces = array_merge( static::get([ 'owner_id' => Auth::currentUserId() ]), PlanSpaceMember::get([ 'member_id' => Auth::currentUserId() ]) ); + + foreach ($spaces as $s){ + if($s->space_name == ""){ + $spaceOwner = User::get(['user_id' => $s->owner_id])[0]; + $s->space_name = $spaceOwner->full_name != "" ? sprintf('%ss space', $spaceOwner->full_name ) : "A users space";; + } + } + + return $spaces; } } \ No newline at end of file diff --git a/models/User.php b/models/User.php new file mode 100644 index 0000000..451f8d2 --- /dev/null +++ b/models/User.php @@ -0,0 +1,15 @@ + "INT", + "md5_id" => "VARCHAR", + "full_name" => "VARCHAR", + "user_email" => "VARCHAR", + "user_level" => "INT", + "date" => "DATE" + ]; +} \ No newline at end of file diff --git a/templates/default/plan/index.html b/templates/default/plan/index.html index 97e991c..6866f10 100644 --- a/templates/default/plan/index.html +++ b/templates/default/plan/index.html @@ -15,7 +15,7 @@ diff --git a/www/api/v2/api.php b/www/api/v2/api.php index 8723582..15f8f8d 100644 --- a/www/api/v2/api.php +++ b/www/api/v2/api.php @@ -33,7 +33,13 @@ abstract class Api { } if($this->success){ - $this->execute(); + try { + $this->execute(); + } + catch (Exception | DatabaseException $e){ + $this->success = false; + $this->message = $e; + } } $this->printResult(); @@ -52,6 +58,9 @@ abstract class Api { echo json_encode( $returns ); } + /** + * @throws DatabaseException + */ protected function execute(){ } /** diff --git a/www/api/v2/index.php b/www/api/v2/index.php index 7a49ebd..901f454 100644 --- a/www/api/v2/index.php +++ b/www/api/v2/index.php @@ -1,5 +1,13 @@ "API not found." ]); diff --git a/www/api/v2/plan/create.php b/www/api/v2/plan/create.php new file mode 100644 index 0000000..ef95476 --- /dev/null +++ b/www/api/v2/plan/create.php @@ -0,0 +1,42 @@ + [ + [ + "name" => "space_id", + "keyword" => "space_id", + "type" => VERIFY_STRING + ], + [ + "name" => "name", + "keyword" => "name", + "type" => VERIFY_STRING + ] + ] + ]; + + /** + * @throws DatabaseException + */ + function execute(){ + try { + $store = new PlanStore(); + $store->space_id = $this->data['space_id']; + $store->name = $this->data['name']; + + $this->result = $store->save(); + + $this->success = true; + $this->message = "OK"; + } + catch (DatabaseException $e){ + $this->success = false; + $this->message = "Error: " . $e; + } + } +} + +$request = new CreateStoreApi(); + diff --git a/www/api/v2/plan/modify.php b/www/api/v2/plan/modify.php new file mode 100644 index 0000000..158a447 --- /dev/null +++ b/www/api/v2/plan/modify.php @@ -0,0 +1,46 @@ + [ + [ + "name" => "store_id", + "keyword" => "store_id", + "type" => VERIFY_INT + ], + [ + "name" => "newname", + "keyword" => "newname", + "type" => VERIFY_STRING + ], + [ + "name" => "state", + "keyword" => "state", + "type" => VERIFY_STRING + ], + ] + ]; + + function execute(){ + + $store = PlanStore::get(['plan_store_id' => $this->data['store_id']])[0]; + if(isset($this->data['newname'])){ + $store->name = $this->data['newname']; + } + elseif(isset($this->data['state'])){ + $store->state = $this->data['state']; + } + + + $store->save(); + + $this->result = $store; + + $this->success = true; + $this->message = "OK"; + } +} + +$request = new ModifyStoreApi(); + diff --git a/www/api/v2/plan/stores.php b/www/api/v2/plan/stores.php new file mode 100644 index 0000000..c75e0f6 --- /dev/null +++ b/www/api/v2/plan/stores.php @@ -0,0 +1,36 @@ + [ + [ + "name" => "space", + "keyword" => "space", + "type" => VERIFY_INT + ], + ], + "URI" => [ + [ + "name" => "space", + "keyword" => "space", + "type" => VERIFY_INT + ], + ] + ]; + + /** + * @throws DatabaseException + */ + function execute(){ + $stores = PlanStore::get(['space_id' => $this->data['space'] ]); + + $this->result = $stores; + + $this->success = true; + $this->message = "OK"; + } +} + +$request = new GetStoresApi(); + diff --git a/www/api/v2/space/create.php b/www/api/v2/space/create.php new file mode 100644 index 0000000..a5a51c6 --- /dev/null +++ b/www/api/v2/space/create.php @@ -0,0 +1,35 @@ + [ + [ + "name" => "space_name", + "keyword" => "space_name", + "type" => VERIFY_STRING + ], + [ + "name" => "space_type", + "keyword" => "space_type", + "type" => VERIFY_STRING + ], + ] + ]; + + function execute(){ + + $space = new PlanSpace(); + $space->space_name = $this->data['space_name']; + $space->space_type = $this->data['space_type'] ?? 'STORE'; + $space->owner_id = Auth::currentUserId(); + + $this->result = $space->save(); + + $this->success = true; + $this->message = "OK"; + } +} + +$request = new CreateSpacesApi(); + diff --git a/www/api/v2/space/get.php b/www/api/v2/space/get.php index 65d032b..6b2a098 100644 --- a/www/api/v2/space/get.php +++ b/www/api/v2/space/get.php @@ -1,7 +1,5 @@ result = PlanSpace::get(); + $this->result = PlanSpace::getUserSpaces(); + $this->success = true; $this->message = "OK"; } diff --git a/www/plan/plan.js b/www/plan/plan.js index 19e6e3c..a16d57d 100644 --- a/www/plan/plan.js +++ b/www/plan/plan.js @@ -227,7 +227,8 @@ class Store { } if(prevState !== state){ - ajaxReq({ plan: 'setState', storeID: this.storeID, state: this.state }); + // ajaxReq({ plan: 'setState', storeID: this.storeID, state: this.state }); + qAPI('plan/modify', 'POST', { store_id: this.storeID, state: this.state }) } } @@ -612,7 +613,7 @@ class Store { getStoreID(){ let that = this; - return ajaxReq({ plan: 'saveStore', storeName: this.title }) + return qAPI('plan/create', 'POST', { space_id: spaceID, name: this.title }) .done(json => { console.log("getStore:", json); that.storeID = json['data']; @@ -624,7 +625,14 @@ class Store { this.title = newName; if(this.storeID !== null){ - return ajaxReq({ plan: 'renameStore', storeID: this.storeID, newName: newName }); + return qAPI('plan/modify', 'POST', { store_id: this.storeID, newname: newName }) + // return ajaxReq({ plan: 'renameStore', storeID: this.storeID, newName: newName }); + } + else { + return qAPI('plan/create', 'POST', { space_id: spaceID, name: this.title }).done(json => { + this.storeID = json.data; + }); + } // Return blank ajax as a false @@ -663,63 +671,54 @@ function updateTotalPrice(){ } let spaceID = 0; -function getSpaces(){ - $("#stores").html("Loading..."); - return ajaxReq({plan: 'spaces'}).done(json => { - let optionsHtml = ""; - - // console.log(json); - - let spaceNum = 1; - for(const spaceI in json.data.spaces){ - const space = json.data.spaces[spaceI]; - if(spaceID === 0){ - spaceID = space['space_id']; - } - let spaceName = space['space_name'] || "Space "+spaceNum; - optionsHtml += "\n"; - spaceNum++; - } - - let spaceSelectElem = $("#spaceSelect"); - spaceSelectElem - .html(optionsHtml) - .on('change', ev => { - spaceID = $(ev.target).val(); - getStores(spaceID); - }); - - if(typeof json.data.lastSpace !== "undefined"){ - spaceID = Number(json.data.lastSpace); - spaceSelectElem.val(spaceID); - } - }); +// function getSpaces(){ + // $("#stores").html("Loading..."); + // return ajaxReq({plan: 'spaces'}).done(json => { + // let optionsHtml = ""; + // + // console.log(json); + // + // let spaceNum = 1; + // for(const spaceI in json.data.spaces){ + // const space = json.data.spaces[spaceI]; + // if(spaceID === 0){ + // spaceID = space['space_id']; + // } + // let spaceName = space['space_name'] || "Space "+spaceNum; + // optionsHtml += "\n"; + // spaceNum++; + // } + // .html(optionsHtml) -} -getSpaces().done(() => { getStores(spaceID); }); + + // if(typeof json.data.lastSpace !== "undefined"){ + // spaceID = Number(json.data.lastSpace); + // spaceSelectElem.val(spaceID); + // } + // }); + +// } +// getSpaces().done(() => { getStores(spaceID); }); + +let spaceSelectElem = $("#spaceSelect"); +spaceSelectElem.on('change', ev => { + // console.debug('Change space'); + spaceID = $(ev.target).val(); + getStores(spaceID); +}).trigger('change'); + // GET STORES function getStores(spaceID){ spaceID = spaceID || 0; - // $("#stores").html(""); stores = []; - let options = { plan: '' }; - if(spaceID !== 0){ - options = { plan: '', space: spaceID } - } - - return $.getJSON('do.php', options) + return qAPI('plan/stores/'+spaceID ) .done(json => { - if(handleJsonErrors(json)){ - return; - } - $("#stores").html(""); - // console.log(json); for(const store in json.data){ let storeKey = stores.length; @@ -735,12 +734,13 @@ function getStores(spaceID){ stores[storeKey].setState(json.data[store].state, 1); } updateTotalPrice(); - }) - .fail(handleAjaxErrors); + }); } function ajaxReq( data ){ + console.log("REQUEST STOPPED", data); + return false; if(typeof spaceID !== "undefined" && spaceID !== 0){ data.space = spaceID; } @@ -756,10 +756,31 @@ function ajaxReq( data ){ .fail(handleAjaxErrors); } +function qAPI( path, method, data ){ + method = method || 'GET'; + data = data || {}; + + // if(typeof spaceID !== "undefined" && spaceID !== 0){ + // data.space = spaceID; + // } + + return $.ajax({ + method: method, + url: "/api/v2/" + path, + data: data, + dataType: 'JSON' + }) + .done(json => { + return !handleJsonErrors(json); + }) + .fail(handleAjaxErrors); +} + function handleJsonErrors(json){ - if(typeof json.status != "undefined" && json.status !== 0){ + if(typeof json.status != "undefined" && json.status !== true && json.status !== 0){ if(json.message === "Not logged in"){ - location.href = '../login.php'; + // location.href = '../login.php'; + alert("Not logged in..."); } else { alert(json.message);