<p>While I am adding them from a plugin, I ran into a similar issue as you, and like you, I didn't like the "recreate the layout from scratch" method the linked answer used. I know this question is old, but it seems the most apt place to put my solution to this issue and contribute to more code samples.</p>

<p>After some experimenting, I got a solution that works (whether it's "the Craft way," I'm not totally sure, and I'm sure some of it could be cleaned up, but it's not like there's a lot of documentation on that front, as we've both found).</p>

<p>This code creates the fields, a field group, and a tab, then adds the tab and fields to the User Layout:</p>

<pre><code>    // Create the field group
    $groupModel = new FieldGroupModel();
    $groupModel-&gt;name = 'CustomGroupName';
    craft()-&gt;fields-&gt;saveGroup($groupModel);

    // I haven't yet found a way to get the group ID without looping through all of the groups. saveGroup() returns a boolean and doesn't appear to update the id attribute of the original model instance.
    $groups = craft()-&gt;fields-&gt;getAllGroups();
    foreach($groups as $group) {
        if($group-&gt;name != 'CustomGroupName') {
            continue;
        }
        $groupModel = $group;
    }

    // Create the tab
    $tabModel = new FieldLayoutTabModel();
    // Get the desired layout (in this case, the User layout) and use the tab's setLayout to link the two
    $layout = craft()-&gt;fields-&gt;getLayoutByType(ElementType::User);
    $tabModel-&gt;setLayout($layout);
    $tabModel-&gt;name = 'CustomTabName';


    // Create the fields. I have a protected class member that contains the fields in a 'fieldName' =&gt; 'FieldName' format.
    foreach($this-&gt;fields as $field =&gt; $name) {
        $fieldModel = new FieldModel();
        $fieldModel-&gt;groupId = $groupModel-&gt;id;
        $fieldModel-&gt;name = Craft::t($name);
        $fieldModel-&gt;handle = $field;
        $fieldModel-&gt;translatable = false;
        $fieldModel-&gt;type = 'PlainText';

        craft()-&gt;fields-&gt;saveField($fieldModel);
    }

    // Get all of the group's fields. This could also be done by saving an array of the fields in the loop above, but this seems a bit cleaner to me, and since we already have the group object, saves us an object/variable declaration.
    $fields = $groupModel-&gt;getFields();

    // Set the tab's fields. This gives us a custom grouping in the layout editor.
    $tabModel-&gt;setFields($fields);

    // Get the existing tabs (and, by extension, fields), since we don't want to nuke the layout, just add to it. I couldn't find any sort of "addTab" type function, so this is how we have to do it.
    $layoutTabs = $layout-&gt;getTabs();

    // getTabs() returns an array, so simply add our new tab to the array.
    $layoutTabs[] = $tabModel;

    // Now, set the tabs back in the layout.
    $layout-&gt;setTabs($layoutTabs);
</code></pre>

<p>The reversal is exactly that--a reversal:</p>

<pre><code>    $group = null;

    // Delete the fields
    foreach($this-&gt;fields as $field =&gt; $name) {
        $fieldModel = craft()-&gt;fields-&gt;getFieldByHandle($field);
        if($fieldModel) {
            // Grab the group from one of the fields. Presumably (in my case), they're all in the same group. This could be refined for a less ham-fisted approach. Unfortunately, it's either this, or doing the loop-through-all-groups method we did in the create section, that I've found.
            $group = $fieldModel-&gt;group;
            craft()-&gt;fields-&gt;deleteField($fieldModel);
        }
    }

    // Delete the field group
    if($group) {
        $groupModel = craft()-&gt;fields-&gt;getGroupById($group-&gt;id);
        // I made sure it was empty, since I don't want to delete the group if more fields were added.
        if($groupModel &amp;&amp; empty($groupModel-&gt;getFields())) {
            craft()-&gt;fields-&gt;deleteGroupById($groupModel-&gt;id);
        }
    }

    // Delete the layout tab
    // Get the layout...
    $layout = craft()-&gt;fields-&gt;getLayoutByType(ElementType::User);

    // Get the tabs...
    $tabs = $layout-&gt;tabs;

    // Find and delete the tab we want, in the same fashion, except now we're unsetting the array instead of setting it.
    foreach($tabs as $index =&gt; $tab) {
        if($tab-&gt;name != "CustomTabName") {
            continue;
        }
        unset($tabs[$index]);
    }
    $layout-&gt;setTabs($tabs);
</code></pre>

CC BY-SA 3.0
2016-12-07T19:34:26.977Z
2016-12-07T19:34:26.977Z
Shauna
<p>Check out Craft's own <a href="http://buildwithcraft.com/classreference/services/InstallService" rel="nofollow">InstallService</a>'s private <code>_createDefaultContent()</code> method for an example of how to do this programatically.  This is called during installation to create the default content on a fresh Craft install.</p>

CC BY-SA 3.0
2015-01-14T20:26:12.823Z
Thank you for the response Brad.  Looking at that method I see the creation of a new layout group on line 694 https://github.com/pixelandtonic/Craft-Release/blob/master/app/services/InstallService.php#L694 but I don't see anything added additionally to it after it's been created.  Could you please point me toward the relevant section?
Paolo
CC BY-SA 3.0
2015-01-14T20:41:34.950Z
None of this is really documented, but that line you linked to is where the fields are assigned.  The first parameter is the posted field info and the second is the required fields.  Other than that you just [set the type](https://github.com/pixelandtonic/Craft-Release/blob/master/app/services/InstallService.php#L701), then [assign it](https://github.com/pixelandtonic/Craft-Release/blob/master/app/services/InstallService.php#L732) to the entry type.
Brad Bell
CC BY-SA 3.0
2016-12-02T10:25:09.850Z
Great suggestion, thx!

https://github.com/pixelandtonic/Craft-Release/blob/master/app/services/InstallService.php#L607
Krzysztof Boduch
CC BY-SA 3.0
2015-01-14T19:00:03.730Z
2015-01-14T19:00:03.730Z
Brad Bell
<p>I am looking to add a field (or perhaps a group?) to the userlayout via a database migration.  I've looked over the docs, and found <a href="https://craftcms.stackexchange.com/questions/763/is-it-possible-to-add-a-custom-field-to-user-model-from-a-plugin">Is it possible to add a custom field to User model from a plugin?</a> but that isn't exactly what I want.  I do not wish to delete the layout and then recreate it.  I just want to add a new field to it.</p>

<p>I was hoping to be able to use the <a href="http://buildwithcraft.com/classreference/models/FieldLayoutModel#getFields-detail" rel="nofollow noreferrer">getFields()</a> and <a href="http://buildwithcraft.com/classreference/models/FieldLayoutModel#setFields-detail" rel="nofollow noreferrer">setFields()</a> methods directly on the FieldLayoutModel but thus far no dice.  Is there a better way to do with, with a service or something?</p>

<p>Right now I am:</p>

<ol>
<li>Creating a new FieldModel, populating it and saving it. </li>
<li>Finding the fieldLayout I want through <code>craft()-&gt;fields-&gt;getLayoutByType()</code> </li>
<li>Calling getFields() on it and saving the result to a variable </li>
<li>Popping my saved field onto the end of that array </li>
<li>Calling setFields on the fieldLayout with that new updated array</li>
</ol>

<p>But it's not functioning.</p>

<p>Thank you for your time!</p>

<field><migration>
Programatically Adding Fields to a Layout
# Programatically Adding Fields to a Layout

(Asked by: username_0 on 2015-01-14)

I am looking to add a field (or perhaps a group?) to the userlayout via a database migration. I've looked over the docs, and found [Is it possible to add a custom field to User model from a plugin?](https://craftcms.stackexchange.com/questions/763/is-it-possible-to-add-a-custom-field-to-user-model-from-a-plugin) but that isn't exactly what I want. I do not wish to delete the layout and then recreate it. I just want to add a new field to it.

I was hoping to be able to use the [getFields()](http://buildwithcraft.com/classreference/models/FieldLayoutModel#getFields-detail) and [setFields()](http://buildwithcraft.com/classreference/models/FieldLayoutModel#setFields-detail) methods directly on the FieldLayoutModel but thus far no dice. Is there a better way to do with, with a service or something?

Right now I am:

1. Creating a new FieldModel, populating it and saving it.
2. Finding the fieldLayout I want through `craft()->fields->getLayoutByType()`
3. Calling getFields() on it and saving the result to a variable
4. Popping my saved field onto the end of that array
5. Calling setFields on the fieldLayout with that new updated array

But it's not functioning.

Thank you for your time!

**Tags:** field, migration

---

## 2 answers

### Answer by username_1 on 2016-12-07. Score: 6

While I am adding them from a plugin, I ran into a similar issue as you, and like you, I didn't like the "recreate the layout from scratch" method the linked answer used. I know this question is old, but it seems the most apt place to put my solution to this issue and contribute to more code samples.
After some experimenting, I got a solution that works (whether it's "the Craft way," I'm not totally sure, and I'm sure some of it could be cleaned up, but it's not like there's a lot of documentation on that front, as we've both found).
This code creates the fields, a field group, and a tab, then adds the tab and fields to the User Layout:
    // Create the field group
    $groupModel = new FieldGroupModel();
    $groupModel->name = 'CustomGroupName';
    craft()->fields->saveGroup($groupModel);

    // I haven't yet found a way to get the group ID without looping through all of the groups. saveGroup() returns a boolean and doesn't appear to update the id attribute of the original model instance.
    $groups = craft()->fields->getAllGroups();
    foreach($groups as $group) {
        if($group->name != 'CustomGroupName') {
            continue;
        }
        $groupModel = $group;
    }

    // Create the tab
    $tabModel = new FieldLayoutTabModel();
    // Get the desired layout (in this case, the User layout) and use the tab's setLayout to link the two
    $layout = craft()->fields->getLayoutByType(ElementType::User);
    $tabModel->setLayout($layout);
    $tabModel->name = 'CustomTabName';


    // Create the fields. I have a protected class member that contains the fields in a 'fieldName' => 'FieldName' format.
    foreach($this->fields as $field => $name) {
        $fieldModel = new FieldModel();
        $fieldModel->groupId = $groupModel->id;
        $fieldModel->name = Craft::t($name);
        $fieldModel->handle = $field;
        $fieldModel->translatable = false;
        $fieldModel->type = 'PlainText';

        craft()->fields->saveField($fieldModel);
    }

    // Get all of the group's fields. This could also be done by saving an array of the fields in the loop above, but this seems a bit cleaner to me, and since we already have the group object, saves us an object/variable declaration.
    $fields = $groupModel->getFields();

    // Set the tab's fields. This gives us a custom grouping in the layout editor.
    $tabModel->setFields($fields);

    // Get the existing tabs (and, by extension, fields), since we don't want to nuke the layout, just add to it. I couldn't find any sort of "addTab" type function, so this is how we have to do it.
    $layoutTabs = $layout->getTabs();

    // getTabs() returns an array, so simply add our new tab to the array.
    $layoutTabs[] = $tabModel;

    // Now, set the tabs back in the layout.
    $layout->setTabs($layoutTabs);

The reversal is exactly that--a reversal:
    $group = null;

    // Delete the fields
    foreach($this->fields as $field => $name) {
        $fieldModel = craft()->fields->getFieldByHandle($field);
        if($fieldModel) {
            // Grab the group from one of the fields. Presumably (in my case), they're all in the same group. This could be refined for a less ham-fisted approach. Unfortunately, it's either this, or doing the loop-through-all-groups method we did in the create section, that I've found.
            $group = $fieldModel->group;
            craft()->fields->deleteField($fieldModel);
        }
    }

    // Delete the field group
    if($group) {
        $groupModel = craft()->fields->getGroupById($group->id);
        // I made sure it was empty, since I don't want to delete the group if more fields were added.
        if($groupModel && empty($groupModel->getFields())) {
            craft()->fields->deleteGroupById($groupModel->id);
        }
    }

    // Delete the layout tab
    // Get the layout...
    $layout = craft()->fields->getLayoutByType(ElementType::User);

    // Get the tabs...
    $tabs = $layout->tabs;

    // Find and delete the tab we want, in the same fashion, except now we're unsetting the array instead of setting it.
    foreach($tabs as $index => $tab) {
        if($tab->name != "CustomTabName") {
            continue;
        }
        unset($tabs[$index]);
    }
    $layout->setTabs($tabs);


---

### Answer by username_2 on 2015-01-14. Score: 3

Check out Craft's own [InstallService](http://buildwithcraft.com/classreference/services/InstallService)'s private `_createDefaultContent()` method for an example of how to do this programatically. This is called during installation to create the default content on a fresh Craft install.

> **Comments:**
> **username_0**: Thank you for the response Brad.  Looking at that method I see the creation of a new layout group on line 694 https://github.com/pixelandtonic/Craft-Release/blob/master/app/services/InstallService.php#L694 but I don't see anything added additionally to it after it's been created.  Could you please point me toward the relevant section?  
> **username_2**: None of this is really documented, but that line you linked to is where the fields are assigned.  The first parameter is the posted field info and the second is the required fields.  Other than that you just [set the type](https://github.com/pixelandtonic/Craft-Release/blob/master/app/services/InstallService.php#L701), then [assign it](https://github.com/pixelandtonic/Craft-Release/blob/master/app/services/InstallService.php#L732) to the entry type.  
> **username_3**: Great suggestion, thx!  https://github.com/pixelandtonic/Craft-Release/blob/master/app/services/InstallService.php#L607  

---

