Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Comments

Added Nette\Utils\Future for lazy value evaluation#272

Open
milo wants to merge 25 commits intonette:master from
milo:pull-future
Open

Added Nette\Utils\Future for lazy value evaluation #272
milo wants to merge 25 commits intonette:master from
milo:pull-future

Conversation

@milo
Copy link
Member

@milo milo commented Nov 19, 2021

  • new feature
  • doc PR: will if accepted

The Future class is a helper for "building data structures" which I use about 4 years. It is handy for small tasks where ORM is too huge, or where two independent data sources are mixing up (database and database, HTTP API and database, local CSV and HTTP API...).

I used to call it "promise" but it is not a promise pattern. It is closer to "future evaluation strategy" but it is not the same. Maybe there exists the corrent name for this evaluation method but I didn't find it.

An example of usage:

# Fetch all books from database with theirs author & editor person ID.
$books = $db->query('
 SELECT
 title,
 author_id AS author, -- this is an integer, ID of person
 editor_id AS editor -- this is an integer, ID of person
 FROM
 book
')->fetchAll();
# Replace all authors & editors ID by corresponding entities.
$futurePersons = new Future(function (array $ids) use ($db) {
 return $db->query('SELECT id, first_name, last_name FROM person')->fetchAssoc('id');
});
$futurePersons->bindArraysKey('author', $books);
$futurePersons->bindArraysKey('editor', $books);
$futurePersons->resolve();

On resolve() call, the resolver (callback from constructor) is called with all required authors and editors ID. So there is no need to iterate over books, collects every ID, fetch them and iterate again to build the desired data structure.

The example with database is a little bit funny (even I'm using it in this way). Some ORM does this task probably better. But this Future class is pretty low level helper. If you compose data structures from heterogeneous sources, like files in filesystem, HTTP API or some other JSON sources, the resulting code is nice, short and clean.

Another use case is scalar to value object translation. For example, translate every e-mail string, to Email object:

$resolver = function (array $keys) {
 $result = [];
 foreach ($keys as $key) {
 $result[$key] = new Email($key);
 }
 return $result;
};
(new Future($resolver))
 ->bindArraysKey('from', $data)
 ->bindArraysKey('to', $data)
 ->resolve();

Or scalar to scalar translation (language translator).

Anyway, the workflow is following:

  1. write resolver
  2. bind variables for future evaluation
  3. resolve (translate)

and API looks like:

# Resolve 'key' and store it into $var
->bind('key', $var) 
# Get $var as a key, resolve it and store it back to $var
->bindVar($var) 
# Get an array of values as keys, and resolve them - like bindVar() for every array item
->bindArrayValues($array)
# The array keys are used as keys to be resolved and stored into array values - like bind('key', $var) for every key/value pair
->bindArrayKeys($array)
# Expects every item of $arrays is an array - do bind('key', $item) for every item
->bindArraysKey('key', $arrays)
 # dtto but expects every item is an object and resolved value is stored into propery
->bindObjectsProperty('key', $arrays)

These are common use cases I hit and examples can be found in attached test.

In general, there is a space for ->bindAssoc('a[]->foo->bar', $structure) but I rarely use it. It could be nice but if required, manual iteration through the $structure and ->bind...() calls work too.

One dark side - it is quite hard to goole future term so maybe different name should be used.

eydun reacted with thumbs up emoji
dg and others added 25 commits October 27, 2021 01:10
Copy link

I like it. But it really should have better name. It basically just maps/binds values from data source into. Maybe something like LazyMapper? LazyBinder?

Copy link
Member Author

milo commented Nov 19, 2021

Yep, the hardest thing :)

Copy link

Deferred? Delayed?

mabar reacted with thumbs up emoji

@dg
Copy link
Member

dg commented Nov 23, 2021

I think there's a missing $ids, or am I misunderstanding?

$futurePersons = new Future(function (array $ids) use ($db) {
 return $db->query('SELECT id, first_name, last_name FROM person')->fetchAssoc('id');
});

@dg dg force-pushed the master branch 2 times, most recently from e673736 to 299a857 Compare January 9, 2026 09:58
@dg dg force-pushed the master branch 13 times, most recently from 167d27a to 2ddfcc6 Compare February 10, 2026 05:52
@dg dg force-pushed the master branch 14 times, most recently from 7c0abb7 to bb3ea63 Compare February 13, 2026 03:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /