spotorm / spot2

Spot v2.x DataMapper built on top of Doctrine's Database Abstraction Layer
http://phpdatamapper.com
BSD 3-Clause "New" or "Revised" License
601 stars 101 forks source link

Modifying many-to-many relationships #163

Closed codyhatch closed 2 years ago

codyhatch commented 8 years ago

How can I change a many-to-many relationship and persist it to the DB?

I've got a User entity and an Office entity, both with a hasManyThrough() relation via an Assignment entity.

I can get an Office object and due $office->users to get a list of users assigned to the DB: Great! How can I modify the list and persist it to the DB? Is there any example code showing how this works?

Ideally I'd like to do something like:

$office->users = $newUserList;
$office->save();

But this doesn't work. Do I need to get an AssignmentMapper and create and delete Assignment entities manually?

nebulousGirl commented 8 years ago

The latest version of the master branch provides a way to persist relations using the save method of the mapper. The solution was just recently merged.

Your example is using the ActiveRecord pattern, but Spot uses the DataMapper pattern, so you have to use the mapper to save your entities:

    $office = $officeMapper->get(10);
    $office->users = $newUserList;
    $officeMapper->save($office, ['relations' => true]);
codyhatch commented 8 years ago

Okay, so I've updated to the latest commit on the master branch, and tried again. Code currently looks like:

$office = $officeMapper->get(211);
print_r(count($office->users));
$office->users = $newUserList;
$officeMapper->save($office, ['relations' => true]);
print_r(count($office->users));
$office = $officeMapper->get(211);
print_r(count($office->users));

The result prints out 2 (the number of users assigned to start with), then 3 (the number of users in $newUserList), and then 2 again. It's not actually touching the database . The Office entity has this line in its relations method:

'users' => $mapper->hasManyThrough($entity, 'Entity\User', 'Entity\Assignment', 'user_id', 'office_id'),

The User entity has this line:

'offices'   => $mapper->hasManyThrough($entity, 'Entity\Office', 'Entity\Assignment', 'office_id', 'user_id')

And the Assignment entity is just:

class Assignment extends Entity
{
    protected static $table = 'assignments';

    public static function fields()
    {
        return [
            'assignment_id' => ['type' => 'integer', 'primary' => true, 'autoincrement' => true],
            'user_id'       => ['type' => 'integer', 'required' => true],
            'office_id' => ['type' => 'integer', 'required' => true],
        ];
    }
}

Any thoughts?

codyhatch commented 8 years ago

Other relations are also not saving. Eg, my User entity has a belongsTo relation with a Plan entity:

$joe = $userMapper->get(1);
print_r($joe->plan->name);
$joe->plan = $planMapper->get(2);
print_r($joe->plan->name);
$userMapper->save($joe, ['relations' => true]);
$joe = $userMapper->get(1);
print_r($joe->plan->name);

This prints out 'Plan 1', 'Plan 2', and then 'Plan 1'; again the DB is not touched. This is with commit 71391cbe76fdbc7acb0dce2fcf1defe68a3e2a4a installed.

nebulousGirl commented 8 years ago

I will take a look at it. Can you tell me what is the $newUserList variable? Is it an array or an instance of a class? in which case, what is the class?

nebulousGirl commented 8 years ago

There is an issue when using not modified entities when saving relationships. I will look into it tonight and add those test cases.