2
votes

I am trying to use a Has_many relation as the summary fields for a DataObject and can't seem to get it working.

Basically:

  • I have a Form
  • each form has many submissions/entries
  • each form has many fields
  • Each field has many answers.

I'm trying to create a gridfield in the back end admin area of each form which displays the entries for each form.

In the summary fields for the entry, i'd like to display the Date created, and the first 3 fields for that form.

So, for example if we had a form with a name, email, and phone field the summary fields would be as follows:

  • Date Created
  • Name
  • Email
  • Phone

with the relevent entry data/responses as summary information for the entry.

Here is what I have so far. This is the form:

<?php

class ContactBlock extends Block {
    private static $db = array(
        // Fields for the form/block go here
    );

    private static $has_many = array(
        'ContactBlockFields' => 'ContactBlockField',
        'ContactBlockEntries' => 'ContactBlockEntry' 
    );

    public function getCMSFields() {

       // Irrelevant code goes here!


        // CONTACT BLOCK ENTRIES
        $entriesInfo = new GridFieldDataColumns();
        $entriesInfo->setDisplayFields(singleton('ContactBlockEntry')->summaryFields());

        $entriesConfig = GridFieldConfig::create();
        $entriesConfig->addComponents(
            new GridFieldToolbarHeader(),
            new GridFieldAddNewButton('toolbar-header-right'),
            $entriesInfo,
            new GridFieldSortableHeader(),
            new GridFieldPaginator(50),
            new GridFieldEditButton(),
            new GridFieldDeleteAction(),
            new GridFieldDetailForm()
        );

        $entriesGrid = GridField::create('ContactBlockEntries', 'Form Entries', $this->ContactBlockEntries(), $entriesConfig);
        $fields->addFieldToTab('Root.FormEntries', $entriesGrid);

        return $fields;
    }

}

This is the Entry DataObject:

<?php

class ContactBlockEntry extends DataObject {

    private static $has_one = array(
        'ContactBlock' => 'ContactBlock'
    );

    private static $has_many = array(
        'ContactBlockFieldAnswers' => 'ContactBlockFieldAnswer',
    );

    private static $many_many = array(
        'FormFields' => 'ContactBlockField'
    );

    static $summary_fields = array(
        'Date'
    );

    public function getCMSFields() {
        $fields = parent::getCMSFields();

        //=== REMOVE FIELDS ====
        $fields->removeFieldFromTab('Root','FormFields');
        $fields->removeFieldFromTab('Root','ContactBlockFieldAnswers');
        $fields->removeFieldFromTab('Root.Main','ContactBlockID');
        //=== REMOVE FIELDS ====

        return $fields;
    }

    public function onBeforeDelete() {
        parent::onBeforeDelete();

        // Delete answers that are associated with this block.
        $data = ContactBlockFieldAnswer::get()
           ->filter('ContactBlockEntry', $this->ID);

        foreach( $data as $d) {
            $d->delete();
        }

    }

    public function getDate() {
        $date = date('d/m/Y',strtotime($this->Created));
        return $date;
    }

}

This is the field code:

<?php

class ContactBlockField extends DataObject {
    private static $db = array(
        'SortOrder' => 'Int',
        'FieldName' => 'Varchar',
        'FieldType' => 'Varchar',
        'DropdownValues' => 'Varchar(255)',
        'Label' => 'Varchar',
        'Placeholder' => 'Varchar',
        'Required' => 'Boolean'
    );

    private static $has_one = array(
        'ContactBlock' => 'ContactBlock',
    );

    private static $has_many = array(
        'ContactBlockFieldAnswer' => 'ContactBlockFieldAnswer',
    );

    private static $belongs_many_many = array(
        'Entries' => 'ContactBlockEntry'
    );

    static $searchable_fields = array(
        'FieldType',
        'FieldLabel',
        'Required'
    );

    static $summary_fields = array(
        'FieldType' => 'Field Type',
        'Label' => 'Field Label',
        'Required'  => 'Required Field?'
    );

    public function getCMSFields() {
        $fields = parent::getCMSFields();

          // Unrelated stuff here

        return $fields;
    }

}

I can't seem to figure out how to get the column labels, and their relevant data showing on the gridfield. Any help or advice would be much appreciated.

UPDATE 24/3/17:

OK I've got a little further with this. On the ContactBlockEntry DataObject, after implementing the changes suggested by UncleCheese, I have discovered the following:

public function getFirstField() {
        return $this->FormFields()->first();
    }

    public function getSecondField() {
        return $this->FormFields()->offsetGet(1);
    }

    public function getThirdField() {
        return $this->FormFields()->offsetGet(2);
    }

    public function summaryFields() {
        return [
            'Date'                      => 'Submitted',
            'Answers.First.Value'       => $this->getFirstField()->Label,
            'Answers.Second.Value'      => $this->getSecondField()->Label,
            'Answers.Third.Value'       => $this->getThirdField()->Label,
        ];
    }

$this->getFirstField()->Label is returning [Notice] Trying to get property of non-object however, when I echo/print this function in getCMSFields() I can see the label value.

Answers.First.Value works. It returns the value/answer submitted in the first field. The problem is, I can't seem to get the second and third values, as I can't figure out the method to retrieve them. I tried offsetGet() and it said the method isn't available.

1

1 Answers

5
votes

In this case, you can define a summaryFields() method in lieu of the static array. This will allow you to return a computed value for the headings.

What complicates things is getting the values. The first three fields are not properties of the Entry object, so you'll need to provide getters for the Entry dataobject to compute them on the fly as "virtual" properties. It's a bit clunky, but something like this should work.

public function getFirstField()
{
    return $this->FormFields()->first();
}

public function getSecondField()
{
    return $this->FormFields()->offsetGet(1);
}

public function getThirdField()
{
    return $this->FormFields()->offsetGet(2);
}

public function summaryFields()
{
    return [
        'Created' => 'Created',
        'FirstField.Answer' => $this->getFirstField()->FieldName,
        'SecondField.Answer' => $this->getSecondField()->FieldName,
        'ThirdField.Answer' => $this->getThirdField()->FieldName,
    ];
}

I'm not too sure about your data model, though. You have the answers has a has_many on your field object. You would in that case have to create another getter on your Field object for AnswerLabel that somehow concatenated all the answers into a string, or maybe just got the first one. Whatever business logic you choose. Just use FirstField.AnswerLabel etc. in your array, or whatever you choose to call that method. The point being, you need to resolve a plural relationship into a single readable value. How you do that is up to you.