Jump to:

7912 Posts in 1355 Topics by 930 members

DataObjectManager Module

SilverStripe Forums » DataObjectManager Module » HOWTO: HasMany and ManyMany new items default to checked

Discuss the DataObjectManager module, and the related ImageGallery module.

Moderators: martimiz, UncleCheese, Howard, Sean, Ryan M., biapar, Willr, Ingo, swaiba, simon_w

Page: 1
Go to End
Author Topic: 1540 Views
  • Anonymous user
    Avatar
    Community Member
    1 Post

    HOWTO: HasMany and ManyMany new items default to checked Link to this post

    Edit: fixed recursion bug when trying to add an image into a set it already belongs to (eg. multiple DOM instances open at once in different browser windows)

    Hi list

    Quick tip for anyone who would like newly created items in a HasManyDataObjectManager or ManyManyDataObjectManager to default to belonging to the item you created them in.

    HasManyDataObjectManager

    In your child DataObject:

    class ChildObject extends DataObject {

       . . .

       /**
        * Add newly created items to the parent
        */
       function onAfterWrite() {
          $relatedClass = 'ParentClassname';
          $referringField = 'ParentReferringField';
          if ( array_key_exists('ctf[parentClass]', $this->record)
          && $this->record['ctf[parentClass]'] == $relatedClass ) {
             $parentRecord = DataObject::get_by_id(
                $relatedClass,
                (int)$this->record['ctf[sourceID]']
             );
             $components = $parentRecord->getComponents($referringField);
             if ( ! in_array($this->ID, $components->getIdList()) ) {
                $components->add($this->ID);
                $components->write();
             }
          }
          parent::onAfterWrite();
       }

    }

    You only need change two lines, the $relatedClass and $referringField vars. $relatedClass is the parent classname, and $referringField is the fieldname on the parent that points to the child object in question.

    ManyManyDataObjectManager

    In the child object:

    class ChildObject extends DataObject {

       . . .

       /**
        * Add newly created items to the parent
        */
       function onAfterWrite() {
          $relatedField = 'RelatedFieldName';
          $relatedClass = 'RelatedClassName';
          if ( array_key_exists('ctf[parentClass]', $this->record)
          && $this->record['ctf[parentClass]'] == $relatedClass ) {
             $components = $this->getManyManyComponents($relatedField);
        if ( ! in_array((int)$this->record['ctf[sourceID]'], $components->getIdList()) ) {
              $components->add((int)$this->record['ctf[sourceID]']);
              $components->write();
             }
          }
          parent::onAfterWrite();
       }

    }

    Again, only two lines need to be changed. The $relatedField is the fieldname on the child object that points to the parent in question, and the $relatedClass is the classname of the parent in question.

    Javascript update

    There is some code in dataobject_manager.js that retains the previous selection status of the items. It doesn't take into account newly created items though; here's a replacement refresh() function that does. It's on line 374 (depending on your version of course) of dataobject_manager.js.

    function refresh($div, link, focus)
    {
        // Kind of a hack. Pass the list of ids to the next refresh
        var listValue = ($div.hasClass('RelationDataObjectManager'))
        ? jQuery('#'+$div.attr('id')+'_CheckedList').val() : false;
        // Make a list of all ids so we can detect newly created ones
        var all = ',';
        $div.find('li.data input:checkbox').each(function(i,el) {
          all += el.value+',';
        });
       
        jQuery.ajax({
        type: "GET",
        url: link,
        success: function(html){
           if(!$div.next().length && !$div.prev().length)
              $div.parent().html(html);
           else
              $div.replaceWith(html);
           if(listValue)
           jQuery('#'+$div.attr('id')+'_CheckedList').attr('value',listValue);
           // Add newly created items in if checked         
           jQuery(html).find('li.data input:checkbox').each(function(i,el) {
              el = jQuery(el);
              if ( el.attr('checked') && jQuery.inArray(el.val(), all.split(',')) == -1 ) {
                 jQuery('#'+$div.attr('id')+'_CheckedList').val(
                    jQuery('#'+$div.attr('id')+'_CheckedList').val()+el.val()+','
                 );
              }
           });

           var $container = jQuery('#'+$div.attr('id'));
           $container.DataObjectManager();
           if (typeof focus == 'string') $container.find(focus).focus();
           //jQuery('#'+$div.attr('id')).DataObjectManager();
          }
        });
    }

    Hope this helps somebody!

  • UncleCheese
    Avatar
    4085 Posts

    Re: HOWTO: HasMany and ManyMany new items default to checked Link to this post

    I posted this solution a while ago. You shouldn't need to update the javascript or anything.

    This is what I do:

       public function onAfterWrite()
       {
          parent::onAfterWrite();
          if(isset($_REQUEST['ctf'])) {
             // new record?
             if(!isset($_REQUEST['ctf']['childID']))
                DB::query("INSERT INTO `[YOUR_JOIN_TABLE]` SET [PARENT_TABLE_ID] = ". $_REQUEST['ctf']['sourceID'] . ", [CHILD_TABLE_ID] = $this->ID");
       

          }
       }

  • Anonymous user
    Avatar
    Community Member
    1 Post

    Re: HOWTO: HasMany and ManyMany new items default to checked Link to this post

    If you have a few items in your DOM already, and then add a new one, the new item's checkbox always gets unchecked by the JS, because it's not in the 'remembered' list. By default that's fine because new items are unchecked by default.

    If you change the default behaviour to make new items checked by default, as above, your new item is now incorrectly unchecked by the stock JS. The above JS fix checks for new items on refresh, and leaves their checkbox state unchanged. Feel free to use it if you think it is an improvement.

    Cheers

  • UncleCheese
    Avatar
    4085 Posts

    Re: HOWTO: HasMany and ManyMany new items default to checked Link to this post

    Oh, right. Yeah, I forgot about that hack. Cool, thanks for the heads up.

  • Garrett
    Avatar
    Community Member
    245 Posts

    Re: HOWTO: HasMany and ManyMany new items default to checked Link to this post

    Hey gents--

    I'm trying to do the opposite -- I have a Category DataObject which has a $belongs_many_many relationship to a NewsPage, which has the corresponding $many_many there. As a result I have the relation table NewsPage_Categories.

    What I'd like to do is to automatically associate a given Category with a newly created page so that there is always at least one Category selected. Seems like it would just be a simple INSERT INTO in an OnAfterWrite() function but alas, no such luck. It's not happening for me. here's my very simple code (in the NewsPage class):

    function onAfterWrite() {
          
    parent::onAfterWrite();
          
          if(isset($_REQUEST['ctf'])) {
             DB::query("INSERT INTO `NewsPage_Categories` SET `NewsPageID` = '".$_REQUEST['ctf']['sourceID']."', `CategoryID` = '1'");
          }
       }

    What's wrong with this picture? I surmised from the code posted above that the $_REQUEST['ctf']['sourceID'] is the ID of the newly created Page. But I am not getting an error; it's just that the insert doesn't occur. onAfterWrite(), however, is firing.

    Thanks in advance,
    Garrett

    1540 Views
Page: 1
Go to Top

Want to know more about the company that brought you SilverStripe? Then check out SilverStripe.com

Comments on this website? Please give feedback.