5

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? 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() and setFields() 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!

5

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);
| improve this answer | |
3

Check out Craft's own 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.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.