Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Model Virtual Fields #89

Merged
merged 6 commits into from

2 participants

@nathggns
Owner

You should be able to apply virtual fields to models. These can either be closures, or scalar values, or a combination.

Will look something like this.

<?php
$model->virtual('scalar', 'blah');
$model->virtual('closure', function($field) {
    // $model === $this

    return 'blah';
});
$model->virtual('combination', [
    'scalar' => 'blah',
    'closure' => function($field) {
        return 'blah';
    }
]);
@nathggns nathggns was assigned
@nathggns
Owner

These can be created from outside the model, or from the init function.

@ClaudioAlbertin

Why is there a need to declare it as a scalar or a closure? Just execute the function (that doesn't need the field as an argument, btw.) if it is one, or return the value if it isn't.
How would combinations work?

@nathggns
Owner

Oh no, the first argument is the field name, I was just using scalar and closure as examples. Well, if the scalar value is iterable, then you loop through it and look for closures to execute.

@ClaudioAlbertin

In my opinion, it should simply work like this:

$model->first_name = 'Nathaniel';
$model->last_name = 'Higgins';

$model->virtual('full_name', function() {
  return $this->first_name + ' ' + $this->last_name;
});

$model->virtual('login_token', 'w89etuvoeiurwoqn3485npv34q95tgjdu');

echo $model->full_name; // Nathaniel Higgins
echo $model->login_token; // w89etuvoeiurwoqn3485npv34q95tgjdu
@nathggns
Owner

That's exactly what it does.

@ClaudioAlbertin

No. Why does a combination even need to exist? And why does the closure have to get the field name as an argument? What field name even?

@nathggns
Owner

I think you're massively misunderstanding what I'm saying.

The first argument is completely irrelevant, it's just what it's assigned to on the model. If its a string or int or whatever, it'll just assign that right away. If it's a closure, it will call it (at __get time, not when you call virtual) and use that value. If it's an array, it will loop through it, calling closures and reassiging the values, and then use that.

@ClaudioAlbertin

I understand what you're saying, but what you're suggesting doesn't make sense in multiple points.
What is $field supposed to be? You're not supposed to override a field that exists in the database, as writing to that field wouldn't work anymore, thus it can't be the previous value of the field.
This way, using a combination doesn't make sense. You could only have one scalar in the combination anyway, and its value would have to be passed from closure to closure. This behaviour isn't of much advantage, and could be easily achieved without a combination.

@nathggns
Owner

No, you're still misunderstanding. Look at the failing tests I commited - 8a567da

@nathggns
Owner

I think this is ready for merge now.

@ClaudioAlbertin ClaudioAlbertin merged commit 8ea4c23 into development
@nathggns nathggns deleted the issue-89 branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 23, 2013
  1. @nathggns

    Add failing tests.

    nathggns authored
  2. @nathggns

    Fix tests.

    nathggns authored
  3. @nathggns
  4. @nathggns

    Add another test.

    nathggns authored
  5. @nathggns

    Add another test.

    nathggns authored
  6. @nathggns

    Make the tests work.

    nathggns authored
This page is out of date. Refresh to see the latest.
Showing with 137 additions and 3 deletions.
  1. +35 −3 system/classes/models/database.php
  2. +102 −0 tests/ModelDatabaseTest.php
View
38 system/classes/models/database.php
@@ -60,6 +60,11 @@ class ModelDatabase extends Model {
protected $relationships = [];
/**
+ * Virtual fields
+ */
+ protected $virtual_fields = [];
+
+ /**
* Default fields to export.
* True means all. False, or empty array, means none.
*/
@@ -255,6 +260,29 @@ protected function relationship($type, $model, $alias = null, $foreign_key = nul
];
}
+ public function virtual($field, $value) {
+ $this->virtual_fields[$field] = $value;
+ $this->schema[$field] = [
+ 'field' => $field
+ ];
+
+ if (count($this->data) > 0) {
+ $this->data[$field] = $this->value($field, $value);
+ }
+
+ return $this;
+ }
+
+ protected function value($field, $value) {
+
+ if ($value instanceof Closure) {
+ $value = $value->bindTo($this);
+ $value = $value($field);
+ }
+
+ return $value;
+ }
+
public function export($values = null, $level = 1, $count_models = false) {
if (is_null($values)) {
@@ -376,12 +404,12 @@ public function count() {
*/
public function __get($key) {
- if ($this->mode !== static::MODE_SINGLE || (count($this->data) > 0 && !array_key_exists($key, $this->data))) {
+ if ($this->mode !== static::MODE_SINGLE || (count($this->data) > 0 && (!isset($key, $this->data) || is_null($this->data[$key])))) {
throw new Exception('Property ' . $key . ' does not exist on model ' . $this->name);
}
if (isset($this->data[$key])) {
- return $this->data[$key];
+ return $this->value($key, $this->data[$key]);
}
$this->__find();
@@ -477,6 +505,10 @@ public function __get($key) {
}
}
+ foreach ($this->virtual_fields as $field => $val) {
+ $result[$field] = $val;
+ }
+
$schema = array_keys($this->schema);
foreach ($schema as $field) {
@@ -487,7 +519,7 @@ public function __get($key) {
$this->data = $result;
- return $this->data[$key];
+ return $this->__get($key);
}
/**
View
102 tests/ModelDatabaseTest.php
@@ -101,4 +101,106 @@ public function testFetchAllUsers() {
}
}
+ public function testScalarVirtual() {
+ $user = $this->get()->fetch(['id' => 1]);
+
+ $user->virtual('full_name', 'Joseph Hudson-Small');
+
+ $this->assertEquals('Joseph Hudson-Small', $user->full_name);
+ }
+
+ public function testClosureVirtualWithoutArguments() {
+ $user = $this->get()->fetch(['id' => 1]);
+
+ $user->virtual('full_name', function() {
+ return 'Joseph Hudson-Small';
+ });
+
+ $this->assertEquals('Joseph Hudson-Small', $user->full_name);
+ }
+
+ public function testClosureVirtualWithArguments() {
+ $user = $this->get()->fetch(['id' => 1]);
+
+ $user->virtual('field', function($field) {
+ return $field;
+ });
+
+ $this->assertEquals('field', $user->field);
+ }
+
+ public function testArrayVirtualWithClosure() {
+ $user = $this->get()->fetch(['id' => 1]);
+
+ $user->virtual('first_name', 'Joseph');
+ $user->virtual('last_name', 'Hudson-Small');
+
+ $user->virtual('name', function() {
+ return [
+ 'short' => 'Joe',
+ 'full_name' => $this->first_name . ' ' . $this->last_name
+ ];
+ });
+
+ $this->assertEquals('Joe', $user->name['short']);
+ $this->assertEquals('Joseph Hudson-Small', $user->name['full_name']);
+ }
+
+ public function testObjectVirtualWithClosure() {
+ $user = $this->get()->fetch(['id' => 1]);
+
+ $user->virtual('first_name', 'Joseph');
+ $user->virtual('last_name', 'Hudson-Small');
+
+ $user->virtual('name', function() {
+ return new Dynamic([
+ 'short' => 'Joe',
+ 'full_name' => $this->first_name . ' ' . $this->last_name
+ ]);
+ });
+
+ $this->assertEquals('Joe', $user->name->short);
+ $this->assertEquals('Joseph Hudson-Small', $user->name->full_name);
+ }
+
+ public function testArrayVirtual() {
+ $user = $this->get()->fetch(['id' => 1]);
+
+
+ $user->virtual('name', [
+ 'short' => 'Joe'
+ ]);
+
+ $this->assertEquals('Joe', $user->name['short']);
+ }
+
+ public function testObjectVirtual() {
+ $user = $this->get()->fetch(['id' => 1]);
+
+
+ $user->virtual('name', new Dynamic([
+ 'short' => 'Joe'
+ ]));
+
+ $this->assertEquals('Joe', $user->name->short);
+ }
+
+ public function testVirtualsSurviveAfterSave() {
+ $user = $this->get()->fetch(['id' => 1]);
+ $user->virtual('name', 'Joseph Hudson-Small')->save();
+
+ $this->assertEquals('Joseph Hudson-Small', $user->name);
+ }
+
+ public function testVirtualAfterFetch() {
+ $user = $this->get()->fetch(['id' => 1]);
+ $name = $user->name;
+
+ $user->virtual('other_name', function() use ($name) {
+ return $name;
+ });
+
+ $this->assertEquals($name, $user->other_name);
+ }
+
}
Something went wrong with that request. Please try again.