Add virtual fields #569

Open
wants to merge 1 commit into
from

Projects

None yet

7 participants

@hans-d
Contributor
hans-d commented Jul 10, 2012

Summary:

Virtual fields in using methods in the Model, that are almost like the current behaviour to implement methods for an Entity.

Virtual fields are defined in the model like _schema (although that is done for SQL db's by the datasource), eg:

protected $_properties = array(
    'extra_field_1' => 'ExtraField`, 
    // expects model to have methods getExtraField, setExtraField, issetExtraField

    'extra_field_2' => array('get' => 'DoSomethingFancy', 'set' => false) 
    // expects model to have methods DoSomethingFancy and issetextra_field_2
    // set is done like for normal fields: data is added to _updated
)

that would save a lot of checks and makes the behaviour more explicit.

Outstanding:

  • set behaviour in data\entity\Document - need some help with that
  • squash commits (when everything is ready)
@hans-d
Contributor
hans-d commented Jul 10, 2012

Open for improvements etc.

@nateabele nateabele commented on an outdated diff Jul 10, 2012
data/Entity.php
@@ -173,9 +188,27 @@ public function __set($name, $value = null) {
* @return mixed Result.
*/
public function __isset($name) {
+ if ($callable = getModelMethod('isset_'. $name)) {
@nateabele
nateabele Jul 10, 2012 Member

Does this line even execute without a fatal error?

@nateabele
Member

Seems like a reasonable feature, only having underscores in method names is a coding standards violation (as is not prefixing protected methods with an underscore), and as I mentioned in the code, it looks like this must not have even been tested, as I don't see how the __isset() implementation could even execute.

Let's see some tests and documentation on this first. Thanks.

@hans-d
Contributor
hans-d commented Jul 10, 2012

@nateabele no probs.. This PR was also more to shape the feature in the correct way. I will submit a new PR with squashed commit when all is sorted out.

What kind of documentation are you looking for, besides updated docblocks ?

I explicitly choose the underscore, as most of the fieldnames will be lowercased with the occasional underscore (at least in the SQL environments I worked) and to maintain consistency at the field level between normal and virtual fields. What do you suggest?

@jails
Contributor
jails commented Jul 10, 2012

What do you mean by "virtual fields" ? I don't figure out the use case.

@hans-d
Contributor
hans-d commented Jul 10, 2012

Virtual fields, or derived fields. Currently it is not yet possible to easily do the derivation using the datasource (see also #558), but it can be done using functions/methods in php.

Use cases I currently have:

  • data is entered using 1 field, and on save this is normalised and broken into mutliple fields. I use the combined field as a virtual field that via the get method provides the combined data from the original fields.
  • a phone number is stored normalized as fully international, but entered and shown localized for the current country (eg local phone numbers without the international prefix, international numbers with the prefix)
  • dates are stored in iso format, but entered/shown in the local format.
    (there is only one local format)

Other use cases:

  • use improved field names
  • other transformations and/or calculations

The advantages:

  • model deals with the data transformations
  • view only have to work with field, and not functions, and is directly available for forms
  • controller has no clue what is happening
  • in case of the combined field entry, in other views I use the separate fields to show data, so I need both combined and separate fields
  • transformations are only done when needed
  • re-uses the Entity to model callback

Current workarounds:

  • do the mapping in the controller by explicitly setting the needed fields
  • call the entity/model to do it, either as a separate call from the controller or via some filters on data retrieval
@hans-d
Contributor
hans-d commented Jul 10, 2012

The getModelMethod might be even more useful if we do not check on the existence of a (static) method, but only test if it is callable. That way we can use the __call and __callStatic in the model. to do more magic...

@jails
Contributor
jails commented Jul 11, 2012

It's a lot of checks ! Why not making Entity::set() filterable instead ?
You can explode your "combined field" on set() call. Then you can get back the "virtual fields" with a minimal :

public function &__get($name) {
    if (isset($this->_updated[$name])) {
        return $this->_updated[$name];
    }
    $null = null;
    return $null;
}
@hans-d
Contributor
hans-d commented Jul 11, 2012

I'm not too happy about the checks either... Filterable methods (set, __get, __set and __isset) can be part of the solution. For my current situation I might be able to get away with only one on set.

I prefer having methods in the Model, that are almost like the current behaviour to implement methods for an Entity. That way I have most of the behaviour in a single place (methods in the model), without requiring filters.

if the the virtual fields are defined in the model like _schema (although that is done for SQL db's by the datasource), eg:

protected $_virtual = array(
    'extra_field_1' => 'ExtraField`, 
    // expects model to have methods GetExtraField, SetExtraField, IssetExtraField

    'extra_field_2' => array('get' => 'DoSomethingFancy') 
    // will only accept a get, a set will put the value in Enity->_updated
    // the get method will be DoSomethingFancy
)

that would save a lot of checks and makes the behaviour more explicit.

@jails
Contributor
jails commented Jul 11, 2012

Currently __set() use set() (or it should). Filterting __get is imo not necessary since pre-processing can be done during the set() (same for isset()).

$entity->vitual = 'data1'; // will populate the real field based on virtual data
$entity->real = 'data2'; // will populate the virtual field based on real data

Imo you can reproduce your virtual behavior with setting a filter on set() in the __construct() of your model and keeping all the methods in models.

@jails
Contributor
jails commented Jul 11, 2012

Well personnaly I would prefer the following solution and let people override __set rather than adding fixed behaviors :
https://gist.github.com/3090475

@hans-d
Contributor
hans-d commented Jul 11, 2012

Doing it like this will add to the overhead of larger entity sets. Each individual property set for a model bound entity now results in 3 additional function calls (invokeMethod, call_user_func, setter). For my "benchmark" dataset, this will be 20k (rows) * 3 (calls) * 6 (fields) = 360k additional function calls, and without these calls it is already way slower as retrieving the same dataset using doctrine (slower as in li3 hits the max execution time where doctrine is already done).

The new fixed behaviour builds on existing behaviour for entity functions, only adding an optional property in the model when used to keep the number of required checks and calls to a minimum.

@hans-d
Contributor
hans-d commented Jul 11, 2012

@nateabele you now have some documentation and tests...

@hans-d hans-d Add virtual fields
define in the model as functions
identify in the model using $_properties
49d7851
@d1rk
Member
d1rk commented Sep 26, 2012

Any news on this one?

@L-P
Contributor
L-P commented Oct 1, 2012

That's definitely something I could use, I'd like some news too if possible.

@2chg
2chg commented Oct 13, 2014

Is someone still working in this? I'm looking for this feature right now and unfortunately the gist link from two years ago is no longer valid. :(

@davidpersson
Member

If the getsyntax RFC made it through (doesn't seem unlikely it's put up for vote one time again), we had this natively implemented: https://wiki.php.net/rfc/propertygetsetsyntax-v1.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment