<p>The main issue here ended up being a Craft bug (<a href="https://github.com/craftcms/cms/issues/6651" rel="nofollow noreferrer">craftcms/cms#6651</a>), which has been fixed for the next release (3.5.5).</p>
<p>There are a couple ways your code could be improved as well, though:</p>
<ul>
<li><p><code>$fieldGroup</code> is a <a href="https://docs.craftcms.com/api/v3/craft-models-fieldgroup.html" rel="nofollow noreferrer">craft\models\FieldGroup</a> object, not an array, so you should access its ID via <code>$fieldGroup-&gt;id</code>, not <code>$fieldGroup['id']</code>. (Or just use the <code>$groupId</code> property.)</p>
</li>
<li><p>Avoid using <code>===</code> for numeric properties like <code>id</code>, unless you know for certain what the type is going to be. IDs <em>should</em> be casted as integers, not strings, and places where they are strings should be considered a minor bug. (It’s due to how MySQL returns the data, but not an issue in PostgreSQL, and we are working toward always ensuring that they are typed as ints correctly even for MySQL.) Safest thing for you to do is replace <code>===</code> with <code>==</code> on this line:</p>
<pre class="lang-php prettyprint-override"><code>if ($entryType-&gt;id === '2') {
</code></pre>
</li>
<li><p>Your two <code>continue</code> statements should be <code>break</code>. <code>continue</code> just tells the loop to move onto the next cycle of the loop; <code>break</code> tells it to completely break out of the loop.</p>
</li>
<li><p><code>$tabsForSaving</code> is never defined. I’m surprised that’s not causing a PHP error for you.</p>
</li>
</ul>

CC BY-SA 4.0
2020-08-18T17:19:25.377Z
Cheers for the code review. Ah yes I stripped `$tabsForSaving` out, discovered that I did not need to call `setTabs` again on the layout for my new fields to be saved to the layout.
joep
CC BY-SA 4.0
2020-08-17T15:31:34.913Z
2020-08-17T15:31:34.913Z
Brandon Kelly
<p>as part of a migration script I'm trying to:</p>
<ol>
<li>create 2 new fields,</li>
<li>add them to a field group</li>
<li>add the new fields to a specific tab on an entry type</li>
<li>Update the values of the new fields</li>
</ol>
<p>What I have so far works up until point 4, in the database the fields exist on the entry, I can see them there when I browse to an entry in the CMS, I can save new values to them, but, I cannot set a value with <code>setFieldValues</code> straight after. Can anyone suggest what I've done wrong here?</p>
<p>Here is my code so far:</p>
<pre class="lang-php prettyprint-override"><code>public function safeUp()
{
    // Get contact field group
    $groupId = 31; // contact field group
    $fields = Craft::$app-&gt;getFields(); // Get fields
    $fieldGroup = $fields-&gt;getGroupById($groupId); // Get contact field group

    // Create heading field
    $headingFieldHandle = 'contactThanksHeading';
    $headingField = new PlainText([
        'groupId' =&gt; $fieldGroup['id'],
        'name' =&gt; 'Contact Thanks Heading',
        'handle' =&gt; $headingFieldHandle,
        &quot;instructions&quot; =&gt; &quot;Larger heading text for the contact form's thanks message&quot;,
        &quot;required&quot; =&gt; false,
        &quot;placeholder&quot; =&gt; &quot;Thank you&quot;,
        &quot;charLimit&quot; =&gt; 300,
        &quot;multiline&quot; =&gt; false,
        &quot;initialRows&quot; =&gt; &quot;4&quot;,
    ]);
    // Save heading field
    Craft::$app-&gt;getFields()-&gt;saveField($headingField);

    // Create copy field
    $copyFieldHandle = 'contactThanksCopy';
    $copyField = new PlainText([
        'groupId' =&gt; $fieldGroup['id'],
        'name' =&gt; 'Contact Thanks Copy',
        'handle' =&gt; $copyFieldHandle,
        &quot;instructions&quot; =&gt; &quot;Smaller copy text for the contact form's thanks message&quot;,
        &quot;required&quot; =&gt; false,
        &quot;placeholder&quot; =&gt; &quot;One of our team will get back shortly...&quot;,
        &quot;charLimit&quot; =&gt; 300,
        &quot;multiline&quot; =&gt; true,
        &quot;initialRows&quot; =&gt; &quot;4&quot;,
    ]);
    // Save copy field
    Craft::$app-&gt;getFields()-&gt;saveField($copyField);

    // Get contact single section
    $contactSection = Craft::$app-&gt;getSections()-&gt;getSectionById(2);
    // Get the section's entry types
    $contactEntryTypes = $contactSection-&gt;getEntryTypes();

    // Find entry type 2
    foreach ($contactEntryTypes as $entryType) {
        if ($entryType-&gt;id === '2') {
            $contactEntryType = $entryType;
            continue;
        }
    }

    // Get the field layout for entry type 2
    $contactFieldLayout = $contactEntryType-&gt;getFieldLayout(2);
    // Loop on tabs to find common
    foreach($contactFieldLayout-&gt;tabs as $tab) {
        if ($tab-&gt;name === 'Common') {
            $contactCommonTab = $tab;
            continue;
        }
    }

    // get the common tab's existing fields
    $tabFields = $contactCommonTab-&gt;getFields();

    // Set the sort order of the new fields to append them to the tab
    $headingField-&gt;sortOrder = count($tabFields);
    $copyField-&gt;sortOrder = count($tabFields)+1;

    // Add new fields to Common tab array
    $newFields = array_merge($tabFields,[$headingField, $copyField]);
    // Save fields to tab
    $contactCommonTab-&gt;setFields($newFields);

    // Set the tabs on the field layout
    $contactFieldLayout-&gt;setTabs($tabsForSaving);

    // Save the contact field layout
    Craft::$app-&gt;fields-&gt;saveLayout($contactFieldLayout);

    // get the contact entry
    $contactEntry = Craft::$app-&gt;entries-&gt;getEntryById(8);
    // Set the contact entry values
    $contactEntry-&gt;setFieldValues([
        $headingFieldHandle =&gt; '1',
        $copyFieldHandle =&gt; '1',
    ]);
    // save the contact entry
    Craft::$app-&gt;elements-&gt;saveElement($contactEntry);
}
</code></pre>

<plugin-development>
unsucessful setFieldValues after programatically creating field on a migration
# unsucessful setFieldValues after programatically creating field on a migration

(Asked by: username_0 on 2020-08-14)

as part of a migration script I'm trying to:

create 2 new fields,
add them to a field group
add the new fields to a specific tab on an entry type
Update the values of the new fields

What I have so far works up until point 4, in the database the fields exist on the entry, I can see them there when I browse to an entry in the CMS, I can save new values to them, but, I cannot set a value with setFieldValues straight after. Can anyone suggest what I've done wrong here?
Here is my code so far:
public function safeUp()
{
    // Get contact field group
    $groupId = 31; // contact field group
    $fields = Craft::$app->getFields(); // Get fields
    $fieldGroup = $fields->getGroupById($groupId); // Get contact field group

    // Create heading field
    $headingFieldHandle = 'contactThanksHeading';
    $headingField = new PlainText([
        'groupId' => $fieldGroup['id'],
        'name' => 'Contact Thanks Heading',
        'handle' => $headingFieldHandle,
        "instructions" => "Larger heading text for the contact form's thanks message",
        "required" => false,
        "placeholder" => "Thank you",
        "charLimit" => 300,
        "multiline" => false,
        "initialRows" => "4",
    ]);
    // Save heading field
    Craft::$app->getFields()->saveField($headingField);

    // Create copy field
    $copyFieldHandle = 'contactThanksCopy';
    $copyField = new PlainText([
        'groupId' => $fieldGroup['id'],
        'name' => 'Contact Thanks Copy',
        'handle' => $copyFieldHandle,
        "instructions" => "Smaller copy text for the contact form's thanks message",
        "required" => false,
        "placeholder" => "One of our team will get back shortly...",
        "charLimit" => 300,
        "multiline" => true,
        "initialRows" => "4",
    ]);
    // Save copy field
    Craft::$app->getFields()->saveField($copyField);

    // Get contact single section
    $contactSection = Craft::$app->getSections()->getSectionById(2);
    // Get the section's entry types
    $contactEntryTypes = $contactSection->getEntryTypes();

    // Find entry type 2
    foreach ($contactEntryTypes as $entryType) {
        if ($entryType->id === '2') {
            $contactEntryType = $entryType;
            continue;
        }
    }

    // Get the field layout for entry type 2
    $contactFieldLayout = $contactEntryType->getFieldLayout(2);
    // Loop on tabs to find common
    foreach($contactFieldLayout->tabs as $tab) {
        if ($tab->name === 'Common') {
            $contactCommonTab = $tab;
            continue;
        }
    }

    // get the common tab's existing fields
    $tabFields = $contactCommonTab->getFields();

    // Set the sort order of the new fields to append them to the tab
    $headingField->sortOrder = count($tabFields);
    $copyField->sortOrder = count($tabFields)+1;

    // Add new fields to Common tab array
    $newFields = array_merge($tabFields,[$headingField, $copyField]);
    // Save fields to tab
    $contactCommonTab->setFields($newFields);

    // Set the tabs on the field layout
    $contactFieldLayout->setTabs($tabsForSaving);

    // Save the contact field layout
    Craft::$app->fields->saveLayout($contactFieldLayout);

    // get the contact entry
    $contactEntry = Craft::$app->entries->getEntryById(8);
    // Set the contact entry values
    $contactEntry->setFieldValues([
        $headingFieldHandle => '1',
        $copyFieldHandle => '1',
    ]);
    // save the contact entry
    Craft::$app->elements->saveElement($contactEntry);
}

**Tags:** plugin-development

---

## 1 answers

### Answer by username_1 on 2020-08-17. Score: 3 | Accepted answer

The main issue here ended up being a Craft bug (craftcms/cms#6651), which has been fixed for the next release (3.5.5).
There are a couple ways your code could be improved as well, though:

$fieldGroup is a craft\models\FieldGroup object, not an array, so you should access its ID via $fieldGroup->id, not $fieldGroup['id']. (Or just use the $groupId property.)

Avoid using === for numeric properties like id, unless you know for certain what the type is going to be. IDs should be casted as integers, not strings, and places where they are strings should be considered a minor bug. (It’s due to how MySQL returns the data, but not an issue in PostgreSQL, and we are working toward always ensuring that they are typed as ints correctly even for MySQL.) Safest thing for you to do is replace === with == on this line:
if ($entryType->id === '2') {


Your two continue statements should be break. continue just tells the loop to move onto the next cycle of the loop; break tells it to completely break out of the loop.

$tabsForSaving is never defined. I’m surprised that’s not causing a PHP error for you.

> **Comments:**
> **username_0**: Cheers for the code review. Ah yes I stripped `$tabsForSaving` out, discovered that I did not need to call `setTabs` again on the layout for my new fields to be saved to the layout.  

---

