dbscript
[ class tree: dbscript ] [ index: dbscript ] [ all elements ]

Source for file model.php

Documentation is available at model.php

  1. <?php
  2.  
  3.   /** 
  4.    * dbscript for PHP 4 & 5 - restful crud framework
  5.    * @version 0.1.2 -- 19-Feb-2007
  6.    * @author Brian Hendrickson <brian@dbscript.net>
  7.    * @link http://dbscript.net/
  8.    * @copyright Copyright 2007 Brian Hendrickson
  9.    * @package dbscript
  10.    * @license http://www.opensource.org/licenses/mit-license.php MIT License
  11.    */
  12.  
  13.   /**
  14.    * Data Model
  15.    * 
  16.    * Describes a database table: fields, rules and relationships.
  17.    *
  18.    * Automatically composes simple JOIN queries for relationships
  19.    * described in the models via has_many, has_one, etc.
  20.    * 
  21.    * Usage:
  22.    * <code>
  23.    *   // define a new table named photos
  24.    * $model =& $db->model( 'photo' );
  25.    *
  26.    *   // define the fields in the table
  27.    * $model->auto_field( 'id' );
  28.    * $model->file_field( 'image' );
  29.    * $model->char_field( 'title', 255 );
  30.    * $model->text_field( 'caption' );
  31.    *
  32.    *   // create the table in the database
  33.    * $model->save();
  34.    *
  35.    *   // deny access to everybody, unless they are an administrator
  36.    * $model->let_access( 'all:never all:admin' );
  37.    *
  38.    *   // function to test whether current user is an administrator
  39.    * function admin() {
  40.    *   return true;
  41.    * }
  42.    * </code>
  43.    * 
  44.    * More info...
  45.    * {@link http://dbscript.net/model}
  46.    * 
  47.    * @package dbscript
  48.    * @author Brian Hendrickson <brian@dbscript.net>
  49.    * @access public
  50.    * @version 0.1.2
  51.    */
  52.  
  53. class Model {
  54.   
  55.   /**
  56.    * name of the database table
  57.    * @var string 
  58.    */
  59.   var $table;
  60.   
  61.   /**
  62.    * true if table exists in database
  63.    * @var boolean 
  64.    */
  65.   var $exists;
  66.   
  67.   /**
  68.    * list of field names
  69.    * @var string[] 
  70.    */
  71.   var $field_array;
  72.   
  73.   /**
  74.    * list of field attributes
  75.    * @var string[] 
  76.    */
  77.   var $field_attrs;
  78.   
  79.   /**
  80.    * list of security access rules
  81.    * @var string[] 
  82.    */
  83.   var $access_list;
  84.   
  85.   /**
  86.    * name of primary key field
  87.    * @var string 
  88.    */
  89.   var $primary_key;
  90.     
  91.   /**
  92.    * name of collection key field
  93.    * @var string 
  94.    */
  95.   var $uri_key;
  96.   
  97.   /**
  98.    * list of relationships to other tables
  99.    * @var string[] 
  100.    */
  101.   var $relations;
  102.   
  103.   /**
  104.    * proper CamelCase name of data model object
  105.    * @var string 
  106.    */
  107.   var $custom_class;
  108.   
  109.   /**
  110.    * list of public methods
  111.    * @var string[] 
  112.    */
  113.   
  114.   /**
  115.    * when querying with find(), offset by x records
  116.    * @var integer 
  117.    */
  118.   var $offset;
  119.  
  120.   /**
  121.    * limit query to x records
  122.    * @var integer 
  123.    */
  124.   var $limit;
  125.  
  126.   /**
  127.    * order query by this column
  128.    * @var string 
  129.    */
  130.   var $orderby;
  131.  
  132.   /**
  133.    * order query ASC or DESC
  134.    * @var string 
  135.    */
  136.   var $order;
  137.   
  138.   /**
  139.    * list of Collection/Feed params for layout
  140.    * @var string[] 
  141.    */
  142.   var $params;
  143.     
  144.   function insert_from_post&$req {
  145.     $db =db_object();
  146.     $fields $this->fields_from_request($req);
  147.     foreach ($fields as $table=>$fieldlist{
  148.       // for each table in the submission do
  149.       $pkfield $db->models[$table]->primary_key;
  150.       $rec $db->models[$table]->base();
  151.       foreach $fieldlist as $field=>$type )
  152.         $rec->set_value$field$req->params[strtolower(classify($table))][$field);
  153.       $result $rec->save_changes();
  154.       if $result {
  155.         $atomentry $db->models['entries']->base();
  156.         $atomentry->set_value'etag'getEtag$rec->$pkfield ) );
  157.         $atomentry->set_value'resource'$table );
  158.         $atomentry->set_value'record_id'$rec->$pkfield );
  159.         $atomentry->set_value'content_type''text/html' );
  160.         $aresult $atomentry->save_changes();
  161.         if ($aresult)
  162.           $rec->save_changes();
  163.       else {
  164.         trigger_error"The record could not be saved into the database."E_USER_ERROR );
  165.       }
  166.     }
  167.   }
  168.   
  169.   function update_from_post&$req {
  170.     $db =db_object();
  171.     $fields $this->fields_from_request($req);
  172.     $atomentry $db->models['entries']->find_by'etag'$req->params['entry']['etag');
  173.     if (!$atomentry)
  174.       trigger_error"The entry was not found in the database."E_USER_ERROR );
  175.     $rec $db->models[$atomentry->resource]->find$atomentry->record_id );
  176.     if (!$rec)
  177.       trigger_error"The record was not found in the database."E_USER_ERROR );
  178.     foreach ($fields[$rec->tableas $field=>$type)
  179.       $rec->set_value$field$req->params[strtolower(classify($rec->table))][$field);
  180.     $result $rec->save_changes();
  181.     foreach ($fields as $table=>$fieldlist{
  182.       // for each table in the submission do
  183.       if (!(in_array$tablearray('entries',$rec->table)true ))) {
  184.         $rel $rec->FirstChild$table );
  185.         foreach ($fieldlist as $field=>$type)
  186.           $rel->set_value$field$req->params[strtolower(classify($table))][$field);
  187.         $rel->save_changes();
  188.       }
  189.     }
  190.     if ($result{
  191.       $atomentry->set_value'last_modified'timestamp() );
  192.       $atomentry->save_changes();
  193.     else {
  194.       trigger_error"The record could not be updated in the database."E_USER_ERROR );
  195.     }
  196.   }
  197.   
  198.   function delete_from_post&$req {
  199.     return false;
  200.   }
  201.   
  202.   function fields_from_request&$req {
  203.     $db =db_object();
  204.     $fields array();
  205.     $obj strtolower(classify($this->table));
  206.     foreach ($this->field_array as $fieldname=>$datatypename{
  207.       if (isset($req->params[$obj][$fieldname])) {
  208.         $fields[$this->table][$fieldname$datatypename;
  209.       }
  210.     }
  211.     foreach ($this->field_array as $fieldname=>$datatypename{
  212.       if (isset($_FILES[$obj]['name'][$fieldname])) {
  213.         $fields[$this->table][$fieldname$datatypename;
  214.         $req->params[$obj][$fieldname$_FILES[$obj]['tmp_name'][$fieldname];
  215.       }
  216.     }
  217.     foreach ($this->relations as $table=>$vals{
  218.       if isset$db->models[$table)) {
  219.         $obj strtolower(classify($table));
  220.         foreach $db->models[$table]->field_array as $fieldname=>$datatypename {
  221.           if (!($table == 'entries'&& isset($req->params[$obj][$fieldname]))
  222.             $fields[$table][$fieldname$datatypename;
  223.         }
  224.         foreach $db->models[$table]->field_array as $fieldname=>$datatypename {
  225.           if (!($table == 'entries'&& isset($_FILES[$obj]['name'][$fieldname])){
  226.             $fields[$table][$fieldname$datatypename;
  227.             $req->params[$obj][$fieldname$_FILES[$obj]['tmp_name'][$fieldname];
  228.           }
  229.         }
  230.       }
  231.     }
  232.     return $fields;
  233.   }
  234.   
  235.   function db_field$field$alias {
  236.     if (!(isset($this->$alias&& isset($this->data_array[$field])))
  237.       $this->$alias =$this->data_array[$field];
  238.   }
  239.   
  240.   function exists({
  241.     return count$this->field_array );
  242.   }
  243.  
  244.   function base$custom_class NULL {
  245.     $db =db_object();
  246.     $func_args func_get_args();
  247.     if (count($func_args== && $custom_class == NULL )
  248.       $custom_class NULL;
  249.     elseif (isset($this->custom_class))
  250.       $custom_class $this->custom_class;
  251.     elseif (class_exists(classify($this->table)))
  252.       $custom_class classify($this->table);
  253.  
  254.     if ($custom_class{
  255.       if (class_exists($custom_class)) {
  256.         $this->custom_class = $custom_class;
  257.         $obj new $custom_class$this );
  258.         if (!$objtrigger_error("error instantiating $custom_class"E_USER_ERROR );
  259.         $obj->Record$this->table$db );
  260.         return $obj;
  261.       else {
  262.         trigger_error("errorclass $custom_class not found"E_USER_ERROR )
  263.       }
  264.     }
  265.     $this->custom_class = NULL;
  266.     return new Record($this->table,$db);
  267.   }
  268.   
  269.   function register$custom_class {
  270.     if (class_exists($custom_class)) {
  271.       $this->custom_class = $custom_class;
  272.       $obj new $custom_class$this );
  273.     }
  274.   }
  275.   
  276.   function set_field$field$data_type$arr=NULL {
  277.     $this->field_array[$field$data_type;
  278.     if (!($arr == NULL))
  279.       $this->field_attributes$field$arr );
  280.   }
  281.   
  282.   function set_uri_key$field {
  283.     $this->uri_key = $field;
  284.   }
  285.   
  286.   function set_primary_key$field {
  287.     $this->primary_key = $field;
  288.     if (!$this->uri_key)
  289.       $this->uri_key = $field;
  290.   }
  291.   
  292.   function field_attributes$field$arr {
  293.     $this->set_attribute$arr$field );
  294.   }
  295.   
  296.   function let_access$fields {
  297.     $this->let_read$fields );
  298.     $this->let_write$fields );
  299.     $this->let_display$fields );
  300.   }
  301.   
  302.   function let_read$fields {
  303.     $args explode" "$fields );
  304.     if (!(count($args)>0)) trigger_error"invalid data model access rule"E_USER_ERROR );
  305.     foreach $args as $str{
  306.       $pair split":"$str );
  307.       if (!(count($pair)==2)) trigger_error"invalid data model access rule"E_USER_ERROR );
  308.       if ($pair[0== 'all'{
  309.         foreach $this->field_array as $field => $data_type {
  310.           $this->access_list['read'][$field][$pair[1];
  311.         }
  312.       else {
  313.         $this->access_list['read'][$pair[0]][$pair[1];
  314.       }
  315.     }
  316.   }
  317.   
  318.   function let_write$fields {
  319.     $args explode" "$fields );
  320.     if (!(count($args)>0)) trigger_error"invalid data model access rule"E_USER_ERROR );
  321.     foreach $args as $str{
  322.       $pair split":"$str );
  323.       if (!(count($pair)==2)) trigger_error"invalid data model access rule"E_USER_ERROR );
  324.       if ($pair[0== 'all'{
  325.         foreach $this->field_array as $field => $data_type {
  326.           $this->access_list['write'][$field][$pair[1];
  327.         }
  328.       else {
  329.         $this->access_list['write'][$pair[0]][$pair[1];
  330.       }
  331.     }
  332.   }
  333.   
  334.   function let_display$fields {
  335.     $args explode" "$fields );
  336.     if (!(count($args)>0)) trigger_error"invalid data model access rule"E_USER_ERROR );
  337.     foreach $args as $str{
  338.       $pair split":"$str );
  339.       if (!(count($pair)==2)) trigger_error"invalid data model access rule"E_USER_ERROR );
  340.       if ($pair[0== 'all'{
  341.         foreach $this->field_array as $field => $data_type {
  342.           $this->access_list['display'][$field][$pair[1];
  343.         }
  344.       else {
  345.         $this->access_list['display'][$pair[0]][$pair[1];
  346.       }
  347.     }
  348.   }
  349.  
  350.   function set_action$method {
  351.     $this->allowed_methods[$method;
  352.   }
  353.     
  354.   function set_param$param$value {
  355.     $this->params[$param$value;
  356.   }
  357.   
  358.   function is_allowed$method {
  359.     if is_callablearray$this->custom_class$method ) ) ) {
  360.       $obj $this->base$this->custom_class );
  361.       if ($obj)
  362.         return in_array$method$obj->allowed_methodstrue );
  363.     }
  364.     return in_array$method$this->allowed_methodstrue );
  365.     return false;
  366.   }
  367.  
  368.   function has_and_belongs_to_many$relstring {
  369.     $this->set_relation$relstring'child-many' );
  370.   }
  371.  
  372.   function belongs_to$relstring {
  373.     $this->set_relation$relstring'child-one' );
  374.   }
  375.   
  376.   function has_many$relstring {
  377.     $this->set_relation$relstring'parent-many' );
  378.   }
  379.   
  380.   function has_one$relstring {
  381.     $this->set_relation$relstring'parent-one' );
  382.   }
  383.   
  384.   function validates_presence_of$field {
  385.     $this->set_attribute'required'$field );
  386.   }
  387.   
  388.   function validates_uniqueness_of$field {
  389.     $this->set_attribute'unique'$field );
  390.   }
  391.   
  392.   function set_attribute$attr$field {
  393.     if (is_array($attr))
  394.       $this->field_attrs[$field]['values'$attr;
  395.     else
  396.       $this->field_attrs[$field][$attrtrue;
  397.   }
  398.   
  399.   function foreign_key_for$table {
  400.     $db =db_object();
  401.     if (strtolower(classify($this->table)) == 'entry')
  402.       return 'record_id';
  403.     if (!(isset($db->models[$table]))) {
  404.       $fields $db->get_fields$table );
  405.       if (isset($fields[$table."_primary_key"]))
  406.         $pk $fields[$table."_primary_key"];
  407.       else
  408.         $pk 'id';
  409.     else {
  410.       $fields =$db->models[$table]->field_array;
  411.       $pk $db->models[$table]->primary_key;
  412.     }
  413.     if in_arraystrtolower(classify($this->table))."_".$this->primary_key$fields ))
  414.       return strtolower(classify($this->table))."_".$this->primary_key;
  415.     else
  416.       return $pk;
  417.   }
  418.   
  419.   function join_table_for$t1$t2 {
  420.     if ($t1 $t2)
  421.       return $t1 "_" $t2;
  422.     else
  423.       return $t2 "_" $t1;
  424.   }
  425.   
  426.   function set_relation$relstring$type {
  427.     $db =db_object();
  428.     $f split(":",$relstring);
  429.     if (count($f)==2{
  430.       $k $f[0];
  431.       $fk $f[1];
  432.     else {
  433.       $k $this->primary_key;
  434.       $fk $relstring;
  435.     }
  436.     $fo split("\.",$fk);
  437.     if (count($fo)==2{
  438.       $table tableize($fo[0]);
  439.       $fkk $fo[1];
  440.     else {
  441.       $table tableize($fk);
  442.       $fk $table.".".$this->foreign_key_for$table );
  443.     }
  444.     if ($type == 'child-many'{
  445.       if (!$db->table_exists($this->join_table_for($table$this->table))) {
  446.         $join =$db->get_table($this->join_table_for($table$this->table));
  447.         if (!($join->exists)) {
  448.           $join->int_fieldstrtolower(classify($this->table))."_".$k );
  449.           $join->int_fieldstrtolower(classify($table))."_".$this->foreign_key_for$table) );
  450.           $join->save();
  451.         }
  452.       }
  453.     }
  454.     $this->relations[$table]['type'$type;
  455.     $this->relations[$table]['col'$k;
  456.     $this->relations[$table]['fkey'$fk;
  457.     $this->relations[$table]['tab'$table;
  458.   }
  459.   
  460.   function can_read_fields$fields {
  461.     $return false;
  462.     foreach$fields as $key=>$val {
  463.       if $this->can_read$key ) ) {
  464.         $return true;
  465.       else {
  466.         return false;
  467.       }
  468.     }
  469.     return $return;
  470.   }
  471.  
  472.   function can_write_fields$fields {
  473.     $return false;
  474.     foreach$fields as $key=>$val {
  475.       if $this->can_write$key ) ) {
  476.         $return true;
  477.       else {
  478.         return false;
  479.       }
  480.     }
  481.     return $return;
  482.   }
  483.   
  484.   function can_display_fields$fields {
  485.     $return false;
  486.     foreach$fields as $key=>$val {
  487.       if $this->can_display$key ) ) {
  488.         $return true;
  489.       else {
  490.         return false;
  491.       }
  492.     }
  493.     return $return;
  494.   }
  495.   
  496.   function can_read$resource {
  497.     if (!(isset($this->access_list['read'][$resource]))) return false;
  498.     foreach $this->access_list['read'][$resourceas $callback {
  499.       if function_exists$callback ) ) {
  500.         if ($callback()) {
  501.           return true;
  502.         }
  503.       }
  504.     }
  505.     return false;
  506.   }
  507.   
  508.   function can_write$resource {
  509.     if (!(isset($this->access_list['write'][$resource]))) return false;
  510.     foreach $this->access_list['write'][$resourceas $callback {
  511.       if function_exists$callback ) ) {
  512.         if ($callback()) {
  513.           return true;
  514.         }
  515.       }
  516.     }
  517.     return false;
  518.   }
  519.  
  520.   function can_insert({
  521.     $return true;
  522.     return $return;
  523.   }
  524.   
  525.   function can_delete({
  526.     $return true;
  527.     return $return;
  528.   }
  529.   
  530.   function can_display$resource {
  531.     
  532.     if (!(isset($this->access_list['read'][$resource]))) return false;
  533.     foreach $this->access_list['read'][$resourceas $callback {
  534.       if function_exists$callback ) ) {
  535.         if ($callback()) {
  536.           return true;
  537.         }
  538.       }
  539.     }
  540.     return false;
  541.   }
  542.  
  543.   function rewind({
  544.     $this->MoveFirst();
  545.   }
  546.  
  547.   function set_routes($table{
  548.     $req =request_object();
  549.     if (!(isset($req->activeroute)))
  550.       return;
  551.     $req->connect(
  552.       $table,
  553.       ':resource',
  554.       array(
  555.         'requirements' => array '[a-z]+' ),
  556.         'resource' => $table
  557.       )
  558.     );
  559.     $req->connect(
  560.       classify($table),
  561.       ':resource/:id',
  562.       array(
  563.         'requirements' => array '[a-z]+''[0-9]+' ),
  564.         'resource' => $table,
  565.         'id' => $req->id
  566.       )
  567.     );
  568.   }
  569.   
  570.   function save({
  571.     $db =db_object();
  572.     trigger_before'save'$this$db );
  573.     if (!(isset($db->tables)))
  574.       $db->tables $db->get_tables();
  575.     if !in_array$this->table$db->tables ) ) ) {
  576.       if (count($this->field_array)>0{
  577.         if (!(isset($this->primary_key)))
  578.           $this->auto_field'id' );
  579.         $db->add_table$this->table$this->field_array );
  580.       else {
  581.         return NULL;
  582.       }
  583.     }
  584.     
  585.     // schema sync
  586.     #$fields = $db->get_fields( $this->table );
  587.     #foreach ( $this->field_array as $field => $data_type ) {
  588.     #  if ( !( array_key_exists( $field, $fields ) ) ) {
  589.     #    $db->add_field( $this->table, $field, $data_type );
  590.     #  }
  591.     #}
  592.     #if ( !( isset( $this->primary_key ))) {
  593.     # if (isset($fields[$this->table."_primary_key"]))
  594.     #    $this->set_primary_key( $fields[$this->table."_primary_key"] );
  595.     #}
  596.     #foreach ( $fields as $field => $type ) {
  597.     #  if ( !( array_key_exists( $field, $this->field_array ) ) ) {
  598.     #    if ( !( $this->table."_primary_key" == $field ) ) {
  599.     #      $this->set_field( $field, $type );
  600.     #    }
  601.     #  }
  602.     #}
  603.  
  604.     if !isset$this->primary_key )) && $this->table != 'db_sessions')
  605.       trigger_error("The ".$this->table." table must have a primary key. Example: ".$this->table."->set_primary_key('field')".@mysql_error($this->conn)E_USER_ERROR );
  606.     $this->exists = true;
  607.     $this->set_routes$this->table );
  608.     trigger_after'save'$this$db );
  609.   }
  610.   
  611.   function is_blob($field{
  612.     $db =db_object();
  613.     return $db->get_mapped_datatype$this->field_array[$field=== 'blob' );
  614.   }
  615.   
  616.   function find$id=NULL$find_by=NULL {
  617.     $db =db_object();
  618.     trigger_before'find'$this$db );
  619.     $db->recordsets[$this->table$db->get_recordset($this->get_query($id$find_by));
  620.     $rs =$db->recordsets[$this->table];
  621.     if (!$rsreturn false;
  622.     if $id != NULL && $rs->rowcount )
  623.       if $find_by != NULL )
  624.         return $rs->Load$this->table);
  625.       else
  626.         return $rs->Load$this->table$rs->rowmap[$this->table][$id);
  627.     trigger_after'find'$this$db );
  628.     return false;
  629.   }
  630.   
  631.   function find_by$col$val {
  632.     return $this->find$val$col );
  633.   }
  634.   
  635.   function MoveFirst({
  636.     $db =db_object();
  637.     if (!(isset($db->recordsets[$this->table])))
  638.       $this->find();
  639.     $rs =$db->recordsets[$this->table];
  640.     if (!$rsreturn false;
  641.     return $rs->MoveFirst$this->table );
  642.   }
  643.   
  644.   function MoveNext({
  645.     $db =db_object();
  646.     if (!(isset($db->recordsets[$this->table])))
  647.       $this->find();
  648.     $rs =$db->recordsets[$this->table];
  649.     if (!$rsreturn false;
  650.     return $rs->MoveNext$this->table );
  651.   }
  652.   
  653.   function session_exists({
  654.     if (isset($_SESSION[$this->table."_submission"]))
  655.       return true;
  656.     return false;
  657.   }
  658.   
  659.   function set_limit$limit {
  660.     if $limit $this->limit = $limit;
  661.   }
  662.   
  663.   function set_offset$offset {
  664.     if $offset $this->offset = $offset;
  665.   }
  666.   
  667.   function set_orderby$col {
  668.     if strlen$col $this->orderby = $this->table . "." $col;
  669.   }
  670.   
  671.   function set_order$order {
  672.     if strlen$order $this->order = $order;
  673.   }
  674.   
  675.   function rowcount({
  676.     $db =db_object();
  677.     $rs =$db->recordsets[$this->table];
  678.     if (!$rsreturn 0;
  679.     return $rs->num_rows$this->table );
  680.   }
  681.   
  682. }
  683.  
  684. ?>

Documentation generated on Mon, 19 Feb 2007 10:24:50 -0800 by phpDocumentor 1.3.1