4

I am running SilverStripe 3.4

I cannot find any documentation on programatically saving many many relationships extra fields. The following code simply will not work:

foreach ($notifications as $notification) {
    $status = $notification
        ->Members()
        ->filter([
            "ID" => Member::currentUserID()
        ])
        ->first();
    $data['Read'] = $status->Read; // whenever I call this code, $status->Read is ALWAYS 0
    $status->Read = 1;
    $status->write();
}

The ORM classes:

class Notification extends DataObject {
    private static $belongs_many_many = [
        "Members" => "Member"
    ];
}

class Member extends DataObject {

    private static $many_many = array(
        "Notifications" => "Notification"
    );

    private static $many_many_extraFields = array(
        "Notifications" => array(
            "Read" => "Boolean"
        )
    );
}

Poking around, I have seen that DataObject::getChangedFields filters out my Read field because it is not a "database field"

Note: I have overridden Notification::onBeforeWrite but:

  • I don't think this is called
  • I have this code at the start of it:

    protected function onBeforeWrite() {
        parent::onBeforeWrite();
        $changedFields = $this->getChangedFields();
        if (isset($changedFields['Read']) && count($changedFields) == 1) {
            return;
        }
    }
    
3dgoo
  • 15,716
  • 6
  • 46
  • 58
Isaac
  • 11,409
  • 5
  • 33
  • 45
  • Did you mean to override the Member class? It's provided by the framework, and it's probably taking precedence in this case. For the sake of argument, what happens when you change the name of Member to MyMember? Does it save? – cryptopay Jul 20 '16 at 23:47
  • @elliot_at_silverstripe we're using DataExtensions – Isaac Jul 22 '16 at 02:56

1 Answers1

2

I found the counter intuitive answer:

foreach($notifications as $notification){
    $data = $notification->toMap();
    $list = $notification->Members()
        ->filter([
            "ID"=>Member::currentUserID()
        ]);
    $status = $list->first();
    $data['Read'] = $status->Read;

    $list->add(Member::currentUserID(),[
        "Read"=>1
    ]);
}

This makes no sense (adding something that is already on the list to the list), but it works. I hope they update this.

Isaac
  • 11,409
  • 5
  • 33
  • 45
  • If you change something on the element itself (Member in this case), it get's saved to the Member table. And therefor is updated automatically. $many_many_extraFields is saved to an extra table in the DB, in your example to Member_Notifications. So when you get an element from the ManyManyList you loose the relation to the list instantly. You just have the element with the extra manymany information. So you don't need to update the Member DO but the ManyManyList. I hope that helps to make the working example more intuitive ;) – wmk Jul 21 '16 at 07:41
  • Why does it have to lose the relationship? You have the schema there (in $many_many), I'm certain it would be possible to do a check for the many many relationship extra fields alongside checks for the standard object properties – Isaac Jul 21 '16 at 22:15
  • Also, why does the method `ManyManyList::add` do more than just add things? It doesn't seem intuitive that an add method would update things – Isaac Jul 21 '16 at 22:16