Flourish PHP Unframework
This is an archived copy of the forum for reference purposes

Populating records 2 relationships away

posted by mike 8 years ago

I have the following one-to-many relationships: Group has many User
User has many Favorite

I am trying to submit data for all three record types at once and have it populate. Here are my inputs:

<?php ... ?>
<input type="hidden" name="id" value="<?=$group->encodeId()?>" />
<?php foreach ($users as $i => $user): ?>
    <input type="hidden" name="users::id[<?=$i?>]" value="<?=$user->encodeId()?>" />
    <input type="text" name="users::name[<?=$i?>]" value="<?=$user->encodeName()?>" />
    
    <?php foreach ($user->buildFavorites() as $j => $favorite): ?>
        <input type="hidden" name="favorites::id[] value="<?=$favorite->encodeId()?>" />
        <input type="text" name="favorites::link[] value="<?=$favorite->encodeLink()?>" />
    <?php endforeach; ?>
<?php endforeach; ?>

My problem is that I can't figure out how to associate the favorites with the correct users. What ends up happening is that each user gets ALL the favorites. I've been playing around with the naming of the fields for a while now, and I can't get it right.

Thanks, Mike

There is a patch to allow for nested populate calls on fActiveRecord (ticket #485), I just haven't had the time to go through it and figure out how I want it to change the API.

// This currently works
$group->populateUsers();

// This is the proposed API
$group->populate(array('Users' => array('Favorites')));

I'm not sure I like the idea of nested array structures, just because it gets hard to tell when in the nesting structure you are. Now, the input name syntax in the HTML is like this:

#!text/html
<input type="text" name="users::favorites::id[]" value="foo" />

Because of that, I am thinking the API for nested populate actions should be:

$group->populate('Users', 'Users::Favorites');

// Or if there is more than one relationship route between tables
$group->populate('Users{route_to_users}', 'Users{route_to_users}::Favorites{route_to_favorites}');

One benefit to the string syntax is that is is very similar to the fActiveRecord::replicate() syntax, and I would like to keep syntax and APIs similar when possible.

What do people think? Do you like the nested array syntax, or the string syntax that is like the input names?

posted by wbond 8 years ago

Will, this would be a very useful addition, and for the record I like the syntax you are proposing over the array syntax. Although it does appear a bit convoluted with the routes being specified. Using the array syntax what would be the method of specifying the route?

posted by mattsah 8 years ago

Yeah, so the routes are relatively uncommon in practice. This {route} syntax is used both by the input names and by fActiveRecord::replicate(). There was a slight bug in the example code I had posted. Instead of:

$group->populate('Users::{route_to_users}', 'Users{route_to_users}::Favorites{route_to_favorites}');

it should be:

$group->populate('Users{route_to_users}', 'Users{route_to_users}::Favorites{route_to_favorites}');

I've fixed my previous post to reflect this.

posted by wbond 8 years ago

Sorry, I missed directly answering your question about the array syntax. It would use the same curly syntax:

$group->populate(array(
    'Users{route_to_users}' => array(
        'Favorites{route_to_favorites}'
    )
));
posted by wbond 8 years ago

Thanks for the quick response. I tried out the patch, but I don't think that it's exactly what I'm looking for. The problem is that there's still no way to group the favorites with particular users.

For example, I tried the following syntax:

<?php foreach ($users as $i => $user): ?>
    <input type="hidden" name="users::id[<?=$i?>]" value="<?=$user->encodeId()?>" />
    <input type="text" name="users::name[<?=$i?>]" value="<?=$user->encodeName()?>" />
    
    <?php foreach ($user->buildFavorites() as $j => $favorite): ?>
        <input type="hidden" name="favorites::id[<?=$i?>][] value="<?=$favorite->encodeId()?>" />
        <input type="text" name="favorites::link[<?=$i?>][] value="<?=$favorite->encodeLink()?>" />
    <?php endforeach; ?>
<?php endforeach; ?>

Notice that the favorites are now arrays of arrays, with the first index indicating which user it belongs to. This way, I can say that "user1" has "favorite1" and "favorite2", and "user2" has "favorite3", "favorite4" and "favorite5". The way things are now, there is no way to distinguish favorites 1 and 2, from 3, 4 and 5.

I hope that cleared things up.

Thanks, Mike

posted by mike 8 years ago

For this patch to work, your HTML should look like the following:

<?php foreach ($users as $i => $user): ?>
    <input type="hidden" name="users::id[<?=$i?>]" value="<?=$user->encodeId()?>" />
    <input type="text" name="users::name[<?=$i?>]" value="<?=$user->encodeName()?>" />
    
    <?php foreach ($user->buildFavorites() as $j => $favorite): ?>
        <input type="hidden" name="users::favorites::id[<?=$i?>][] value="<?=$favorite->encodeId()?>" />
        <input type="text" name="users::favorites::link[<?=$i?>][] value="<?=$favorite->encodeLink()?>" />
    <?php endforeach; ?>
<?php endforeach; ?>

The reason for this is that when populating child records, the request is filtered by the users:: prefix. Then when the favorites are populated for the users, it is further filtered by favorites::. Hence you need users::favorites::.

posted by wbond 8 years ago

Perfect. Got it working. Thanks.

posted by mike 8 years ago