diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0ee7bb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +sys/*.sqlite \ No newline at end of file diff --git a/README.md b/README.md index e4256b7..d4e49e6 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,103 @@ -#meta +# miniMVC +miniMVC is a minimalistic Modular MVC framework, with built-in minifier, and pure-PHP templating system. -So, you know all that client information you need to keep track of, like passwords and servers? Or how about all those serial keys for the software you have? meta is a webapp to organize these kinds of data for easier reference. +### Requirements +* PHP 5.4+ +* PDO extensions for databases you wish to use +* Webserver that correctly handles REQUEST_URI, such as: + * Apache + * IIS + * Lighttpd +* SimpleTest library for running unit tests -##Organizing Data - -Meta has a four-level hierarchy - -1. Genre - * The broadest category. An example could be titled 'Client Data' -2. Category - * A general item in the genre. For example, an individual client. -3. Section - * A grouping of label-data pairs. Something like 'CMS Access' -4. Data - * Pairs of labels and values. +### Unique features +#### Extensive use of PHP's magic methods on the base class +* `__toString()` method allows a view of the current class object when the current class object is used as a string. If you prefer `var_dump()` or `var_export()`, you can pass the name of that function if you call the `__toString` method directly. - Eg. Username : admin + Eg. `$this . "string"`, `$this->__toString()`, `echo $this`; + +* `__call()` method allows the dynamic addition of callable closure objects + + Eg. `$this->foo = function($baz){}` is callable as `$this->foo()`, with the current object as the last argument + +* `MM` class extends ArrayObject, and all the main classes extend this class. Functions begining with `array_` are callable on object from this class. E.g. `$this->array_keys()` will return a list of the class properties. + +#### Database class is an extension of PHP's PDO class. + +Database class uses [Query](https://github.com/aviat4ion/Query) as a database abstraction layer and query builder. + +Database connections are set in /app/config/db.php + +### File Structure + +* index.php - framework bootstrap + +* app - configuration and app-wide files + * classes - helper classes + * config - configuration files + * modules - MVC triads + * controllers - controller classes + * models - model classes + * views - module-specific views + * views - global page templates + * errors - error page templates + +* assets - frontend files + * js - javascript files + * css - css files + * config - minifier configuration files + +* sys - core framework classes + +### Common Tasks + +* Creating a controller + + db = miniMVC\db::get_instance($db_name);` + + Note that multiple databases can be used in the same class + by specifying a different database name. + +* Loading a model (From a controller) + + `$this->load_model($model)` - creates an instance of that model as a member of the current class. After loading the model, you can call its methods like so + + `$this->[model name]->method()` + +* Loading a class + + Librarys / classes found in `app/classes` or `sys/libraries` are autoloaded. + To call a library, simply instantiate that class. + + Classes with a `get_instance` static methods should be called like so: + + `$obj =& miniMVC\class::get_instance()` + + + Other classes should be called using the new operator + + `$obj = new miniMVC\class()` + + \ No newline at end of file diff --git a/app/classes/index.html b/app/classes/index.html new file mode 100644 index 0000000..e69de29 diff --git a/app/config/config.php b/app/config/config.php new file mode 100644 index 0000000..ea843d7 --- /dev/null +++ b/app/config/config.php @@ -0,0 +1,135 @@ + array( + 'type' => 'sqlite', + 'host' => '', + 'user' => '', + 'pass' => '', + 'port' => '', + 'database' => '', + 'file' => MM_SYS_PATH . 'meta.sqlite', + ) +); + +// End of db.php \ No newline at end of file diff --git a/app/config/routes.php b/app/config/routes.php new file mode 100644 index 0000000..51a2b11 --- /dev/null +++ b/app/config/routes.php @@ -0,0 +1,41 @@ + 'blog/blog/index' + * + * To route a special 404 page, set '404_route' to the "module/controller/method" you wish to use + * + * @package miniMVC + * @subpackage App + */ + +// -------------------------------------------------------------------------- + +return array( + // Default Paths + 'default_controller' => 'welcome', + 'default_module' => 'meta', + 'genre' => 'meta/genre/index', + 'genre/add' => 'meta/genre/add', + '404_route' => '', +); + +// End of routes.php \ No newline at end of file diff --git a/app/modules/meta/controllers/genre.php b/app/modules/meta/controllers/genre.php new file mode 100644 index 0000000..c8a5ea7 --- /dev/null +++ b/app/modules/meta/controllers/genre.php @@ -0,0 +1,102 @@ +load_model('meta\model'); + + $this->page->build_header(); + } + + /** + * Default controller method + */ + public function index() + { + // Re-route to detail page if the last segment + // is a valid integer + $id = (int) miniMVC\get_last_segment(); + + if ($id !== 0) + { + return $this->detail($id); + } + + // Otherwise, display list of genres + $data = array(); + $data['genres'] = $this->model->get_genres(); + + $this->load_view('genres', $data); + $this->page->build_footer(); + } + + /** + * Adds a new genre + */ + public function add() + { + // Strip away tags for the sake of security + $name = strip_tags($_POST['genre']); + + // Make sure the name doesn't already exist. If it does, show an error. + $res = $this->model->add_genre($name); + + if ($res === TRUE) + { + $this->page->set_message('success', 'Added new genre'); + } + else + { + $this->page->set_message('error', 'Genre already exists'); + } + + // Render the basic page + $this->index(); + } + + /** + * Returns the categories / editing options for a genre + * + * @param int + */ + public function detail($id) + { + $genre = $this->model->get_genre_by_id($id); + $categories = $this->model->get_categories($id); + + $data = array( + 'genre' => $genre, + 'categories' => $categories, + 'genre_id' => $id + ); + + $this->load_view('genre_detail', $data); + $this->page->build_footer(); + } +} + +// End of genre.php \ No newline at end of file diff --git a/app/modules/meta/controllers/welcome.php b/app/modules/meta/controllers/welcome.php new file mode 100644 index 0000000..a448b9b --- /dev/null +++ b/app/modules/meta/controllers/welcome.php @@ -0,0 +1,59 @@ +load_model('meta\model'); + + } + + // -------------------------------------------------------------------------- + + /** + * Default route for the controller + * + * @return void + */ + public function index() + { + $data = array(); + $data['genres'] = $this->model->get_genres(); + + $this->page->render('genres', $data); + } + + // -------------------------------------------------------------------------- + + public function login() + { + + } +} + +// End of welcome.php \ No newline at end of file diff --git a/app/modules/meta/models/model.php b/app/modules/meta/models/model.php new file mode 100644 index 0000000..13c5c83 --- /dev/null +++ b/app/modules/meta/models/model.php @@ -0,0 +1,351 @@ +session =& \miniMVC\Session::get_instance(); + $this->db =& \miniMVC\db::get_instance(); + } + + // -------------------------------------------------------------------------- + + /** + * Delete a genre/category/section or data item + * + * @param string $type + * @param int $id + */ + public function delete($type, $id) + { + $this->db->where('id', (int) $id) + ->delete($type); + } + + // -------------------------------------------------------------------------- + + /** + * Move a category/section/data item to another parent + * + * @param string + * @param int + * @param int + */ + public function move($type, $type_id, $parent_id) + { + $parent_type = array( + 'data' => 'section', + 'section' => 'category', + 'category' => 'genre' + ); + + $parent_field = "{$parent_type[$type]}_id"; + + $this->db->set($parent_field, $parent_id) + ->where('id', (int) $type_id) + ->update($type); + } + + // -------------------------------------------------------------------------- + + /** + * Add genre + * + * @param string + * @return bool + */ + public function add_genre($genre) + { + // Check for duplicates + $query = $this->db->from('genre') + ->where('genre', $genre) + ->get(); + + // Fetch the data as a workaround + // for databases that do not support + // grabbing result counts (SQLite / Firebird) + $array = $query->fetchAll(); + if (count($array) === 0) + { + $this->db->set('genre', $genre) + ->insert('genre'); + + return TRUE; + } + + return FALSE; + + } + + // -------------------------------------------------------------------------- + + /** + * Rename a genre + * + * @param int + * @param string + */ + public function update_genre($genre_id, $genre) + { + $this->db->set('genre', $genre) + ->where('id', $genre_id) + ->update('genre'); + } + + // -------------------------------------------------------------------------- + + /** + * Add category to genre + * + * @param string + * @param int + */ + public function add_category($cat, $genre_id) + { + $this->db->set('category', $cat) + ->set('genre_id', $genre_id) + ->insert('category'); + } + + // -------------------------------------------------------------------------- + + /** + * Rename a category + * + * @param int + * @param string + */ + public function update_category($cat_id, $category) + { + $this->db->set('category', $category) + ->where('id', (int) $cat_id) + ->update('category'); + } + + // -------------------------------------------------------------------------- + + /** + * Add a section to a category + * + * @param string + * @param int + */ + public function add_section($section, $category_id) + { + $this->db->set('section', $section) + ->set('category_id', (int) $category_id) + ->insert('section'); + } + + // -------------------------------------------------------------------------- + + /** + * Rename a section + * + * @param int + * @param string + */ + public function update_section($section_id, $section) + { + $this->db->set('section', $section) + ->where('id', (int) $section_id) + ->update('section'); + } + + // -------------------------------------------------------------------------- + + /** + * Add key/value data to a section + * + * @param int + * @param mixed object/array + */ + public function add_data($section_id, $data) + { + // Convert the data to json for storage + $data_str = json_encode($data); + + // Save the data + $this->db->set('data', $data_str) + ->set('section_id', (int) $section_id) + ->insert('data'); + } + + // -------------------------------------------------------------------------- + + /** + * Update the data + * + * @param int + * @param mixed + */ + public function update_data($data_id, $data) + { + // Convert the data to json for storage + $data_str = json_encode('data', $data_str); + + // Save the data + $this->db->set('data', $data_str) + ->where('id', (int) $data_id) + ->update('data'); + + } + + // -------------------------------------------------------------------------- + + /** + * Gets the list of genres + * + * @return array + */ + public function get_genres() + { + $genres = array(); + $query = $this->db->select('id, genre') + ->from('genre') + ->order_by('genre', 'asc') + ->get(); + + while($row = $query->fetch(\PDO::FETCH_ASSOC)) + { + $genres[$row['id']] = $row['genre']; + } + + return $genres; + } + + /** + * Gets the name of the genre from its id + * + * @param int + * @return string + */ + public function get_genre_by_id($id) + { + $query = $this->db->select('genre') + ->from('genre') + ->where('id', (int) $id) + ->get(); + + $row = $query->fetch(\PDO::FETCH_ASSOC); + + return $row['genre']; + } + + // -------------------------------------------------------------------------- + + /** + * Get the categories for the specified genre + * + * @param int + * @return array + */ + public function get_categories($genre_id) + { + $cats = array(); + + $query = $this->db->select('id, category') + ->from('category') + ->where('genre_id', (int) $genre_id) + ->get(); + + while($row = $query->fetch(\PDO::FETCH_ASSOC)) + { + $cats[$row['id']] = $row['category']; + } + + return $cats; + } + + // -------------------------------------------------------------------------- + + /** + * Get the sections for the specified category id + * + * @param int + * @return array + */ + public function get_sections($category_id) + { + $sections = array(); + + $query = $this->db->select('id, section') + ->from('section') + ->where('category_id', (int) $category_id) + ->get(); + + while($row = $query->fetch(\PDO::FETCH_ASSOC)) + { + $sections[$row['id']] = $row['section']; + } + + return $sections; + } + + // -------------------------------------------------------------------------- + + /** + * Get the data fro the section + * + * @param int + * @return array + */ + public function get_data($section_id) + { + $data = array(); + + $query = $this->db->select('id, data') + ->from('data') + ->where('section_id', (int) $section_id) + ->get(); + + while($row = $query->fetch(\PDO::FETCH_ASSOC)) + { + $data[$row['id']] = json_decode($row['data'], TRUE); + } + + return $data; + } + +} + +// End of model.php \ No newline at end of file diff --git a/app/modules/meta/models/user_model.php b/app/modules/meta/models/user_model.php new file mode 100644 index 0000000..023837a --- /dev/null +++ b/app/modules/meta/models/user_model.php @@ -0,0 +1,32 @@ +meta +
Code: = $code ?>
+Driver Code: = $driver_code ?>
+Message: = $message ?>
+Message: = $message; ?>
+ + + +Backtrace:
+ getTrace() as $error): ?> + + +
+ File: = str_replace(MM_BASE_PATH, "", $error['file']) ?>
+ Line: = $error['line'] ?>
+ Function: = $error['function'] ?>
+