Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

many updates and added features

  • Loading branch information...
commit c24f742b086ade567e6b26286d4699a6173547a0 1 parent 5ebae5d
Stefan Henzen authored
View
0  .gitignore 100644 → 100755
File mode changed
View
0  .htaccess 100644 → 100755
File mode changed
View
0  MIT-LICENSE 100644 → 100755
File mode changed
View
2  README 100644 → 100755
@@ -17,7 +17,7 @@ To set up a project using micro-framework you have to make a directory which con
* configure access with [project]/.htaccess and [project]/public/.htaccess
-On windows: I have no idea
+On windows: I have no idea [please help]
== Routing
All requests must be made to [project]/index.php. They are routed to a controller in [project]/controllers based on their url. For example, a request to [project]/index.php/static/index results in a call to the index method of a StaticController instance. A controller can use models in [project]/models to access a database. It can render a view from [project]/views/[controllername]/.
View
2  boot.php 100644 → 100755
@@ -4,6 +4,8 @@
require('utils.php');
require('model_utils.php');
require('html_utils.php');
+require('html_form_helpers.php');
+require('html_table_helpers.php');
// autoloading of classes
function __autoload($class_name) {
View
0  config.php
No changes.
View
0  controller.php 100644 → 100755
File mode changed
View
0  framework.php 100644 → 100755
File mode changed
View
75 html_form.php 100644 → 100755
@@ -1,26 +1,73 @@
<?
class HTMLForm extends MicroObject{
-
- function __construct($object, $fields, $url='', $method = 'POST'){
- $this->object = $object;
+ /**
+ * $fields contains a hash with field names and field properties:
+ * name : a name for the field
+ * id [optional] : an html id, default: name
+ * type [optional] default: string, supported (extensible): string, password, textarea
+ * value [optional] default: null
+ * title [optional] default: field name
+ * error [optional] default: null
+ */
+ function __construct($fields, $url='', $method = 'POST'){
$this->fields = $fields;
$this->url = $url;
$this->method = $method;
- $this->db_fields = $object->fields();
-
- $this->render_prefix();
+ }
+
+ function render_all(){
+ $this->render_form_prefix();
$this->render_fields();
- $this->render_suffix();
+ $this->render_form_suffix();
+ $this->render_field_extras();
}
- function render_prefix(){echo html_form_open($this->url, $this->method);}
+ function render_form_prefix(){echo "\n".html_form_open($this->url, $this->method);}
function render_fields(){
- foreach($this->fields as $fieldname => $field){
- if(!$this->do_callback("render_$fieldname")
- && !$this->do_callback("render_field", $field)){
- echo html_input($fieldname, $object->$fieldname());
- }
+ echo "\n";
+ foreach($this->fields as $props){
+ if(!$this->done_by_callback('input', $props)) echo 'dunno';
+ echo "\n";
}
}
- function render_suffix(){echo '</form>';}
+
+ function done_by_callback($part, $props){
+ return $this->do_callback("render_{$props['name']}_{$part}", $props)
+ || $this->do_callback("render_".array_try($props, 'type', 'string')."_{$part}", $props)
+ || $this->do_callback("render_{$part}", $props);
+ }
+
+ function render_string_input($props){
+ $id = array_try($props, 'id', $props['name']);
+ echo '<p>';
+ echo html_label($id, array_try($props, 'title', util_humanize($props['name'])));
+ echo html_input($props['name'], array_try($props, 'value', ''), array('id' => $id));
+ if($error = array_try($props, 'error', false))
+ echo html_span($error, array('class'=>'error'));
+ echo '</p>';
+ }
+ function render_password_input($props){
+ $id = array_try($props, 'id', $props['name']);
+ echo '<p>';
+ echo html_label($id, array_try($props, 'title', util_humanize($props['name'])));
+ echo html_password($props['name'], array('id' => $id));
+ if($error = array_try($props, 'error', false))
+ echo html_span($error, array('class'=>'error'));
+ echo '</p>';
+ }
+ function render_textarea_input($props){
+ $id = array_try($props, 'id', $props['name']);
+ echo '<p class="textarea">';
+ echo html_label($id, array_try($props, 'title', util_humanize($props['name'])));
+ if($error = array_try($props, 'error', false))
+ echo html_span($error, array('class'=>'error'));
+ echo html_textarea($props['name'], array_try($props, 'value', ''), array('id' => $id));
+ echo '</p>';
+ }
+ function render_form_suffix(){echo "</form>\n";}
+
+ function render_field_extras(){
+ foreach($this->fields as $props)
+ $this->done_by_callback('extras', $props);
+ }
}
View
19 html_form_helpers.php
@@ -0,0 +1,19 @@
+<?
+function f_string($object, $field, $title = null){
+ return array('name' => get_class($object)."[$field]",
+ 'id' => util_under_score(get_class($object))."-$field",
+ 'title' => ($title ? $title : util_humanize($field)),
+ 'value' => $object->attributes[$field],
+ 'error' => array_try($object->errors, $field, '')
+ );
+}
+
+function f_password($object, $field, $title = null){
+ return array_merge(array('type' => 'password'),
+ f_string($object, $field, $title));
+}
+
+function f_textarea($object, $field, $title = null){
+ return array_merge(array('type' => 'textarea'),
+ f_string($object, $field, $title));
+}
View
80 html_table.php 100644 → 100755
@@ -1,46 +1,82 @@
<?
class HTMLTable extends MicroObject{
-
+ /**
+ * $columns contains a hash with column properties:
+ * column : the name of the column
+ * type [optional] default: string, supported (extensible): string, icon, link
+ * title [optional] default: column
+ * for type icon (is also a link by default):
+ * icon_name [optional] default: column
+ * link_field [optional] default: $row[id]
+ * link_as [optional] default: id
+ * url
+ * for type link:
+ * link_field [optional] default: $row[id]
+ * link_as [optional] default: id
+ * url
+ * rows is an array of arrays
+ */
function __construct($columns, $rows=array()){
- $this->columns = array_flatten($columns);
+ $this->columns = $columns;
$this->rows = $rows;
-
- $this->render_prefix();
+ }
+
+ function render_all(){
+ echo '<table>';
$this->render_cols();
$this->render_head();
$this->render_body();
- $this->render_suffix();
+ echo '</table>';
+ }
+
+ function done_by_callback($part, $props){
+ return $this->do_callback("render_{$props['column']}_{$part}", $props)
+ || $this->do_callback("render_".array_try($props, 'type', 'string')."_{$part}", $props)
+ || $this->do_callback("render_{$part}", $props);
}
- function render_prefix(){echo '<table>';}
function render_cols(){
- foreach($this->columns as $column){
- if(!($this->do_callback("render_{$column}_col")
- || $this->do_callback("render_col", $column)))
+ foreach($this->columns as $props){
+ if(!$this->done_by_callback("col", $props))
echo '<col/>';
}
}
function render_head(){
echo '<thead><tr>';
- foreach($this->columns as $column){
- if(!($this->do_callback("render_{$column}_header")
- || $this->do_callback("render_header", $column)))
- echo "<th>$column</th>";
+ foreach($this->columns as $props){
+ if(!$this->done_by_callback("header", $props))
+ echo "<th>".array_try($props, 'title', $props['column'])."</th>";
}
echo '</tr></thead>';
}
function render_body(){
- echo '<tbody>';
+ echo "\n<tbody>\n";
foreach($this->rows as $row){
- if(!$this->do_callback("render_row_prefix") echo '<tr>';
- foreach($this->columns as $column){
- if(!($this->do_callback("render_{$column}_cell" $row)
- || $this->do_callback("render_cell", $column, $row)))
- echo "<td>{$row[$column]}</td>";
+ echo '<tr>';
+ foreach($this->columns as $props){
+ if(!($this->do_callback("render_{$props['column']}_cell", $props, $row)
+ || $this->do_callback("render_".array_try($props, 'type', 'string')."_cell", $props, $row)
+ || $this->do_callback("render_cell", $props, $row)))
+ echo "<td>".$row[$props['column']]."</td>";
}
- if(!$this->do_callback("render_row_suffix") echo '</tr>';
+ echo "</tr>\n";
}
- echo '</tr></thead>';
+ echo "</tbody>\n";
+ }
+
+ function render_icon_cell($props, $row){
+ echo "<td>".linkify(
+ iconify(array_try($props, 'icon_name', $props['column'])),
+ $props['url'],
+ array(array_try($props, 'link_as', 'id')
+ => $row[array_try($props, 'link_field', 'id')]
+ ))."</td>";
+ }
+
+ function render_link_cell($props, $row){
+ echo "<td>".linkify($row[$props['column']], $props['url'],
+ array(array_try($props, 'link_as', 'id')
+ => $row[array_try($props, 'link_field', 'id')]
+ ))."</td>";
}
- function render_suffix(){echo '</table>';}
}
View
18 html_table_helpers.php
@@ -0,0 +1,18 @@
+<?
+function t_string($column, $title = null){
+ return array('column' => $column,
+ 'title' => ($title ? $title : util_humanize($column))
+ );
+}
+
+function t_link($column, $url, $title = null){
+ return array_merge(
+ array('type' => 'link', 'url' => $url),
+ t_string($column, $title));
+}
+
+function t_icon($column, $url, $title = null){
+ return array_merge(
+ t_string($column),
+ array('type' => 'icon', 'url' => $url, 'title' => ($title ? $title : '')));
+}
View
45 html_utils.php 100644 → 100755
@@ -7,13 +7,14 @@ function html_dl($items, $attributes=array()){
}
function html_input($name, $value, $attributes=array()){
- $attributes['name'] = $name;
- $attributes['value'] = $value;
+ $attributes = array_merge(
+ array('name' => $name, 'value' => $value), $attributes);
+
return '<input type="text" '.xml_attributify($attributes).'>';
}
function html_password($name, $attributes=array()){
- $attributes['name'] = $name;
+ $attributes = array_merge(array('name' => $name), $attributes);
return '<input type="password" '.xml_attributify($attributes).'>';
}
@@ -28,43 +29,59 @@ function html_select($name, $options, $selected=false, $attributes=array()){
if($selected && $selected == $key) $option_attributes['selected'] = 'selected';
$result[] = '<option '.xml_attributify($option_attributes).">$value</option>";
}
- $attributes['name'] = $name;
+ $attributes = array_merge(array('name' => $name), $attributes);
return '<select '.xml_attributify($attributes).'>'.implode("\n", $result)."</select>";
}
+function html_textarea($name, $value, $attributes = array()){
+ $attributes = array_merge(array(
+ 'rows' => 10, 'cols'=>60, 'name'=> $name), $attributes);
+
+ return '<textarea '.xml_attributify($attributes).'>'.$value.'</textarea>';
+}
+
function html_span($content, $attributes=array()){
return '<span '.xml_attributify($attributes).'>'.$content.'</span> ';
}
function html_form_open($url, $method='POST', $attributes=array()){
- $attributes['action'] = $url;
- $attributes['method'] = $method;
+ $attributes = array_merge(
+ array('action' => $url, 'method' => $method), $attributes);
return '<form '.xml_attributify($attributes).'>';
}
+function html_label($for, $text, $attributes = array()){
+ $attributes = array_merge(
+ array('for' => $for), $attributes);
+ return '<label '.xml_attributify($attributes).'>'.$text.'</label> ';
+}
function html_js_link($file, $attributes=array()){
if(substr($file, -3) != '.js') $file .= '.js';
- $attributes['src'] = URL_BASE."/public/$file";
- $attributes['type'] = 'text/javascript';
- $attributes['charset'] = 'utf-8';
+ $attributes = array_merge(
+ array('src' => URL_BASE."/public/$file",
+ 'type' => 'text/javascript',
+ 'charset' => 'utf-8'), $attributes);
+
return '<script '.xml_attributify($attributes).'></script>';
}
function html_css_link($file, $attributes=array()){
if(substr($file, -4) != '.css') $file .= '.css';
- $attributes['rel'] = "stylesheet";
- $attributes['href'] = URL_BASE."/public/$file";
- $attributes['type'] = "text/css";
+ $attributes = array_merge(array(
+ 'rel' => "stylesheet",
+ 'href' => URL_BASE."/public/$file",
+ 'type' => "text/css"), $attributes);
+
return '<link '.xml_attributify($attributes).'/>';
}
function linkify($text, $path, $params = array(), $attributes = array()){
- return "<a href=\"".urlify($path, $params)."\" ".attributify($attributes).">$text</a>";
+ return "<a href=\"".urlify($path, $params)."\" ".xml_attributify($attributes).">$text</a>";
}
function iconify($icon, $attributes=array()){
$path = URL_BASE."/public/icons/$icon.png";
- $attributes['src'] = $path;
+ $attributes = array_merge(array('src' => $path), $attributes);
return '<img '.xml_attributify($attributes).'/>';
}
View
0  micro_object.php 100644 → 100755
File mode changed
View
74 model.php 100644 → 100755
@@ -43,9 +43,21 @@ public static function find($model, $conditions = null, $orderby = null){
return self::get_results($resultset, $model);
}
- public static function find_by_sql($model, $sql){
- $resultset = self::do_query($sql);
- return self::get_results($resultset, $model);
+ public static function find_by_sql($sql){
+ $args = array_slice(func_get_args(), 1);
+ return self::get_results(self::find_by_sql_array($sql, $args), "Model");
+ }
+
+ public static function find_by_sql_array($sql, $args = array()){
+ return self::find_by_sql_as_type('Model', $sql, $args);
+ }
+
+ public static function find_by_sql_as_type($type, $sql, $args = array()){
+ if(!empty($args)){
+ foreach($args as $arg)
+ $sql = preg_replace('/\?/', self::make_value($arg), $sql, 1);
+ }
+ return self::get_results(self::do_query($sql), $type);
}
function first($model, $conditions = null, $orderby = null) {
@@ -69,13 +81,19 @@ public static function delete_where($model, $conditions){
return self::do_query("DELETE FROM " . $table . " WHERE " . $condition_string);
}
- //get fields for a model
+ // empties a particular table
+ public static function truncate($model){
+ $table = self::table_for($model);
+ return self::do_query("TRUNCATE TABLE " . $table);
+ }
+
+ // get fields for a model
public static function fields($model){
$resultset = self::do_query("SHOW COLUMNS FROM " . self::table_for($model));
return self::get_results_array($resultset, 'Field');
}
- //get fields for a model
+ // get fields for a model
public static function field_names($model){
$fields = self::fields($model);
return array_keys($fields);
@@ -118,31 +136,35 @@ protected static function do_query($sql){
protected static function make_conditions($conditions){
$sql_conditions = array();
-
+
foreach($conditions as $key => $value){
if(is_array($value)){
- $values = array();
- foreach($value as $part) $values[] = self::make_value($part);
- $sql_conditions[] = "$key IN (".implode(",", $values).")";
+ $sql_conditions[] = "$key IN ".self::make_value($value);
} else {
$matches = array();
- if (ereg("^(<|>|>=|<=|LIKE) ", $value, $matches))
- $sql_conditions[] = "$key ".reset($matches)." ".
- self::make_value(substr($value, strlen(reset($matches))));
- else $sql_conditions[] = "$key =" . self::make_value($value);
+ if (is_string($value) && ereg("^(<>|!=|<|>|>=|<=|LIKE) ", $value, $matches))
+ $sql_conditions[] = "$key ".$matches[0].self::make_value(substr($value, strlen($matches[0])));
+ else $sql_conditions[] = "$key = " . self::make_value($value);
}
}
-
+
return join(" AND ", $sql_conditions);
}
protected static function make_value($value){
+ if(is_array($value)){
+ $parts = array();
+ foreach($value as $part) $parts[] = self::make_value($part);
+ return "(".implode(', ', $parts).")";
+ } else if (is_a($value, 'Model'))
+ return $value->get_id();
+
return "'" . Framework::$db->escape_string($value) . "'";
}
private static function table_for($model){
$reflect = new ReflectionClass($model);
- return $reflect->getConstant('table');
+ return $reflect->getStaticPropertyValue('table');
}
public $attributes = array();
@@ -182,11 +204,11 @@ public function save(){
//insert or update
if (isset($this->attributes['id'])){
- $this->dbupdate($datavalues, $this->id());
+ $this->dbupdate($datavalues, $this->get_id());
$result = true;
} else {
$result = $this->dbinsert($datavalues);
- $this->attributes['id'] = $result;
+ $this->set_id($result);
}
$this->do_callback("after_save");
@@ -216,11 +238,19 @@ protected function dbinsert($array){
//-----------------------------------------
// metaprogramming
//-----------------------------------------
- function __call($name, $arguments){
- if(empty($arguments) && isset($this->attributes[$name])) return $this->attributes[$name];
- else throw new ErrorException('Call to undefined function');
- }
+ //get/set attributes
+ public function __call($name, $arguments){
+ $parts = explode('_', $name);
+ if(count($parts) > 1){
+ if($parts[0] == 'get' && count($arguments == 0))
+ return $this->attributes[implode('_', array_slice($parts, 1))];
+ else if($parts[0] == 'set' && count($arguments == 1))
+ $this->attributes[implode('_', array_slice($parts, 1))] = $arguments[0];
+ else throw new Exception('undefined method or wrong number of arguments');
+ } else throw new Exception('undefined method');
+ }
+
//-----------------------------------------
//validation
//-----------------------------------------
@@ -242,8 +272,6 @@ public function require_field_length($field, $length){
$this->errors[$field] = "This field should be longer than " . $length;
}
}
-
-
}
View
10 model_utils.php 100644 → 100755
@@ -3,8 +3,14 @@ function find($model, $conditions = array(), $order=""){
return Model::find($model, $conditions, $order);
}
-function find_by_sql($model, $sql){
- return Model::find_by_sql($model, $sql);
+function find_by_sql($sql){
+ $args = array_slice(func_get_args(), 1);
+ return Model::find_by_sql_array($sql, $args);
+}
+
+function find_by_sql_as_type($type, $sql){
+ $args = array_slice(func_get_args(), 2);
+ return Model::find_by_sql_as_type($type, $sql, $args);
}
function first($model, $conditions = null, $orderby = null) {
View
0  test/controller_test.php 100644 → 100755
File mode changed
View
7 test/framework_test.php 100644 → 100755
@@ -4,6 +4,13 @@
* a table called test_table with no records
*/
+class Test extends Model {
+ public static $table = 'test_table';
+}
+
+//empty test db
+Model::Truncate('Test');
+
require 'model_test.php';
require 'controller_test.php';
require 'view_helpers_test.php';
View
7 test/model_test.php 100644 → 100755
@@ -1,9 +1,4 @@
<?
-
-class Test extends Model {
- const table = 'test_table';
-}
-
// a simple scenario
log_info("###### test 1: see if table is empty");
$result = find('Test');
@@ -16,7 +11,7 @@ class Test extends Model {
assert('!empty($test2)');
log_info("###### test 3: attribute getters");
-assert('$test2->id() == $test2->attributes[\'id\']');
+assert('$test2->get_id() == $test2->attributes[\'id\']');
log_info("###### test 4: update the object");
$test2->update_attributes(array('name'=>'ha'));
View
31 test/view_helpers_test.php 100644 → 100755
@@ -0,0 +1,31 @@
+<?
+// make some test data
+foreach(array(
+ new Test(array('name' => 'bla', 'name2' => 'blabla')),
+ new Test(array('name' => 'blaaa', 'name2' => 'blab')),
+ new Test(array('name' => 'blaa3', 'name2' => 'blab')),
+ new Test(array('name' => 'blaa3', 'name2' => 'blb')),
+ ) as $object) {
+ $object->save();
+}
+
+// test form
+$object = reset(find('Test'));
+$f = new HTMLForm(array(
+ f_string($object, 'name'),
+ f_password($object, 'name2'),
+ f_textarea($object, 'name2'),
+ ));
+
+$f->render_all();
+
+// test table
+$objects = find('Test');
+$t = new HTMLTable(array(
+ t_string('name'),
+ t_link('name', '/tests/test'),
+ t_icon('table', '/tests/test')
+ ), collection_get_property($objects, 'attributes'));
+
+$t->render_all();
+
View
22 utils.php 100644 → 100755
@@ -1,5 +1,5 @@
<?
-// core extensions
+######### core extensions
function array_insert($array, $element, $position){
if($position > count($array) || $position < 0)
throw new Exception('insert out of bounds');
@@ -40,7 +40,23 @@ function array_accept($array){
return $result;
}
-// framework-specific
+######### shortcuts for common tasks
+
+// retrieve a variable from each object in array
+function collection_get_property($array, $property){
+ $result = array();
+ foreach($array as $object) $result[] = $object->$property;
+ return $result;
+}
+
+// retrieve the result of a function call on each object in array
+function collection_get_result($array, $function){
+ $result = array();
+ foreach($array as $object) $result[] = $object->$function();
+ return $result;
+}
+
+######### framework-specific
function util_capitalize($string){
$elements = explode('_', $string);
$result = "";
@@ -81,7 +97,7 @@ function urlify($path, $params= array()){
return URL_BASE.'/index.php'.$path.(empty($encoded)?'':'?').implode('&', $encoded);
}
-// application level:
+######### application level:
function log_info(){
foreach(func_get_args() as $string){
if(is_string($string))
Please sign in to comment.
Something went wrong with that request. Please try again.