public function set($values, $value = NULL) { // Accept set($_POST, array('author', 'body')); // Only $_POST['author'] and $_POST['body'] will be passed if (is_array($values) and is_array($value)) { $keys = array_flip($value); $values = array_intersect_key($values, $keys); $value = NULL; } parent::set($values, $value); if (!empty($this->_unmapped)) { $related = array(); foreach ($this->_meta->fields() as $k => $v) { if ($v instanceof Jelly_Field_Relationship) { $related[$k] = Jelly::meta($v->foreign['model']); } } if (!empty($related)) { foreach ($this->_unmapped as $k => $v) { foreach ($related as $key => $r) { if ($r->fields($k) !== NULL and $r instanceof Model) { $this->{$key}->set($k, $v); continue; } } } } } return $this; }
/** * Tracks a database result * * @param mixed $model * @param mixed $result */ public function __construct($result, $model = NULL) { $this->_result = $result; // Load our default model if ($model and Jelly::meta($model)) { $this->_model = $model instanceof Jelly_Model ? $model : new $model(); $this->_meta = $this->_model->meta(); } }
/** * Returns a query builder that can be used for querying. * * If $key is passed, the key will be passed to unique_key(), the result * will be limited to 1, and the record will be returned directly. * * In essence, passing a $key is analogous to: * * Jelly::query($model)->where(':unique_key', '=' $key)->limit(1); * * @param string $model * @param mixed $key * @return Jelly_Builder */ public static function query($model, $key = NULL) { $builder = 'Jelly_Builder'; if ($meta = Jelly::meta($model)) { if ($meta->builder()) { $builder = $meta->builder(); } } return new $builder($model, $key); }
/** * Tests Jelly::meta() and that meta objects are correctly returned. * * @dataProvider provider_register */ public function test_meta($model, $expected) { $result = Jelly::meta($model); // Should return a Jelly_Meta instance if ($expected === TRUE) { $this->assertTrue($result instanceof Jelly_Meta); $this->assertTrue($result->initialized()); } else { $this->assertFalse($result); } }
protected function init() { foreach ($this->model['fields'] as $key => &$field) { if (array_key_exists('data_source', $field)) { $val_field = $field['data_source']['val_field']; $text_field = $field['data_source']['text_field']; $params = array_key_exists('params', $field['data_source']) ? $field['data_source']['params'] : null; $list = call_user_func_array($field['data_source']['callback'], $params); if (!array_key_exists('items', $field)) { $field['items'] = array(); } if (!array_key_exists('result', $field['data_source']) || $field['data_source']['result'] == 'array') { foreach ($list as $item) { $field['items'][] = array('value' => $item[$val_field], 'text' => $item[$text_field]); } } else { if ($field['data_source']['result'] == 'object') { foreach ($list as $item) { $field['items'][] = array('value' => $item->{$val_field}, 'text' => $item->{$text_field}); } } else { throw new Exception('unknown result type: ' . $field['data_source']['result']); } } } //pull info if from a Jelly Model if (array_key_exists('model', $field)) { //get model if (!isset($this->models[$field['model']])) { $this->metas[$field['model']] = $meta = Jelly::meta($field['model']); } else { $meta = $this->metas[$field['model']]; } $fld = $meta->fields($field['field']); //get Jelly class of field $class = get_class($fld); $field['type'] = self::$field_map[$class]; //now grab info needed from the field $field['name'] = $fld->name; if ($this->model['populate_defaults'] === true) { $field['value'] = $fld->default; } } if (isset($field['attributes'])) { $field['attributes'] = array(); } $field['attributes']['type'] = $field['type']; if (in_array($field['type'], array('password'))) { $field['type'] = 'text'; } //Jx_Debug::dump($field, "field $key in init()"); } }
/** * Tests returning a model directly from a SELECT. * * @dataProvider provider_single_select * @param Jelly $model * @param bool $exists * @return void */ public function test_single_select($model, $exists) { $this->assertTrue($model instanceof Jelly_Model); if ($exists) { $this->assertTrue($model->loaded()); $this->assertTrue($model->saved()); $this->assertTrue($model->id > 0); } else { $this->assertFalse($model->loaded()); $this->assertFalse($model->saved()); $this->assertTrue($model->id === $model->meta()->field('id')->default); } }
/** * Constructor. * * A key can be passed to automatically load a model by its * unique key. * * @param mixed|null $key */ public function __construct($key = NULL) { // Load the object's meta data for quick access $this->_meta = Jelly::meta($this); // Copy over the defaults into the original data. $this->_original = $this->_meta->defaults(); // Have an id? Attempt to load it if ($key !== NULL) { $result = Jelly::query($this, $key)->as_object(FALSE)->select(); // Only load if a record is found if ($result) { $this->load_values($result); } } }
/** * Implementation for Jelly_Field_Behavior_Saveable. * * @param Jelly $model * @param mixed $value * @return void */ public function save($model, $value, $loaded) { // Find all current records so that we can calculate what's changed $in = $loaded ? $this->_in($model, true) : array(); // Find old relationships that must be deleted if ($old = array_diff($in, (array) $value)) { Jelly::delete($this->through['model'])->where($this->through['columns'][0], '=', $model->id())->where($this->through['columns'][1], 'IN', $old)->execute(Jelly::meta($model)->db()); } // Find new relationships that must be inserted if (!empty($value) && ($new = array_diff((array) $value, $in))) { foreach ($new as $new_id) { if (!is_null($new_id)) { Jelly::insert($this->through['model'])->columns($this->through['columns'])->values(array($model->id(), $new_id))->execute(Jelly::meta($model)->db()); } } } }
/** * Constructor. * * If $values is passed and it is an array, it will be * applied to the model as if it were a database result. * The model is then considered to be loaded. * * It is important to note that, although Jelly Models are * not instantiated from Database_Results (by using * as_object()), they can be instantiated this way. * * @param array $values **/ public function __construct($values = array()) { // Load the object's meta data for quick access $this->_meta = Jelly::meta($this); // Copy over the defaults into the original data. This also has // the added benefit of registering the model's metadata, if it does not exist yet $this->_original = $this->_meta->defaults(); // Add the values stored by mysql_set_object if (!empty($this->_preload_data) and is_array($this->_preload_data)) { $this->load_values($this->_preload_data); $this->_preload_data = array(); } // Have an id? Attempt to load it if ($values) { // Arrays are loaded as values, but not load()ed if (is_array($values)) { $this->set($values); } } }
public function testInitialization() { $this->assertEquals(TRUE, Jelly::meta('alias')->initialized()); $this->assertEquals(TRUE, Jelly::meta(new Model_Alias())->initialized()); $this->assertEquals(FALSE, Jelly::meta('non-existent-model')); }
protected function generate_field(Jelly_Model $model, &$fields, $field_id, $field, array $validation_errors = array(), $attrs = array(), $field_prefix = 'field-') { $field_id_attr = $field_prefix . $field->name; if (!$this->include_field($field, $model->id() == 0)) { return; } $id_attribs = array('attributes' => array('id' => $field_id_attr) + $attrs, 'name' => $field_id_attr); if ($field->prevent_edit) { $label = Form::label($field_id_attr, $field->label); $field_str = $field->display($model, $model->get($field_id)); $fields[$label] = '<div class="non-editable">' . ($field_str == '' || $field_str == ' ' ? ' ' : $field_str) . '</div>'; } else { $field_output = $model->input($field->name, $id_attribs); if ($field instanceof Field_HasManyUniquely) { $label = View::factory('jelly/field/hasmanyuniquely/header', array('label' => $field->label, 'is_sortable' => isset($field->sort_on) && $field->sort_on)) . ''; } else { if ($field instanceof Field_BelongsTo && $field->edit_inline) { $label = '<!-- ' . $field_id . ' -->'; $sub_model = $model->{$field_id}; $sub_meta = Jelly::meta($sub_model); $field_output = View::factory('kadmium/fieldset_subedit', array('field_id' => $field_id, 'label' => $field->label, 'fields' => $this->generate_fields($sub_model, $sub_meta, 'field-' . $field_id . '-', $validation_errors))); } else { $label = Form::label($field_id_attr, $field->label); } } $fields[$label] = $field_output; if (isset($validation_errors[$field_id])) { array_push($field->css_class, 'error'); $fields[$label]->errors = $validation_errors[$field_id]; } } }
/** * This method aliases models to tables. * * @param string $table * @return string */ protected function _table($model) { if ($meta = Jelly::meta($model)) { $model = $meta->table(); } return $model; }
protected function _tables() { // Prepare an array to hold tables $tables = array(); // Create a new database table with name and database $table = new Database_Table($this->_model->table(), $this->_db); // Get the model's primary keys as an array $model_pks = is_array($this->_model->primary_key()) ? $this->_model->primary_key() : array($this->_model->primary_key()); // Loop through each field within the model foreach ($this->_model->fields() as $field) { // Check if the field implaments the migratable field interface if ($field instanceof Jelly_Field_Migratable) { // Loop through each column in the field foreach ($field->columns() as $column) { // Add the column to the table $table->add_column($column); } } elseif ($field->in_db) { // If the field is unique if ($field->unique) { // Add a unique constraint to the table $table->add_constraint(new Database_Constraint_Unique($field->column)); } // Loop through every column in the model foreach ($this->_columns($field, $table) as $column) { // Add the column to the table $table->add_column($column); } } elseif ($field instanceof Jelly_Field_ManyToMany) { // ManyToMany fields also contain a pivot table $pivot = new Database_Table($field->through['model'], $this->_db); // Get fields $columns = $field->through['columns']; foreach ($columns as $field) { // Chekt if the field names are defaults if (strstr($field, ':')) { list($model, $field) = explode(':', $field); // Append the : back onto $field, it's key for recognizing the alias below $field = ':' . $field; // We should be able to find a valid meta object here if (FALSE == ($meta = Jelly::meta($model))) { throw new Kohana_Exception('Meta data for :model was not found while trying to resolve :field', array(':model' => $model, ':field' => $field)); } $field = $meta->foreign_key(); } $column = Database_Column::factory('int'); $column->auto_increment = FALSE; $column->name = $field; $cols[] = $column; } // Add to pivot foreach ($cols as $column) { // Add it to the pivot table $pivot->add_column($column); } // Add a primary key constraint on all fields within the pivot table $pivot->add_constraint(new Database_Constraint_Primary(array_keys($pivot->columns()), $pivot->name)); /** * @todo It would be more than appropriate to add a contstraint in a following * form into a database: ALTER TABLE `roles_users` ADD CONSTRAINT `roles_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, * */ // Add the pivot table to the list of tables $tables[] = $pivot; } } // Add the primary key constraints to the table $table->add_constraint(new Database_Constraint_Primary($model_pks, $table->name)); // Add the table to the list $tables[] = $table; // And return all tables. return $tables; }
/** * Easy-to-override method that expands aliases. * * @param string $model * @param string $alias * @param array $state * @return array */ protected function _expand_alias($model, $alias, $state) { switch ($alias) { case ':primary_key': $state['field'] = Jelly::meta($model)->primary_key(); break; case ':name_key': $state['field'] = Jelly::meta($model)->name_key(); break; case ':foreign_key': $state['field'] = Jelly::meta($model)->foreign_key(); break; case ':unique_key': $state['field'] = Jelly::query(Jelly::meta($model)->model())->unique_key($state['value']); break; default: throw new Kohana_Exception('Unknown meta alias :alias', array(':alias' => $alias)); } return $state; }
public function display($model, $value) { return $value->execute()->{Jelly::meta($this->foreign['model'])->name_key()}; }
/** * Generates a builder where clause based on the provided values * * @param Jelly_Builder $builder * @param string $model Model name used in the where clause * @param mixed $values argument list used to construct the clause * @return Jelly_Builder */ protected function _where($builder, $model, array $values) { // Check for an outer and/or indexed array $where = 'where'; if (isset($values['or'])) { $where = 'or_where'; $values = is_array($values['or']) ? $values['or'] : array($values['or']); } elseif (isset($values['and'])) { $where = 'and_where'; $values = is_array($values['and']) ? $values['and'] : array($values['and']); } $count = count($values); /* One Argument is passed, can be: * interger - for the primary key lookup * string - for the name key lookup * object - a loaded model of the same class */ if ($count == 1) { if (is_integer($values[0])) { return $builder->{$where}($model . '.:primary_key', '=', $values[0]); } elseif (is_string($values[0])) { return $builder->{$where}($model . '.:name_key', '=', $values[0]); } elseif (is_object($values[0])) { $class = Jelly::class_name($model); if ($values[0] instanceof $class) { return $builder->where($model . '.:primary_key', '=', $values[0]->id()); } } } elseif ($count == 2) { if (!Jelly::meta($model)->fields($values[0])) { throw new Kohana_Exception('":field" is not a valid :model field', array(':field' => $values[0], ':model' => $model)); } return $builder->{$where}($model . '.' . $values[0], '=', $values[1]); } elseif ($count == 3) { if (!Jelly::meta($model)->fields($values[0])) { throw new Kohana_Exception('":field" is not a valid :model field', array(':field' => $values[0], ':model' => $model)); } return $builder->{$where}($model . '.' . $values[0], $values[1], $values[2]); } // Not Supported throw new Kohana_Exception('invalid or unsupported argument'); }
/** * Returns either an array or unexecuted query to find * which columns the model is "in" in the join table * * @param Jelly $model * @param boolean $as_array * @return mixed */ protected function _in($model, $as_array = FALSE) { $result = Jelly::select($this->through['model'])->select($this->through['columns'][1])->where($this->through['columns'][0], '=', $model->id()); if ($as_array) { $result = $result->execute(Jelly::meta($model)->db())->as_array(NULL, $this->through['columns'][1]); } return $result; }
/** * Returns the builder class to use for the specified model * * @param string $model * @param int $type * @return string */ protected static function builder($model, $type = NULL) { $builder = 'Jelly_Builder'; if ($meta = Jelly::meta($model)) { if ($meta->builder()) { $builder = $meta->builder(); } } return new $builder($model, $type); }